> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orsunpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Unified Checkout Integration

> Complete guide to integrating Orsunpay's Unified Checkout for seamless payment experiences

# Unified Checkout Integration

Orsunpay's Unified Checkout provides a seamless payment experience that automatically displays the best payment methods for each customer based on their location, currency, and preferences.

## Overview

The Unified Checkout process involves three main steps:

1. **Create Session**: Your backend creates a checkout session
2. **Load SDK**: Your frontend loads the Orsunpay Checkout SDK
3. **Handle Events**: Process payment success/failure events

## Step 1: Create Checkout Session

First, create a checkout session from your backend:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.orsunpay.com/v1/checkout/sessions" \
    -X POST \
    -H "Authorization: Bearer sk_live_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "amount": 5000,
      "currency": "USD",
      "country": "US",
      "customer": {
        "email": "customer@example.com",
        "firstName": "John",
        "lastName": "Doe"
      },
      "returnUrl": "https://yoursite.com/payment/return",
      "cancelUrl": "https://yoursite.com/payment/cancel",
      "metadata": {
        "orderId": "order_123",
        "productId": "prod_456"
      }
    }'
  ```

  ```javascript JavaScript (Fetch) theme={null}
  const response = await fetch('https://api.orsunpay.com/v1/checkout/sessions', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_live_your_api_key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      amount: 5000,
      currency: 'USD',
      country: 'US',
      customer: {
        email: 'customer@example.com',
        firstName: 'John',
        lastName: 'Doe'
      },
      returnUrl: 'https://yoursite.com/payment/return',
      cancelUrl: 'https://yoursite.com/payment/cancel',
      metadata: {
        orderId: 'order_123',
        productId: 'prod_456'
      }
    })
  });

  const session = await response.json();
  ```

  ```javascript Node.js (Axios) theme={null}
  const axios = require('axios');

  const response = await axios.post(
    'https://api.orsunpay.com/v1/checkout/sessions',
    {
      amount: 5000,
      currency: 'USD',
      country: 'US',
      customer: {
        email: 'customer@example.com',
        firstName: 'John',
        lastName: 'Doe'
      },
      returnUrl: 'https://yoursite.com/payment/return',
      cancelUrl: 'https://yoursite.com/payment/cancel',
      metadata: {
        orderId: 'order_123',
        productId: 'prod_456'
      }
    },
    {
      headers: {
        'Authorization': 'Bearer sk_live_your_api_key',
        'Content-Type': 'application/json'
      }
    }
  );

  const session = response.data;
  ```
</CodeGroup>

### Session Response

```json theme={null}
{
  "id": "cs_clkj3h2n0000qjr8x4c5h6e7b",
  "clientSecret": "cs_clkj3h2n0000qjr8x4c5h6e7b_secret_abc123",
  "amount": 5000,
  "currency": "USD",
  "country": "US",
  "status": "open",
  "expiresAt": "2023-11-15T11:30:00.000Z",
  "createdAt": "2023-11-15T10:30:00.000Z"
}
```

<Warning>
  **Never expose your secret API key in frontend code.** The `clientSecret` from the session response is safe to use in client-side code.
</Warning>

### Session Parameters

| Parameter       | Type    | Required | Description                             |
| --------------- | ------- | -------- | --------------------------------------- |
| `amount`        | integer | Yes      | Amount in smallest currency unit        |
| `currency`      | string  | Yes      | Three-letter currency code              |
| `country`       | string  | Yes      | Customer's country (ISO 3166-1 alpha-2) |
| `customer`      | object  | Yes      | Customer information                    |
| `returnUrl`     | string  | Yes      | URL to redirect after payment           |
| `cancelUrl`     | string  | No       | URL to redirect if canceled             |
| `methodsFilter` | array   | No       | Restrict to specific payment methods    |
| `locale`        | string  | No       | Language preference (default: en-US)    |
| `metadata`      | object  | No       | Custom key-value data                   |

## Step 2: Load Checkout SDK

Add the Orsunpay Checkout SDK to your page:

```html theme={null}
<script src="https://checkout.orsunpay.com/sdk.js" async></script>
```

For sandbox testing:

```html theme={null}
<script src="https://sandbox-checkout.orsunpay.com/sdk.js" async></script>
```

## Step 3: Initialize Checkout

### Basic Integration

```html theme={null}
<div id="orsunpay-checkout"></div>

<script>
document.addEventListener('DOMContentLoaded', function() {
  OrsunpayCheckout.mount('#orsunpay-checkout', {
    clientSecret: 'cs_clkj3h2n0000qjr8x4c5h6e7b_secret_abc123',
    
    onReady: function() {
      console.log('Checkout is ready');
    },
    
    onSuccess: function(result) {
      console.log('Payment succeeded:', result);
      // Redirect to success page or update UI
      window.location.href = '/payment/success?transaction_id=' + result.transactionId;
    },
    
    onFail: function(error) {
      console.error('Payment failed:', error);
      // Show error message to user
      alert('Payment failed: ' + error.message);
    },
    
    onCancel: function() {
      console.log('Payment canceled');
      // Handle cancellation
      window.location.href = '/payment/cancel';
    }
  });
});
</script>
```

### Advanced Configuration

```javascript theme={null}
OrsunpayCheckout.mount('#orsunpay-checkout', {
  clientSecret: 'cs_clkj3h2n0000qjr8x4c5h6e7b_secret_abc123',
  
  // Appearance customization
  theme: 'light', // 'light', 'dark', or 'auto'
  locale: 'en-US',
  
  // Display options
  amount: 5000,
  currency: 'USD',
  
  // Payment method filtering
  methodsFilter: ['card', 'paypal', 'apple_pay'],
  
  // Event handlers
  onReady: function() {
    document.getElementById('loading').style.display = 'none';
  },
  
  onSuccess: function(result) {
    // result.transactionId - Orsunpay transaction ID
    // result.status - Transaction status
    // result.metadata - Your custom metadata
    
    // Update your order status
    fetch('/api/orders/update', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        orderId: result.metadata.orderId,
        transactionId: result.transactionId,
        status: 'paid'
      })
    }).then(() => {
      window.location.href = '/order/confirmation';
    });
  },
  
  onFail: function(error) {
    // error.code - Error code
    // error.message - Human-readable message
    // error.details - Additional error details
    
    showErrorMessage(error.message);
  },
  
  onCancel: function() {
    // User canceled the payment
    analytics.track('checkout_canceled');
    window.location.href = '/cart';
  }
});
```

## Next.js Integration (App Router)

### Server Component (Create Session)

```typescript theme={null}
// app/api/checkout/session/route.ts
import { NextRequest, NextResponse } from 'next/server';

const ORSUNPAY_API_KEY = process.env.ORSUNPAY_SECRET_KEY;
const ORSUNPAY_API_URL = process.env.NODE_ENV === 'production' 
  ? 'https://api.orsunpay.com/v1'
  : 'https://sandbox-api.orsunpay.com/v1';

export async function POST(request: NextRequest) {
  try {
    const { amount, currency, customer, orderId } = await request.json();
    
    const response = await fetch(`${ORSUNPAY_API_URL}/checkout/sessions`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${ORSUNPAY_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        amount,
        currency,
        country: 'US', // You might detect this from user's IP
        customer,
        returnUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/payment/return`,
        cancelUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/payment/cancel`,
        metadata: { orderId }
      })
    });
    
    const session = await response.json();
    
    if (!response.ok) {
      throw new Error(session.error?.message || 'Failed to create session');
    }
    
    return NextResponse.json({ clientSecret: session.clientSecret });
    
  } catch (error) {
    console.error('Checkout session error:', error);
    return NextResponse.json(
      { error: 'Failed to create checkout session' },
      { status: 500 }
    );
  }
}
```

### Client Component (Checkout Form)

```typescript theme={null}
// app/checkout/checkout-form.tsx
'use client';

import { useEffect, useRef, useState } from 'react';

interface CheckoutFormProps {
  amount: number;
  currency: string;
  customer: {
    email: string;
    firstName: string;
    lastName: string;
  };
  orderId: string;
}

export default function CheckoutForm({ 
  amount, 
  currency, 
  customer, 
  orderId 
}: CheckoutFormProps) {
  const checkoutRef = useRef<HTMLDivElement>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let mounted = true;

    async function initializeCheckout() {
      try {
        // Create checkout session
        const response = await fetch('/api/checkout/session', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            amount,
            currency,
            customer,
            orderId
          })
        });

        const { clientSecret, error } = await response.json();

        if (error) {
          throw new Error(error);
        }

        // Wait for Orsunpay SDK to load
        if (typeof window !== 'undefined' && window.OrsunpayCheckout) {
          mountCheckout(clientSecret);
        } else {
          // Wait for SDK to load
          const script = document.querySelector('script[src*="checkout.orsunpay.com"]');
          if (script) {
            script.addEventListener('load', () => {
              if (mounted) {
                mountCheckout(clientSecret);
              }
            });
          }
        }
      } catch (err) {
        if (mounted) {
          setError(err instanceof Error ? err.message : 'Failed to initialize checkout');
          setIsLoading(false);
        }
      }
    }

    function mountCheckout(clientSecret: string) {
      if (!mounted || !checkoutRef.current) return;

      window.OrsunpayCheckout.mount(checkoutRef.current, {
        clientSecret,
        theme: 'light',
        locale: 'en-US',
        
        onReady() {
          if (mounted) {
            setIsLoading(false);
          }
        },
        
        onSuccess(result) {
          console.log('Payment succeeded:', result);
          // Redirect to success page
          window.location.href = `/order/success?transaction_id=${result.transactionId}`;
        },
        
        onFail(error) {
          console.error('Payment failed:', error);
          if (mounted) {
            setError(error.message);
            setIsLoading(false);
          }
        },
        
        onCancel() {
          console.log('Payment canceled');
          // Redirect back to cart
          window.location.href = '/cart';
        }
      });
    }

    initializeCheckout();

    return () => {
      mounted = false;
    };
  }, [amount, currency, customer, orderId]);

  if (error) {
    return (
      <div className="checkout-error">
        <p>Payment Error: {error}</p>
        <button onClick={() => window.location.reload()}>
          Try Again
        </button>
      </div>
    );
  }

  return (
    <div className="checkout-container">
      {isLoading && (
        <div className="checkout-loading">
          Loading payment methods...
        </div>
      )}
      <div ref={checkoutRef} id="orsunpay-checkout" />
    </div>
  );
}
```

### Page Component

```typescript theme={null}
// app/checkout/page.tsx
import Script from 'next/script';
import CheckoutForm from './checkout-form';

// This would typically come from your cart/order context
const orderData = {
  amount: 5000, // $50.00
  currency: 'USD',
  customer: {
    email: 'customer@example.com',
    firstName: 'John',
    lastName: 'Doe'
  },
  orderId: 'order_123'
};

export default function CheckoutPage() {
  const checkoutSDKUrl = process.env.NODE_ENV === 'production'
    ? 'https://checkout.orsunpay.com/sdk.js'
    : 'https://sandbox-checkout.orsunpay.com/sdk.js';

  return (
    <div className="container mx-auto px-4 py-8">
      <div className="max-w-2xl mx-auto">
        <h1 className="text-2xl font-bold mb-6">Complete Your Payment</h1>
        
        <div className="bg-white p-6 rounded-lg shadow">
          <CheckoutForm {...orderData} />
        </div>
      </div>

      <Script
        src={checkoutSDKUrl}
        strategy="beforeInteractive"
      />
    </div>
  );
}
```

## Event Handling

### Success Event

The `onSuccess` callback receives a result object:

```javascript theme={null}
{
  transactionId: 'tr_clkj3h2n0000qjr8x4c5h6e7b',
  status: 'SUCCESS',
  amount: 5000,
  currency: 'USD',
  metadata: {
    orderId: 'order_123',
    productId: 'prod_456'
  }
}
```

### Error Event

The `onFail` callback receives an error object:

```javascript theme={null}
{
  code: 'card_declined',
  message: 'Your card was declined',
  details: {
    declineReason: 'insufficient_funds'
  }
}
```

### Cancel Event

The `onCancel` callback is triggered when users explicitly cancel the payment flow.

## Styling and Theming

### Theme Options

```javascript theme={null}
OrsunpayCheckout.mount('#checkout', {
  clientSecret: 'cs_...',
  
  // Theme options
  theme: 'light',    // 'light', 'dark', 'auto'
  
  // Custom CSS variables
  style: {
    '--primary-color': '#007bff',
    '--border-radius': '8px',
    '--font-family': 'Arial, sans-serif'
  }
});
```

### CSS Customization

```css theme={null}
/* Custom styles for checkout container */
#orsunpay-checkout {
  max-width: 400px;
  margin: 0 auto;
  padding: 20px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  background: white;
}

/* Responsive design */
@media (max-width: 768px) {
  #orsunpay-checkout {
    padding: 15px;
    margin: 10px;
  }
}
```

## Best Practices

### Security

1. **Server-side session creation**: Always create sessions from your secure backend
2. **Webhook verification**: Use webhooks as the authoritative source of payment status
3. **Client secret protection**: The client secret is safe for frontend use but should be unique per session

### User Experience

1. **Loading states**: Show loading indicators while the checkout initializes
2. **Error handling**: Provide clear error messages and recovery options
3. **Mobile optimization**: Ensure the checkout works well on mobile devices
4. **Accessibility**: The checkout SDK is built with accessibility in mind

### Integration

1. **Webhook backup**: Always implement webhook handling as primary status source
2. **Idempotency**: Handle duplicate success callbacks gracefully
3. **Testing**: Thoroughly test all payment flows in sandbox environment

## Troubleshooting

### Common Issues

| Issue                       | Solution                                  |
| --------------------------- | ----------------------------------------- |
| SDK not loading             | Check script URL and network connectivity |
| Client secret invalid       | Verify session creation on backend        |
| Payment methods not showing | Check country/currency configuration      |
| Success callback not firing | Verify webhook endpoint configuration     |

### Debug Mode

Enable debug mode for additional logging:

```javascript theme={null}
OrsunpayCheckout.mount('#checkout', {
  clientSecret: 'cs_...',
  debug: true // Enable debug logging
});
```

<Tip>
  Always test your integration thoroughly in sandbox mode before going live. Use webhook notifications as the primary source of truth for payment status updates.
</Tip>
