# Payout (Funds Distribution) - Examples

Comprehensive examples for distributing funds to multiple beneficiaries using the ABA PayWay Funds Route API.

---

## Table of Contents

1. [Basic Examples](#basic-examples)
2. [Marketplace Examples](#marketplace-examples)
3. [Commission Distribution Examples](#commission-distribution-examples)
4. [Service Provider Examples](#service-provider-examples)
5. [Error Handling Examples](#error-handling-examples)
6. [Advanced Examples](#advanced-examples)

---

## Basic Examples

### Example 1: Simple Two-Party Split

Split payment between two accounts:

```typescript
import { PayWayClient } from 'aba-payway-sdk';

const client = new PayWayClient({
  merchantId: 'ec000002',
  apiKey: process.env.PAYWAY_API_KEY!,
  sandbox: true
});

const rsaPublicKey = process.env.PAYWAY_RSA_PUBLIC_KEY!;

async function simpleSplit() {
  try {
    const result = await client.payout({
      beneficiaries: [
        { account: '200030000', amount: 100 },
        { account: '012538302', amount: 200 }
      ],
      amount: 300,
      currency: 'USD',
      rsaPublicKey: rsaPublicKey
    });

    console.log('✓ Payout successful');
    console.log('Transaction ID:', result.transaction_id);
    console.log('Status:', result.status?.message);
  } catch (error) {
    console.error('Payout failed:', error);
  }
}

simpleSplit();
```

### Example 2: Payout with Custom Transaction ID

Use your own transaction ID for tracking:

```typescript
async function payoutWithCustomId() {
  const customTranId = `PAYOUT-${Date.now()}`;

  const result = await client.payout({
    tranId: customTranId,
    beneficiaries: [
      { account: '200030000', amount: 500 },
      { account: '012538302', amount: 500 }
    ],
    amount: 1000,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey
  });

  console.log('Transaction ID:', result.transaction_id);
  // Save customTranId to your database for tracking
}
```

### Example 3: KHR Currency Payout

Distribute funds in Cambodian Riel:

```typescript
async function khrPayout() {
  const result = await client.payout({
    beneficiaries: [
      { account: '200030000', amount: 50000 },  // 50,000 KHR
      { account: '012538302', amount: 50000 }   // 50,000 KHR
    ],
    amount: 100000,
    currency: 'KHR',
    rsaPublicKey: rsaPublicKey
  });

  console.log('Amount:', result.transaction_amount, 'KHR');
}
```

---

## Marketplace Examples

### Example 4: E-commerce Platform Split

Split order payment between platform and vendor:

```typescript
async function ecommerceSplit(orderId: string, orderAmount: number) {
  const platformFee = orderAmount * 0.15;  // 15% commission
  const vendorAmount = orderAmount * 0.85;  // 85% to vendor

  const result = await client.payout({
    beneficiaries: [
      { account: 'ec000002', amount: platformFee },    // Platform
      { account: '000123456', amount: vendorAmount }   // Vendor
    ],
    amount: orderAmount,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey,
    customFields: {
      order_id: orderId,
      split_type: 'marketplace'
    }
  });

  if (result.status?.code === '0') {
    console.log('✓ Order payment distributed');
    console.log(`Platform: $${platformFee.toFixed(2)}`);
    console.log(`Vendor: $${vendorAmount.toFixed(2)}`);
  }

  return result;
}

// Usage
await ecommerceSplit('ORD-12345', 100.00);
// Platform: $15.00, Vendor: $85.00
```

### Example 5: Multi-Vendor Order

Distribute payment across multiple vendors:

```typescript
async function multiVendorOrder() {
  const vendors = [
    { name: 'Platform', account: 'ec000002', amount: 30.00 },
    { name: 'Vendor 1', account: '000111111', amount: 70.00 },
    { name: 'Vendor 2', account: '000222222', amount: 50.00 }
  ];

  const totalAmount = vendors.reduce((sum, v) => sum + v.amount, 0);

  const result = await client.payout({
    beneficiaries: vendors.map(v => ({
      account: v.account,
      amount: v.amount
    })),
    amount: totalAmount,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey,
    customFields: {
      distribution_type: 'multi_vendor',
      order_id: 'MKT-789'
    }
  });

  console.log('✓ Multi-vendor distribution successful');
  vendors.forEach(v => {
    console.log(`${v.name}: $${v.amount.toFixed(2)}`);
  });

  return result;
}
```

### Example 6: Dynamic Commission Calculation

Calculate and distribute commissions dynamically:

```typescript
interface OrderItem {
  vendorId: string;
  price: number;
  commission: number; // percentage
}

async function dynamicCommissionPayout(items: OrderItem[]) {
  const beneficiaries = items.map(item => {
    const vendorAmount = item.price * (1 - item.commission / 100);
    const platformAmount = item.price * (item.commission / 100);
    
    return [
      { account: 'platform_account', amount: platformAmount },
      { account: item.vendorId, amount: vendorAmount }
    ];
  }).flat();

  // Group by account and sum amounts
  const grouped = beneficiaries.reduce((acc, ben) => {
    const existing = acc.find(b => b.account === ben.account);
    if (existing) {
      existing.amount += ben.amount;
    } else {
      acc.push({ ...ben });
    }
    return acc;
  }, [] as Array<{ account: string; amount: number }>);

  const totalAmount = grouped.reduce((sum, b) => sum + b.amount, 0);

  return await client.payout({
    beneficiaries: grouped,
    amount: totalAmount,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey
  });
}
```

---

## Commission Distribution Examples

### Example 7: Affiliate Commission Payout

Pay multiple affiliates their earned commissions:

```typescript
async function affiliateCommissions() {
  const affiliates = [
    { id: '100001', name: 'Affiliate A', commission: 25.00 },
    { id: '100002', name: 'Affiliate B', commission: 35.00 },
    { id: '100003', name: 'Affiliate C', commission: 40.00 }
  ];

  const beneficiaries = affiliates.map(aff => ({
    account: aff.id,
    amount: aff.commission
  }));

  const totalCommission = beneficiaries.reduce((sum, b) => sum + b.amount, 0);

  const result = await client.payout({
    beneficiaries,
    amount: totalCommission,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey,
    customFields: {
      payout_type: 'affiliate_commission',
      period: '2024-Q1',
      affiliate_count: affiliates.length
    }
  });

  console.log(`✓ Paid ${affiliates.length} affiliates`);
  console.log(`Total: $${totalCommission.toFixed(2)}`);

  return result;
}
```

### Example 8: Referral Bonus Distribution

Distribute referral bonuses to multiple users:

```typescript
async function referralBonuses(referrals: Array<{ userId: string; bonus: number }>) {
  const beneficiaries = referrals.map(ref => ({
    account: ref.userId,
    amount: ref.bonus
  }));

  const totalBonus = beneficiaries.reduce((sum, b) => sum + b.amount, 0);

  return await client.payout({
    beneficiaries,
    amount: totalBonus,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey,
    customFields: {
      payout_type: 'referral_bonus',
      campaign: 'Q1-2024'
    }
  });
}

// Usage
await referralBonuses([
  { userId: 'USER001', bonus: 10.00 },
  { userId: 'USER002', bonus: 15.00 },
  { userId: 'USER003', bonus: 10.00 }
]);
```

---

## Service Provider Examples

### Example 9: Driver/Delivery Partner Payouts

Pay multiple drivers for completed deliveries:

```typescript
async function driverPayouts() {
  const drivers = [
    { id: 'DRV001', name: 'Driver A', earnings: 50.00, trips: 5 },
    { id: 'DRV002', name: 'Driver B', earnings: 75.00, trips: 7 },
    { id: 'DRV003', name: 'Driver C', earnings: 60.00, trips: 6 }
  ];

  const beneficiaries = drivers.map(driver => ({
    account: driver.id,
    amount: driver.earnings
  }));

  const totalEarnings = beneficiaries.reduce((sum, b) => sum + b.amount, 0);

  const result = await client.payout({
    beneficiaries,
    amount: totalEarnings,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey,
    customFields: {
      payout_batch: `DRIVERS-${Date.now()}`,
      total_trips: drivers.reduce((sum, d) => sum + d.trips, 0),
      driver_count: drivers.length
    }
  });

  console.log('✓ Driver payouts completed');
  drivers.forEach(driver => {
    console.log(`${driver.name}: $${driver.earnings} (${driver.trips} trips)`);
  });

  return result;
}
```

### Example 10: Freelancer Payment Batch

Process payments for multiple freelancers:

```typescript
interface Freelancer {
  accountId: string;
  name: string;
  projectHours: number;
  hourlyRate: number;
}

async function freelancerBatch(freelancers: Freelancer[]) {
  const beneficiaries = freelancers.map(freelancer => ({
    account: freelancer.accountId,
    amount: freelancer.projectHours * freelancer.hourlyRate
  }));

  const totalPayment = beneficiaries.reduce((sum, b) => sum + b.amount, 0);

  return await client.payout({
    beneficiaries,
    amount: totalPayment,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey,
    customFields: {
      payment_type: 'freelancer_batch',
      period: 'Weekly',
      freelancer_count: freelancers.length
    }
  });
}

// Usage
await freelancerBatch([
  { accountId: 'FL001', name: 'Designer', projectHours: 40, hourlyRate: 25 },
  { accountId: 'FL002', name: 'Developer', projectHours: 35, hourlyRate: 50 }
]);
```

---

## Error Handling Examples

### Example 11: Comprehensive Error Handling

Handle all possible error scenarios:

```typescript
async function payoutWithErrorHandling() {
  try {
    const result = await client.payout({
      beneficiaries: [
        { account: '200030000', amount: 100 },
        { account: '012538302', amount: 200 }
      ],
      amount: 300,
      currency: 'USD',
      rsaPublicKey: rsaPublicKey
    });

    // Check response status
    if (result.status?.code === '0') {
      console.log('✓ Payout successful');
      return { success: true, data: result };
    } else {
      console.error('✗ Payout failed');
      console.error('Code:', result.status?.code);
      console.error('Message:', result.status?.message);
      return { success: false, error: result.status };
    }
  } catch (error: any) {
    console.error('Payout exception:', error.message);

    // Handle specific error codes
    if (error.statusCode) {
      switch (error.statusCode) {
        case '93':
          console.error('⚠️  Insufficient balance');
          // Notify admin, pause payouts
          break;
        case '37':
          console.error('⚠️  Account not in whitelist');
          // Contact support to whitelist account
          break;
        case '25':
          console.error('⚠️  Too many beneficiaries');
          // Split into multiple requests
          break;
        case '83':
          console.error('⚠️  Duplicate transaction ID');
          // Generate new transaction ID
          break;
        case '92':
          console.error('⚠️  Amount mismatch');
          // Check beneficiary sum equals total
          break;
        default:
          console.error('⚠️  Unknown error:', error.statusCode);
      }
    }

    return { success: false, error };
  }
}
```

### Example 12: Retry Logic with Exponential Backoff

Implement retry mechanism for transient failures:

```typescript
async function payoutWithRetry(
  options: any,
  maxRetries: number = 3,
  baseDelay: number = 1000
) {
  let lastError: Error | null = null;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      console.log(`Attempt ${attempt}/${maxRetries}...`);
      
      const result = await client.payout(options);
      
      if (result.status?.code === '0') {
        console.log('✓ Payout successful');
        return result;
      }

      // Don't retry for certain errors
      const nonRetryableCodes = ['37', '83', '92', '25'];
      if (result.status?.code && nonRetryableCodes.includes(result.status.code)) {
        throw new Error(`Non-retryable error: ${result.status.message}`);
      }

      lastError = new Error(result.status?.message || 'Unknown error');
    } catch (error: any) {
      lastError = error;
      
      // Don't retry on validation errors
      if (error.message.includes('whitelist') || 
          error.message.includes('mismatch')) {
        throw error;
      }

      if (attempt < maxRetries) {
        const delay = baseDelay * Math.pow(2, attempt - 1);
        console.log(`Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }

  throw lastError || new Error('Payout failed after retries');
}
```

---

## Advanced Examples

### Example 13: Batch Processing for Large Lists

Handle more than 10 beneficiaries by splitting into batches:

```typescript
async function largeBatchPayout(
  allBeneficiaries: Array<{ account: string; amount: number }>,
  currency: 'USD' | 'KHR'
) {
  const BATCH_SIZE = 10; // Maximum per request
  const batches: Array<Array<{ account: string; amount: number }>> = [];

  // Split into batches
  for (let i = 0; i < allBeneficiaries.length; i += BATCH_SIZE) {
    batches.push(allBeneficiaries.slice(i, i + BATCH_SIZE));
  }

  console.log(`Processing ${batches.length} batches...`);

  const results = [];
  for (let i = 0; i < batches.length; i++) {
    const batch = batches[i];
    const batchTotal = batch.reduce((sum, b) => sum + b.amount, 0);

    console.log(`Batch ${i + 1}/${batches.length}: ${batch.length} beneficiaries`);

    const result = await client.payout({
      beneficiaries: batch,
      amount: batchTotal,
      currency,
      rsaPublicKey: rsaPublicKey,
      tranId: `BATCH-${i + 1}-${Date.now()}`,
      customFields: {
        batch_number: i + 1,
        total_batches: batches.length
      }
    });

    results.push(result);

    // Delay between batches to avoid rate limiting
    if (i < batches.length - 1) {
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }

  console.log(`✓ Processed ${batches.length} batches successfully`);
  return results;
}

// Usage with 25 beneficiaries
const largeBeneficiaryList = Array.from({ length: 25 }, (_, i) => ({
  account: `ACC${String(i + 1).padStart(5, '0')}`,
  amount: 10.00
}));

await largeBatchPayout(largeBeneficiaryList, 'USD');
```

### Example 14: Percentage-Based Distribution

Distribute based on percentages:

```typescript
function calculatePayoutFromPercentages(
  totalAmount: number,
  distribution: Array<{ account: string; percentage: number }>
): Array<{ account: string; amount: number }> {
  // Validate percentages sum to 100
  const totalPercentage = distribution.reduce((sum, d) => sum + d.percentage, 0);
  if (Math.abs(totalPercentage - 100) > 0.01) {
    throw new Error('Percentages must sum to 100%');
  }

  return distribution.map(d => ({
    account: d.account,
    amount: Number((totalAmount * d.percentage / 100).toFixed(2))
  }));
}

async function percentageBasedPayout() {
  const totalAmount = 1000.00;
  const distribution = [
    { account: 'ec000002', percentage: 20 },   // 20%
    { account: '000111111', percentage: 50 },  // 50%
    { account: '000222222', percentage: 30 }   // 30%
  ];

  const beneficiaries = calculatePayoutFromPercentages(totalAmount, distribution);

  return await client.payout({
    beneficiaries,
    amount: totalAmount,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey
  });
}
```

### Example 15: Conditional Payout with Validation

Add business logic validation before payout:

```typescript
interface PayoutRule {
  minAmount: number;
  maxAmount: number;
  requiresApproval: boolean;
}

async function conditionalPayout(
  beneficiaries: Array<{ account: string; amount: number }>,
  rules: PayoutRule
) {
  const totalAmount = beneficiaries.reduce((sum, b) => sum + b.amount, 0);

  // Validate against rules
  if (totalAmount < rules.minAmount) {
    throw new Error(`Total amount must be at least ${rules.minAmount}`);
  }

  if (totalAmount > rules.maxAmount) {
    throw new Error(`Total amount exceeds maximum of ${rules.maxAmount}`);
  }

  if (rules.requiresApproval) {
    // Check if approved in your system
    const isApproved = await checkApprovalStatus();
    if (!isApproved) {
      throw new Error('Payout requires approval');
    }
  }

  // Validate each beneficiary
  for (const beneficiary of beneficiaries) {
    const isWhitelisted = await checkWhitelist(beneficiary.account);
    if (!isWhitelisted) {
      throw new Error(`Account ${beneficiary.account} not whitelisted`);
    }
  }

  // Proceed with payout
  return await client.payout({
    beneficiaries,
    amount: totalAmount,
    currency: 'USD',
    rsaPublicKey: rsaPublicKey
  });
}

async function checkApprovalStatus(): Promise<boolean> {
  // Your approval logic
  return true;
}

async function checkWhitelist(account: string): Promise<boolean> {
  // Your whitelist check logic
  return true;
}
```

---

## Tips & Best Practices

1. **Always validate totals**: Ensure beneficiary amounts sum to the total
2. **Use meaningful transaction IDs**: Include context like order ID, batch number
3. **Store payout IDs**: Save `payout_id` from response for reconciliation
4. **Handle errors gracefully**: Check status codes and implement retry logic
5. **Batch large lists**: Split into chunks of 10 beneficiaries
6. **Add delays between batches**: Prevent rate limiting
7. **Use custom fields**: Add metadata for tracking and reporting
8. **Test in sandbox**: Always test payouts in sandbox environment first

---

For more information, see:
- [Complete API Reference](./REFERENCE.md)
- [Main Skill Documentation](./SKILL.md)
