# Pre-Auth Complete with Payout - Examples

This document provides comprehensive examples for completing pre-authorized transactions with optional payout distribution.

---

## Table of Contents

1. [Basic Examples](#basic-examples)
2. [Payout Distribution Examples](#payout-distribution-examples)
3. [Use Case Examples](#use-case-examples)
4. [Error Handling Examples](#error-handling-examples)
5. [Advanced Examples](#advanced-examples)

---

## Basic Examples

### Example 1: Complete Pre-Auth (No Payout)

Complete a pre-auth transaction for the exact amount:

```typescript
import { PayWayClient } from 'aba-payway-sdk';

const client = new PayWayClient({
  merchantId: 'ec000002',
  apiKey: process.env.PAYWAY_API_KEY!,
  sandbox: true
});

async function completePreAuth() {
  try {
    const result = await client.completePreAuth({
      tranId: '17394277693',
      completeAmount: 100.00,
      rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!
    });

    console.log('✓ Pre-auth completed');
    console.log('Status:', result.transaction_status);
    console.log('Amount:', result.grand_total, result.currency);
  } catch (error) {
    console.error('Failed to complete pre-auth:', error);
  }
}

completePreAuth();
```

### Example 2: Complete with Higher Amount (Card Payment)

For card payments, you can complete up to 10% above the original pre-auth amount:

```typescript
async function completeWithHigherAmount() {
  // Original pre-auth: $100.00
  // Complete with additional amount for cards
  const result = await client.completePreAuth({
    tranId: '17394277693',
    completeAmount: 110.00, // 10% more than original
    rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!
  });

  if (result.status.code === '00') {
    console.log('✓ Completed with extra amount');
    console.log('Final charge:', result.grand_total);
  }
}
```

---

## Payout Distribution Examples

### Example 3: Two-Way Split (Platform + Vendor)

Split payment between platform fee and vendor payment:

```typescript
async function marketplaceSplit() {
  const totalAmount = 100.00;
  const platformFee = 20.00;
  const vendorPayment = 80.00;

  const result = await client.completePreAuth({
    tranId: '17394277693',
    completeAmount: totalAmount,
    rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
    payout: [
      { acc: 'ec000002', amt: platformFee },    // Platform account
      { acc: '000123456', amt: vendorPayment }  // Vendor ABA account
    ]
  });

  console.log('✓ Payment split completed');
  console.log('Platform received:', platformFee);
  console.log('Vendor received:', vendorPayment);
}
```

### Example 4: Three-Way Split (Multi-Vendor)

Distribute payment among multiple vendors:

```typescript
async function multiVendorPayout() {
  const result = await client.completePreAuth({
    tranId: '17394277693',
    completeAmount: 150.00,
    rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
    payout: [
      { acc: 'ec000002', amt: 30.00 },   // Platform fee (20%)
      { acc: '000111111', amt: 70.00 },  // Vendor 1 (46.67%)
      { acc: '000222222', amt: 50.00 }   // Vendor 2 (33.33%)
    ]
  });

  return result;
}
```

### Example 5: Commission-Based Split

Dynamic commission calculation:

```typescript
interface CommissionConfig {
  platformRate: number;  // e.g., 0.15 for 15%
  processingFee: number; // Fixed fee
}

async function completeWithCommission(
  tranId: string,
  amount: number,
  vendorAccount: string,
  config: CommissionConfig
) {
  const platformFee = amount * config.platformRate + config.processingFee;
  const vendorPayment = amount - platformFee;

  const result = await client.completePreAuth({
    tranId,
    completeAmount: amount,
    rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
    payout: [
      { acc: 'ec000002', amt: platformFee },
      { acc: vendorAccount, amt: vendorPayment }
    ]
  });

  return {
    success: result.status.code === '00',
    platformFee,
    vendorPayment,
    result
  };
}

// Usage
const result = await completeWithCommission(
  '17394277693',
  100.00,
  '000123456',
  { platformRate: 0.15, processingFee: 2.00 }
);
// Platform: $17.00 (15% + $2.00)
// Vendor: $83.00
```

---

## Use Case Examples

### Example 6: Hotel Checkout with Service Split

Split hotel charges between hotel and booking platform:

```typescript
interface HotelBooking {
  preAuthTranId: string;
  roomCharges: number;
  roomService: number;
  miniBarCharges: number;
  hotelAccount: string;
  platformAccount: string;
  platformCommissionRate: number;
}

async function completeHotelCheckout(booking: HotelBooking) {
  const totalCharges = 
    booking.roomCharges + 
    booking.roomService + 
    booking.miniBarCharges;
  
  const platformCommission = totalCharges * booking.platformCommissionRate;
  const hotelPayment = totalCharges - platformCommission;

  try {
    const result = await client.completePreAuth({
      tranId: booking.preAuthTranId,
      completeAmount: totalCharges,
      rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
      payout: [
        { acc: booking.platformAccount, amt: platformCommission },
        { acc: booking.hotelAccount, amt: hotelPayment }
      ]
    });

    if (result.status.code === '00') {
      return {
        success: true,
        checkoutDetails: {
          totalCharges,
          platformCommission,
          hotelPayment,
          breakdown: {
            room: booking.roomCharges,
            roomService: booking.roomService,
            miniBar: booking.miniBarCharges
          }
        }
      };
    }
  } catch (error) {
    console.error('Hotel checkout failed:', error);
    throw error;
  }
}

// Usage
await completeHotelCheckout({
  preAuthTranId: '17394277693',
  roomCharges: 200.00,
  roomService: 30.00,
  miniBarCharges: 20.00,
  hotelAccount: '000111111',
  platformAccount: 'ec000002',
  platformCommissionRate: 0.12  // 12% commission
});
```

### Example 7: E-Commerce Marketplace Order

Complete order with shipping fee distribution:

```typescript
interface MarketplaceOrder {
  preAuthTranId: string;
  productPrice: number;
  shippingFee: number;
  vendorAccount: string;
  courierAccount: string;
  platformAccount: string;
  platformFeeRate: number;
}

async function completeMarketplaceOrder(order: MarketplaceOrder) {
  const subtotal = order.productPrice + order.shippingFee;
  const platformFee = order.productPrice * order.platformFeeRate;
  const vendorPayment = order.productPrice - platformFee;
  
  const result = await client.completePreAuth({
    tranId: order.preAuthTranId,
    completeAmount: subtotal,
    rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
    payout: [
      { acc: order.platformAccount, amt: platformFee },
      { acc: order.vendorAccount, amt: vendorPayment },
      { acc: order.courierAccount, amt: order.shippingFee }
    ]
  });

  return {
    orderId: order.preAuthTranId,
    paymentStatus: result.transaction_status,
    distribution: {
      platform: platformFee,
      vendor: vendorPayment,
      courier: order.shippingFee
    }
  };
}
```

### Example 8: Ride-Sharing Payment Split

Distribute ride payment between driver and platform:

```typescript
interface RidePayment {
  preAuthTranId: string;
  baseFare: number;
  distanceCharge: number;
  timeCharge: number;
  driverAccount: string;
  platformCommissionRate: number;
}

async function completeRidePayment(ride: RidePayment) {
  const totalFare = ride.baseFare + ride.distanceCharge + ride.timeCharge;
  const platformCommission = totalFare * ride.platformCommissionRate;
  const driverEarnings = totalFare - platformCommission;

  const result = await client.completePreAuth({
    tranId: ride.preAuthTranId,
    completeAmount: totalFare,
    rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
    payout: [
      { acc: 'ec000002', amt: platformCommission },
      { acc: ride.driverAccount, amt: driverEarnings }
    ]
  });

  if (result.status.code === '00') {
    console.log('✓ Ride payment completed');
    console.log(`Driver earned: $${driverEarnings.toFixed(2)}`);
    console.log(`Platform commission: $${platformCommission.toFixed(2)}`);
  }

  return result;
}
```

---

## Error Handling Examples

### Example 9: Comprehensive Error Handling

Handle various error scenarios:

```typescript
async function completePreAuthWithErrorHandling(
  tranId: string,
  amount: number,
  payout?: Array<{ acc: string; amt: number }>
) {
  try {
    // Validate payout amounts match
    if (payout) {
      const totalPayout = payout.reduce((sum, p) => sum + p.amt, 0);
      if (Math.abs(totalPayout - amount) > 0.01) {
        throw new Error(`Payout total (${totalPayout}) doesn't match amount (${amount})`);
      }
    }

    const result = await client.completePreAuth({
      tranId,
      completeAmount: amount,
      rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
      payout
    });

    if (result.status.code === '00') {
      return { success: true, data: result };
    } else {
      return { success: false, error: result.status.message };
    }
  } catch (error: any) {
    // Handle specific error codes
    switch (error.statusCode) {
      case 'PTL60':
        console.error('Amount exceeds authorized limit');
        return { success: false, error: 'Amount too high' };
      
      case 'PTL59':
        console.error('Pre-auth cannot be completed (already completed or expired)');
        return { success: false, error: 'Transaction not eligible' };
      
      case 'PTL153':
        console.error('Merchant has multiple settlement accounts');
        return { success: false, error: 'Payout not supported for this merchant' };
      
      case 'PTL168':
        console.error('Concurrent request - retry needed');
        return { success: false, error: 'Please retry in a few seconds', retry: true };
      
      default:
        console.error('Unexpected error:', error.message);
        return { success: false, error: error.message };
    }
  }
}
```

### Example 10: Retry Logic for Concurrent Requests

Implement retry mechanism for PTL168 error:

```typescript
async function completeWithRetry(
  tranId: string,
  amount: number,
  payout?: Array<{ acc: string; amt: number }>,
  maxRetries: number = 3
) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await client.completePreAuth({
        tranId,
        completeAmount: amount,
        rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
        payout
      });

      return { success: true, data: result, attempts: attempt };
    } catch (error: any) {
      if (error.statusCode === 'PTL168' && attempt < maxRetries) {
        console.log(`Concurrent request detected. Retry ${attempt}/${maxRetries}...`);
        // Wait with exponential backoff
        await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
        continue;
      }
      
      throw error;
    }
  }
}
```

---

## Advanced Examples

### Example 11: Dynamic Payout Calculation

Calculate payout distribution based on business rules:

```typescript
interface PayoutRule {
  accountId: string;
  accountType: 'platform' | 'vendor' | 'service' | 'tax';
  percentage?: number;
  fixedAmount?: number;
  priority: number;
}

function calculatePayout(
  totalAmount: number,
  rules: PayoutRule[]
): Array<{ acc: string; amt: number }> {
  // Sort by priority
  const sortedRules = [...rules].sort((a, b) => a.priority - b.priority);
  
  let remaining = totalAmount;
  const payout: Array<{ acc: string; amt: number }> = [];

  for (const rule of sortedRules) {
    let amount: number;
    
    if (rule.fixedAmount !== undefined) {
      amount = rule.fixedAmount;
    } else if (rule.percentage !== undefined) {
      amount = totalAmount * rule.percentage;
    } else {
      // Last rule gets remaining amount
      amount = remaining;
    }

    // Round to 2 decimal places
    amount = Math.round(amount * 100) / 100;
    
    payout.push({
      acc: rule.accountId,
      amt: amount
    });

    remaining -= amount;
  }

  // Adjust last payout to account for rounding
  if (Math.abs(remaining) > 0.01) {
    payout[payout.length - 1].amt += remaining;
    payout[payout.length - 1].amt = Math.round(payout[payout.length - 1].amt * 100) / 100;
  }

  return payout;
}

// Usage
const payoutRules: PayoutRule[] = [
  { accountId: 'ec000010', accountType: 'tax', fixedAmount: 5.00, priority: 1 },
  { accountId: 'ec000002', accountType: 'platform', percentage: 0.15, priority: 2 },
  { accountId: '000123456', accountType: 'vendor', percentage: 0, priority: 3 } // Gets remaining
];

const payout = calculatePayout(100.00, payoutRules);
// Result: [
//   { acc: 'ec000010', amt: 5.00 },   // Tax
//   { acc: 'ec000002', amt: 15.00 },  // Platform (15%)
//   { acc: '000123456', amt: 80.00 }  // Vendor (remaining)
// ]

await client.completePreAuth({
  tranId: '17394277693',
  completeAmount: 100.00,
  rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
  payout
});
```

### Example 12: Validate Pre-Auth Before Completion

Check transaction status before attempting completion:

```typescript
async function safeCompletePreAuth(
  tranId: string,
  completeAmount: number,
  payout?: Array<{ acc: string; amt: number }>
) {
  // Step 1: Get transaction details
  const details = await client.getTransactionDetail({ tranId });

  // Step 2: Validate transaction status
  if (details.data.payment_status !== 'PRE-AUTH') {
    throw new Error(`Transaction is ${details.data.payment_status}, not PRE-AUTH`);
  }

  // Step 3: Validate amount
  const originalAmount = details.data.original_amount;
  const maxAllowed = originalAmount * 1.10; // 10% extra for cards

  if (completeAmount > maxAllowed) {
    throw new Error(
      `Complete amount $${completeAmount} exceeds maximum $${maxAllowed.toFixed(2)}`
    );
  }

  // Step 4: Complete pre-auth
  const result = await client.completePreAuth({
    tranId,
    completeAmount,
    rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
    payout
  });

  return {
    success: true,
    originalAmount,
    completedAmount: completeAmount,
    status: result.transaction_status
  };
}
```

### Example 13: Batch Completion Processing

Process multiple pre-auth completions:

```typescript
interface PreAuthCompletion {
  tranId: string;
  amount: number;
  payout?: Array<{ acc: string; amt: number }>;
}

async function batchCompletePreAuth(
  completions: PreAuthCompletion[],
  delayMs: number = 1000
) {
  const results = [];

  for (const completion of completions) {
    try {
      const result = await client.completePreAuth({
        tranId: completion.tranId,
        completeAmount: completion.amount,
        rsaPublicKey: process.env.PAYWAY_RSA_PUBLIC_KEY!,
        payout: completion.payout
      });

      results.push({
        tranId: completion.tranId,
        success: true,
        status: result.transaction_status
      });
    } catch (error: any) {
      results.push({
        tranId: completion.tranId,
        success: false,
        error: error.message
      });
    }

    // Delay to avoid rate limiting
    if (delayMs > 0) {
      await new Promise(resolve => setTimeout(resolve, delayMs));
    }
  }

  return {
    total: completions.length,
    successful: results.filter(r => r.success).length,
    failed: results.filter(r => !r.success).length,
    results
  };
}
```

---

## Testing Examples

### Example 14: Sandbox Testing

Test in sandbox environment:

```typescript
// Sandbox configuration
const sandboxClient = new PayWayClient({
  merchantId: 'ec000002', // Sandbox merchant ID
  apiKey: process.env.PAYWAY_SANDBOX_API_KEY!,
  sandbox: true
});

async function testPreAuthCompletion() {
  // Step 1: Create pre-auth (using purchase API)
  const purchase = await sandboxClient.purchase({
    amount: 100.00,
    currency: 'USD',
    type: 'pre-auth',
    returnUrl: 'https://example.com/callback',
    items: [{ name: 'Test Product', quantity: 1, price: 100.00 }]
  });

  console.log('Pre-auth created. Complete payment at:', purchase.checkout_url);
  console.log('Transaction ID:', purchase.tran_id);

  // Step 2: After customer completes payment, complete pre-auth
  // Wait for payment...
  
  // Step 3: Complete with payout
  const result = await sandboxClient.completePreAuth({
    tranId: purchase.tran_id,
    completeAmount: 100.00,
    rsaPublicKey: process.env.PAYWAY_SANDBOX_RSA_PUBLIC_KEY!,
    payout: [
      { acc: 'ec000002', amt: 20.00 },
      { acc: '000123456', amt: 80.00 }
    ]
  });

  console.log('✓ Test completed:', result.transaction_status);
}
```

---

## Summary

These examples demonstrate:

- ✅ Basic pre-auth completion
- ✅ Payout distribution strategies
- ✅ Real-world use cases (marketplace, hotel, ride-sharing)
- ✅ Comprehensive error handling
- ✅ Advanced payout calculations
- ✅ Batch processing
- ✅ Testing in sandbox

For complete API reference, see [REFERENCE.md](./REFERENCE.md).
