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 |
The notification method is configured per merchant. Contact support if you need to change it.
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": 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.
<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.
postMessage Method
When postMessage method is configured, payment results are sent to the parent window via JavaScript postMessage.
Receive Events
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.
{
"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.
This event only occurs for specific payment methods. Depending on the payment method, authentication may be handled automatically, and this event may not occur.
{
"type": "redirect_required",
"url": "https://auth.example.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
Origin Verification
Always verify event.origin when receiving postMessage.
HTTPS Required
Iframe embedding only works on HTTPS sites.
Use with Webhooks
For payment confirmation, always receive and verify webhooks on your server in addition to postMessage.
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 |
- 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.
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
// 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;
}
});
For payment confirmation, always receive and verify webhooks on your server in addition to URL parameters. URL parameters can be tampered with.
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.