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

# Iframe Embedded Checkout

> How to embed the checkout form in an iframe

## Overview

By embedding ZAFA PAY's checkout page in an iframe, you can complete payments within your site without redirecting users.

## Result Notification Methods

There are two methods for receiving payment results.

| Method          | Description                                 | Use Case                             |
| --------------- | ------------------------------------------- | ------------------------------------ |
| **postMessage** | Send events to parent window via JavaScript | SPAs and JavaScript-controlled pages |
| **redirect**    | Redirect the parent window                  | Server-side result handling          |

<Note>
  The notification method is configured per merchant. Contact support if you need to change it.
</Note>

## Implementation Steps

### 1. Create Payment Request

Create a payment request using `POST /v1/payments` as usual. Get the `payment_url` from the response.

```bash theme={null}
curl -X POST https://api.sandbox.zafapay.com/v1/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100,
    "currency": "usd",
    "external_id": "order_12345",
    "success_redirect_url": "https://your-site.com/success",
    "failure_redirect_url": "https://your-site.com/failure"
  }'
```

### 2. Embed in iframe

Add the `?mode=embedded` parameter to the `payment_url` and set it in the iframe.

```html theme={null}
<iframe
  src="https://pay.zafapay.com/checkout/req_xxx?token=xxx&mode=embedded"
  width="100%"
  height="700"
  style="border: none;"
  allow="payment"
></iframe>
```

<Warning>
  The `mode=embedded` parameter is required. Without it, a redirect will occur when the payment is completed.
</Warning>

***

## postMessage Method

When postMessage method is configured, payment results are sent to the parent window via JavaScript postMessage.

### Receive Events

```javascript theme={null}
window.addEventListener('message', function(event) {
  // Security: Verify origin
  if (event.origin !== 'https://pay.zafapay.com') {
    return;
  }

  const data = event.data;
  if (!data || !data.type) return;

  switch (data.type) {
    case 'payment_success':
      console.log('Payment successful:', data.transactionId);
      // Handle success
      break;
    case 'payment_failed':
      console.log('Payment failed:', data.error);
      // Handle failure
      break;
    case 'payment_cancel':
      console.log('Cancelled');
      // Handle cancellation
      break;
    case 'redirect_required':
      console.log('Redirect required:', data.url);
      // Handle redirect for 3D Secure etc.
      window.open(data.url, '_blank');
      break;
  }
});
```

## Event Types

| Event               | Description                          | Included Data                |
| ------------------- | ------------------------------------ | ---------------------------- |
| `payment_success`   | Payment successful                   | `transactionId`, `requestId` |
| `payment_failed`    | Payment failed                       | `error`, `code`              |
| `payment_cancel`    | User cancelled                       | None                         |
| `redirect_required` | Redirect required for 3D Secure etc. | `url`, `reason`              |

### payment\_success

Sent when payment completes successfully.

```json theme={null}
{
  "type": "payment_success",
  "transactionId": "tx_abc123",
  "requestId": "req_xyz789"
}
```

### payment\_failed

Sent when payment fails.

```json theme={null}
{
  "type": "payment_failed",
  "error": "Card was declined",
  "code": "card_declined"
}
```

### payment\_cancel

Sent when user clicks "Cancel and return to site".

```json theme={null}
{
  "type": "payment_cancel"
}
```

### redirect\_required

Sent when redirect is required for 3D Secure authentication etc.

<Note>
  This event only occurs for specific payment methods. Depending on the payment method, authentication may be handled automatically, and this event may not occur.
</Note>

```json theme={null}
{
  "type": "redirect_required",
  "url": "https://auth.example.com/3ds/...",
  "reason": "3ds_required"
}
```

<Note>
  When redirect is required, open it in a new tab/window or navigate the parent window. Redirects within the iframe may not work due to security restrictions.
</Note>

## Complete Implementation Example

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <title>Checkout Page</title>
</head>
<body>
  <h1>Payment</h1>

  <div id="checkout-container">
    <!-- iframe will be inserted dynamically -->
  </div>

  <script>
    // Set payment URL (get from backend)
    const paymentUrl = 'YOUR_PAYMENT_URL';

    // Embed iframe
    const container = document.getElementById('checkout-container');
    const iframe = document.createElement('iframe');
    iframe.src = paymentUrl + (paymentUrl.includes('?') ? '&' : '?') + 'mode=embedded';
    iframe.style.width = '100%';
    iframe.style.height = '700px';
    iframe.style.border = 'none';
    iframe.setAttribute('allow', 'payment');
    container.appendChild(iframe);

    // Event listener
    window.addEventListener('message', function(event) {
      if (event.origin !== 'https://pay.zafapay.com') return;

      const data = event.data;
      if (!data || !data.type) return;

      switch (data.type) {
        case 'payment_success':
          alert('Payment completed!');
          window.location.href = '/thank-you?tx=' + data.transactionId;
          break;
        case 'payment_failed':
          alert('Payment failed: ' + data.error);
          break;
        case 'payment_cancel':
          if (confirm('Cancel the payment?')) {
            window.location.href = '/cart';
          }
          break;
        case 'redirect_required':
          window.open(data.url, '_blank');
          break;
      }
    });
  </script>
</body>
</html>
```

## Security Considerations

<Steps>
  <Step title="Origin Verification">
    Always verify `event.origin` when receiving postMessage.
  </Step>

  <Step title="HTTPS Required">
    Iframe embedding only works on HTTPS sites.
  </Step>

  <Step title="Use with Webhooks">
    For payment confirmation, always receive and verify webhooks on your server in addition to postMessage.
  </Step>
</Steps>

***

## Redirect Method

When redirect method is configured, payment results are notified by redirecting the parent window.

### Redirect URL

Users are redirected to the `success_redirect_url`, `failure_redirect_url`, or `cancel_redirect_url` specified when creating the payment.

### URL Parameters

| Status  | Redirect To            | Parameters                                          |
| ------- | ---------------------- | --------------------------------------------------- |
| Success | `success_redirect_url` | `?request_id=xxx&transaction_id=xxx&status=success` |
| Failed  | `failure_redirect_url` | `?request_id=xxx&transaction_id=xxx&status=failed`  |
| Cancel  | `cancel_redirect_url`  | `?request_id=xxx&status=cancel`                     |

<Note>
  * Cancel does not include `transaction_id` because the payment was not initiated.
  * If `cancel_redirect_url` is not set, cancellations will redirect to `failure_redirect_url`.
</Note>

### Parameter Details

| Parameter        | Description                             |
| ---------------- | --------------------------------------- |
| `request_id`     | Payment request ID (starts with `req_`) |
| `transaction_id` | Transaction ID (starts with `tx_`)      |
| `status`         | `success` / `failed` / `cancel`         |

### Implementation Example

```javascript theme={null}
// Server-side (Node.js example)
app.get('/payment/callback', (req, res) => {
  const { request_id, transaction_id, status } = req.query;

  switch (status) {
    case 'success':
      // Success handling (also verify with webhook)
      res.redirect('/thank-you?order=' + request_id);
      break;
    case 'failed':
      // Failure handling
      res.redirect('/checkout?error=payment_failed');
      break;
    case 'cancel':
      // Cancel handling
      res.redirect('/cart');
      break;
  }
});
```

<Warning>
  For payment confirmation, always receive and verify webhooks on your server in addition to URL parameters. URL parameters can be tampered with.
</Warning>

***

## Testing

When testing in the Sandbox environment, you can use the following test cards.

| Card Number           | Result                        |
| --------------------- | ----------------------------- |
| `4242 4242 4242 4242` | Success                       |
| `4000 0000 0000 3220` | 3D Secure required            |
| `4000 0000 0000 9995` | Declined (insufficient funds) |
| `4000 0000 0000 0002` | Declined (generic)            |

Use any future date for expiry and any 3 digits for CVC.
