# Purchase API - Code Examples

Complete integration examples for the ABA PayWay Purchase API.

⚠️ **Critical**: Don't use `client.createPurchase()` for form generation! It makes an API call and returns HTML/JSON. Instead, build form parameters manually using hash utilities.

## 1. Express.js Checkout API (Correct Implementation)

```typescript
import express from 'express';
import {
  generatePurchaseHash,
  generateReqTime,
  generateTransactionId,
  encodeItems,
} from 'aba-payway-sdk/utils';

const app = express();
app.use(express.json());

// Generate purchase form parameters
app.post('/api/generate-purchase-params', async (req, res) => {
  try {
    const { cart, customer } = req.body;
    
    const total = cart.reduce((sum, item) => 
      sum + (item.price * item.quantity), 0
    );

    const req_time = generateReqTime();
    const tran_id = `ORDER-${Date.now()}`;
    
    // Build form parameters
    const formParams = {
      req_time,
      merchant_id: process.env.PAYWAY_MERCHANT_ID,
      tran_id,
      amount: total,
      currency: 'USD',
      firstname: customer.firstName,
      lastname: customer.lastName,
      email: customer.email,
      phone: customer.phone,
      items: encodeItems(cart.map(item => ({
        name: item.name,
        quantity: item.quantity,
        price: item.price
      }))),
      return_url: Buffer.from(
        `${req.protocol}://${req.get('host')}/checkout/return`
      ).toString('base64'),
      continue_success_url: `${req.protocol}://${req.get('host')}/order/success`,
      hash: '' // Will be calculated below
    };

    // Calculate hash with ALL parameters in CORRECT order
    formParams.hash = generatePurchaseHash(
      formParams,
      process.env.PAYWAY_API_KEY
    );

    // Send to client
    res.json({
      formAction: process.env.NODE_ENV === 'production'
        ? 'https://checkout.payway.com.kh/api/payment-gateway/v1/payments/purchase'
        : 'https://checkout-sandbox.payway.com.kh/api/payment-gateway/v1/payments/purchase',
      formParams
    });
  } catch (error) {
    console.error('Generate purchase params error:', error);
    res.status(500).json({ error: 'Failed to generate purchase parameters' });
  }
});

// Handle return from PayWay
app.get('/checkout/return', async (req, res) => {
  const { tran_id, status } = req.query;
  
  // Verify with transaction detail API
  res.redirect(`/order/${tran_id}`);
});
```

## 2. Next.js Checkout Page (Correct Implementation)

```tsx
// app/checkout/page.tsx
'use client';

import { useState } from 'react';

export default function CheckoutPage() {
  const [loading, setLoading] = useState(false);

  const handleCheckout = async () => {
    setLoading(true);
    
    try {
      // Fetch form parameters from backend
      const response = await fetch('/api/generate-purchase-params', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          amount: 99.99,
          currency: 'USD',
          customer: {
            firstName: 'Sok',
            lastName: 'Dara',
            email: 'sokdara@example.com',
            phone: '012345678'
          }
        })
      });

      const { formAction, formParams } = await response.json();

      // Create form and auto-submit
      const form = document.createElement('form');
      form.method = 'POST';
      form.action = formAction;

      Object.entries(formParams).forEach(([key, value]) => {
        const input = document.createElement('input');
        input.type = 'hidden';
        input.name = key;
        input.value = String(value);
        form.appendChild(input);
      });

      document.body.appendChild(form);
      form.submit(); // Redirects to PayWay
    } catch (error) {
      console.error('Checkout error:', error);
      alert('Failed to initiate checkout');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <h1>Checkout</h1>
      <button onClick={handleCheckout} disabled={loading}>
        {loading ? 'Processing...' : 'Pay Now'}
      </button>
    </div>
  );
}
```

```typescript
// app/api/generate-purchase-params/route.ts
import { NextRequest, NextResponse } from 'next/server';
import {
  generatePurchaseHash,
  generateReqTime,
  generateTransactionId,
} from 'aba-payway-sdk/utils';

export async function POST(req: NextRequest) {
  try {
    const body = await req.json();
    const { amount, currency, customer } = body;

    const req_time = generateReqTime();
    const tran_id = generateTransactionId();

    const formParams = {
      req_time,
      merchant_id: process.env.PAYWAY_MERCHANT_ID!,
      tran_id,
      amount,
      currency: currency || 'USD',
      firstname: customer.firstName,
      lastname: customer.lastName,
      email: customer.email,
      phone: customer.phone,
      return_url: Buffer.from(`${req.nextUrl.origin}/checkout/return`).toString('base64'),
      continue_success_url: `${req.nextUrl.origin}/order/success`,
      hash: ''
    };

    // Calculate hash
    formParams.hash = generatePurchaseHash(
      formParams,
      process.env.PAYWAY_API_KEY!
    );

    return NextResponse.json({
      formAction: 'https://checkout-sandbox.payway.com.kh/api/payment-gateway/v1/payments/purchase',
      formParams
    });
  } catch (error) {
    console.error('Error:', error);
    return NextResponse.json(
      { error: 'Failed to generate purchase parameters' },
      { status: 500 }
    );
  }
}
```
  };

  return (
    <div className="checkout-page">
      <h1>Checkout</h1>
      <button 
        onClick={handleCheckout}
        disabled={loading}
        className="btn btn-primary"
      >
        {loading ? 'Processing...' : 'Proceed to Payment'}
      </button>
    </div>
  );
}
```

## 3. Return URL Handler

```typescript
// Handle customer return from PayWay
app.get('/checkout/return', async (req, res) => {
  const { tran_id, status } = req.query;

  if (status === 'success') {
    // Verify with transaction detail API
    const details = await payway.getTransactionDetail({
      tranId: tran_id as string
    });

    if (details.data.payment_status === 'APPROVED') {
      res.redirect(`/order/${tran_id}/success`);
    } else {
      res.redirect(`/order/${tran_id}/pending`);
    }
  } else {
    res.redirect('/checkout/failed');
  }
});
```

## 4. React Subscription Checkout

```tsx
import { useState } from 'react';

function SubscriptionCheckout({ plan }: { plan: any }) {
  const [processing, setProcessing] = useState(false);

  const subscribe = async () => {
    setProcessing(true);

    try {
      const response = await fetch('/api/subscriptions/checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          planId: plan.id,
          customer: {
            firstName: 'Customer',
            lastName: 'Name',
            email: 'customer@example.com',
            phone: '012345678'
          }
        })
      });

      const { checkoutUrl, formData } = await response.json();

      // Redirect to checkout
      const form = document.createElement('form');
      form.method = 'POST';
      form.action = checkoutUrl;

      Object.entries(formData).forEach(([key, value]) => {
        const input = document.createElement('input');
        input.type = 'hidden';
        input.name = key;
        input.value = String(value);
        form.appendChild(input);
      });

      document.body.appendChild(form);
      form.submit();
    } catch (error) {
      alert('Failed to start checkout');
      setProcessing(false);
    }
  };

  return (
    <button onClick={subscribe} disabled={processing}>
      {processing ? 'Processing...' : `Subscribe - $${plan.price}/month`}
    </button>
  );
}
```

## 5. Multi-Currency Support

```typescript
async function createInternationalCheckout(
  amount: number,
  customerCountry: string
) {
  const payway = new PayWayClient();
  
  // Determine currency based on country
  const currency = customerCountry === 'KH' ? 'KHR' : 'USD';
  
  // Convert if needed
  const finalAmount = currency === 'KHR' 
    ? amount * 4100  // USD to KHR conversion
    : amount;

  return await payway.createPurchase({
    amount: finalAmount,
    currency,
    firstName: 'Customer',
    lastName: 'Name',
    email: 'customer@example.com',
    phone: '012345678',
    returnUrl: 'https://example.com/return'
  });
}
```

## 6. QR Deep Link Payment

```typescript
// Get QR code data instead of HTML redirect
const result = await client.createPurchase({
  amount: 50.00,
  currency: 'USD',
  paymentOption: 'abapay_khqr_deeplink',  // Returns JSON
  firstName: 'Sok',
  lastName: 'Dara',
  email: 'sokdara@example.com',
  phone: '012345678',
  returnUrl: 'https://shop.com/return'
});

// Response format:
console.log(result.status?.code);        // '0' for success
console.log(result.status?.tran_id);     // Transaction ID
console.log(result.qr_string);           // KHQR string
console.log(result.abapay_deeplink);     // Deep link for ABA app
console.log(result.checkout_qr_url);     // Checkout page URL
```

## 7. Mobile App Integration

```typescript
// For mobile apps, use return deeplink
const result = await client.createPurchase({
  amount: 75.00,
  currency: 'USD',
  firstName: 'Mobile',
  lastName: 'User',
  email: 'user@example.com',
  phone: '012345678',
  returnUrl: 'https://api.example.com/webhook',
  returnDeeplink: {
    ios_scheme: 'myapp://payment/success',
    android_scheme: 'myapp://payment/success'
  },
  viewType: 'popup'  // Mobile-optimized
});
```

## 8. Pre-Authorization

```typescript
// Pre-auth (hold funds, capture later)
const result = await client.createPurchase({
  amount: 100.00,
  currency: 'USD',
  purchaseType: 'pre-auth',  // Only for ABA PAY, KHQR, and Card
  firstName: 'Hotel',
  lastName: 'Guest',
  email: 'guest@hotel.com',
  phone: '012345678',
  returnUrl: 'https://hotel.com/booking/confirm',
  items: [
    { name: 'Room Reservation Deposit', quantity: 1, price: 100.00 }
  ]
});
```

## 9. Advanced Features

```typescript
const result = await client.createPurchase({
  amount: 250.00,
  currency: 'USD',
  
  // Customer info
  firstName: 'Advanced',
  lastName: 'Customer',
  email: 'customer@example.com',
  phone: '012345678',
  
  // Items
  items: [
    { name: 'Product A', quantity: 2, price: 100.00 },
    { name: 'Product B', quantity: 1, price: 50.00 }
  ],
  
  // Payment settings
  paymentOption: 'cards',
  viewType: 'popup',
  lifetime: 60,  // 1 hour expiry
  
  // URLs
  returnUrl: 'https://shop.com/payment/return',
  cancelUrl: 'https://shop.com/cart',
  continueSuccessUrl: 'https://shop.com/order/success',
  
  // Skip PayWay success page
  skipSuccessPage: true,
  
  // Custom data to track
  customFields: {
    orderId: 'ORD-12345',
    customerId: 'CUST-789',
    source: 'web'
  },
  
  // Data to include in return URL
  returnParams: {
    sessionId: 'abc123',
    referrer: 'homepage'
  },
  
  // Payout to multiple accounts
  payout: [
    { acc: '000133879', amt: 200.00 },
    { acc: '000133880', amt: 50.00 }
  ]
});
```

## 10. WeChat Mini Program

```typescript
// For WeChat Mini Program integration
const result = await client.createPurchase({
  amount: 88.88,
  currency: 'USD',
  paymentOption: 'wechat',
  firstName: 'WeChat',
  lastName: 'User',
  email: 'user@example.com',
  phone: '012345678',
  returnUrl: 'https://api.example.com/wechat/callback',
  additionalParams: {
    wechat_sub_appid: 'YOUR_WECHAT_APP_ID',
    wechat_sub_openid: 'YOUR_WECHAT_OPEN_ID'
  }
});
```

## 11. Complete E-commerce Flow

```typescript
// 1. Create order in database
const order = await db.orders.create({
  data: {
    userId: user.id,
    total: cartTotal,
    status: 'pending',
    items: cartItems
  }
});

// 2. Create PayWay purchase
const purchaseData = await payway.createPurchase({
  amount: order.total,
  currency: 'USD',
  firstName: user.firstName,
  lastName: user.lastName,
  email: user.email,
  phone: user.phone,
  items: order.items.map(item => ({
    name: item.productName,
    quantity: item.quantity,
    price: item.unitPrice
  })),
  returnUrl: `${baseUrl}/checkout/return`,
  callbackUrl: `${baseUrl}/api/webhook`,
  continueSuccessUrl: `${baseUrl}/order/${order.id}/success`,
  tranId: order.id
});

// 3. Store transaction ID
await db.orders.update({
  where: { id: order.id },
  data: { transactionId: purchaseData.tran_id }
});

// 4. Redirect to PayWay
redirectToPayWay(purchaseData);

// 5. Handle webhook callback
app.post('/api/webhook', async (req, res) => {
  const { tran_id } = req.body;
  
  const details = await payway.getTransactionDetail({ tranId: tran_id });
  
  if (details.data.payment_status === 'APPROVED') {
    await db.orders.update({
      where: { transactionId: tran_id },
      data: { status: 'paid', paidAt: new Date() }
    });
    
    // Send confirmation email, fulfill order, etc.
  }
  
  res.json({ success: true });
});
```
