ZAFA PAYの決済ページをiframeで埋め込むことで、ユーザーをリダイレクトせずに自社サイト内で決済を完結させることができます。
結果通知方式
決済結果の通知方式は2種類あります。
| 方式 | 説明 | 用途 |
|---|
| postMessage | JavaScriptで親ウィンドウにイベント送信 | SPAなどJavaScript制御が可能な場合 |
| リダイレクト | 親ウィンドウごとリダイレクト | サーバーサイドで結果を処理したい場合 |
利用する方式は加盟店ごとに事前に設定されます。変更が必要な場合はサポートにお問い合わせください。
実装手順
1. 決済リクエストを作成
通常通りPOST /v1/paymentsで決済リクエストを作成します。レスポンスのpayment_urlを取得します。
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. iframeに埋め込む
payment_urlに?mode=embeddedパラメータを追加してiframeに設定します。
<iframe
src="https://pay.zafapay.com/checkout/req_xxx?token=xxx&mode=embedded"
width="100%"
height="700"
style="border: none;"
allow="payment"
></iframe>
mode=embeddedパラメータは必須です。これがないと、決済完了時にリダイレクトが発生します。
postMessage方式
postMessage方式が設定されている場合、決済の結果はJavaScriptのpostMessageで親ウィンドウに通知されます。
イベントを受信
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':
console.log('決済成功:', data.transactionId);
// 成功処理
break;
case 'payment_failed':
console.log('決済失敗:', data.error);
// 失敗処理
break;
case 'payment_cancel':
console.log('キャンセル');
// キャンセル処理
break;
case 'redirect_required':
console.log('リダイレクトが必要:', data.url);
// 3Dセキュア等のリダイレクト処理
window.open(data.url, '_blank');
break;
}
});
イベントタイプ
| イベント | 説明 | 含まれるデータ |
|---|
payment_success | 決済成功 | transactionId, requestId |
payment_failed | 決済失敗 | error, code |
payment_cancel | ユーザーがキャンセル | なし |
redirect_required | 3Dセキュア等のリダイレクトが必要 | url, reason |
payment_success
決済が正常に完了した場合に送信されます。
{
"type": "payment_success",
"transactionId": "tx_abc123",
"requestId": "req_xyz789"
}
payment_failed
決済が失敗した場合に送信されます。
{
"type": "payment_failed",
"error": "カードが拒否されました",
"code": "card_declined"
}
payment_cancel
ユーザーが「キャンセルしてサイトに戻る」をクリックした場合に送信されます。
{
"type": "payment_cancel"
}
redirect_required
3Dセキュア認証などでリダイレクトが必要な場合に送信されます。
このイベントは特定の決済方法でのみ発生します。決済方法によっては、認証処理が自動的に行われるため、このイベントが発生しない場合があります。
{
"type": "redirect_required",
"url": "https://auth.example.com/3ds/...",
"reason": "3ds_required"
}
リダイレクトが必要な場合、新しいタブ/ウィンドウで開くか、親ウィンドウでナビゲートしてください。iframe内でのリダイレクトはセキュリティ上の制限により機能しない場合があります。
完全な実装例
<!DOCTYPE html>
<html>
<head>
<title>決済ページ</title>
</head>
<body>
<h1>お支払い</h1>
<div id="checkout-container">
<!-- iframeは動的に挿入 -->
</div>
<script>
// 決済URLを設定(バックエンドから取得)
const paymentUrl = 'YOUR_PAYMENT_URL';
// 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);
// イベントリスナー
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('決済が完了しました!');
window.location.href = '/thank-you?tx=' + data.transactionId;
break;
case 'payment_failed':
alert('決済に失敗しました: ' + data.error);
break;
case 'payment_cancel':
if (confirm('決済をキャンセルしますか?')) {
window.location.href = '/cart';
}
break;
case 'redirect_required':
window.open(data.url, '_blank');
break;
}
});
</script>
</body>
</html>
セキュリティ考慮事項
オリジン検証
postMessageを受信する際は、必ずevent.originを検証してください。
HTTPS必須
iframe埋め込みはHTTPSサイトでのみ動作します。
Webhookとの併用
決済結果の確定にはpostMessageだけでなく、必ずサーバーサイドでWebhookを受信して検証してください。
リダイレクト方式
リダイレクト方式が設定されている場合、決済結果は親ウィンドウへのリダイレクトで通知されます。
リダイレクト先URL
決済作成時に指定したsuccess_redirect_url、failure_redirect_url、cancel_redirect_urlにリダイレクトされます。
URLパラメータ
| ステータス | リダイレクト先 | パラメータ |
|---|
| 成功 | success_redirect_url | ?request_id=xxx&transaction_id=xxx&status=success |
| 失敗 | failure_redirect_url | ?request_id=xxx&transaction_id=xxx&status=failed |
| キャンセル | cancel_redirect_url | ?request_id=xxx&status=cancel |
- キャンセル時は決済実行前のため
transaction_idは含まれません。
cancel_redirect_urlが設定されていない場合、キャンセル時はfailure_redirect_urlにリダイレクトされます。
パラメータ詳細
| パラメータ | 説明 |
|---|
request_id | 決済リクエストID(req_で始まる) |
transaction_id | トランザクションID(tx_で始まる) |
status | success / failed / cancel |
実装例
// サーバーサイド(Node.js例)
app.get('/payment/callback', (req, res) => {
const { request_id, transaction_id, status } = req.query;
switch (status) {
case 'success':
// 成功処理(Webhookでも検証すること)
res.redirect('/thank-you?order=' + request_id);
break;
case 'failed':
// 失敗処理
res.redirect('/checkout?error=payment_failed');
break;
case 'cancel':
// キャンセル処理
res.redirect('/cart');
break;
}
});
決済結果の確定には、URLパラメータだけでなく必ずサーバーサイドでWebhookを受信して検証してください。URLパラメータは改ざんされる可能性があります。
テスト
Sandbox環境でテストする場合は、以下のテストカードを使用できます。
| カード番号 | 結果 |
|---|
4242 4242 4242 4242 | 成功 |
4000 0000 0000 3220 | 3Dセキュア必須 |
4000 0000 0000 9995 | 残高不足で拒否 |
4000 0000 0000 0002 | 一般的な拒否 |
有効期限は将来の日付、CVCは任意の3桁を使用してください。