Skip to main content

Overview

By embedding ZAFA PAY’s checkout page in an iframe, you can complete payments within your site without redirecting users.
Iframe embedding provides a seamless payment experience while maintaining PCI DSS compliance.

Implementation Steps

1. Create Payment Request

Create a payment request using POST /v1/payments as usual. Get the payment_url from the response.
curl -X POST https://api.sandbox.zafapay.com/v1/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1000,
    "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.
<iframe
  src="https://pay.zafapay.com/checkout/req_xxx?token=xxx&mode=embedded"
  width="100%"
  height="700"
  style="border: none;"
  allow="payment"
></iframe>
The mode=embedded parameter is required. Without it, a redirect will occur when the payment is completed.

3. Receive postMessage Events

Payment results are sent to the parent window via postMessage.
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

EventDescriptionIncluded Data
PAYMENT_SUCCESSPayment successfultransactionId, requestId
PAYMENT_FAILEDPayment failederror, code
PAYMENT_CANCELUser cancelledNone
REDIRECT_REQUIREDRedirect required for 3D Secure etc.url, reason

PAYMENT_SUCCESS

Sent when payment completes successfully.
{
  "type": "PAYMENT_SUCCESS",
  "transactionId": "tx_abc123",
  "requestId": "req_xyz789"
}

PAYMENT_FAILED

Sent when payment fails.
{
  "type": "PAYMENT_FAILED",
  "error": "Card was declined",
  "code": "card_declined"
}

PAYMENT_CANCEL

Sent when user clicks “Cancel and return to site”.
{
  "type": "PAYMENT_CANCEL"
}

REDIRECT_REQUIRED

Sent when redirect is required for 3D Secure authentication etc.
{
  "type": "REDIRECT_REQUIRED",
  "url": "https://stripe.com/3ds/...",
  "reason": "3ds_required"
}
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.

Complete Implementation Example

<!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

1

Origin Verification

Always verify event.origin when receiving postMessage.
2

HTTPS Required

Iframe embedding only works on HTTPS sites.
3

Use with Webhooks

For payment confirmation, always receive and verify webhooks on your server in addition to postMessage.

Testing

When testing in the Sandbox environment, you can use the following test cards.
Card NumberResult
4242 4242 4242 4242Success
4000 0000 0000 32203D Secure required
4000 0000 0000 9995Declined (insufficient funds)
4000 0000 0000 0002Declined (generic)
Use any future date for expiry and any 3 digits for CVC.