継続決済(リカーリング)機能を使用すると、顧客のカード情報を安全に保存し、2回目以降の決済でカード入力なしに課金できます。サブスクリプションや定期課金に最適です。
継続決済は一部のゲートウェイでのみ利用可能です。利用可否については担当者にお問い合わせください。
継続決済フロー
初回決済(カード保存)
初回決済時に save_card: true と customer_id を指定してカードを保存します。
リクエスト
curl -X POST https://api.sandbox.zafapay.com/v1/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 1000,
"currency": "jpy",
"customer_id": "cust_abc123",
"save_card": true,
"external_id": "subscription_initial_001",
"metadata": {
"plan": "premium"
}
}'
顧客を識別する一意のID。加盟店側で管理するユーザーIDなどを指定してください。
継続決済の初回では true を指定してください。決済完了後にカード情報が保存されます。
レスポンス
{
"id": "req_abc123",
"status": "pending",
"amount": 1000,
"currency": "jpy",
"payment_type": "initial",
"payment_url": "https://pay.sandbox.zafapay.com/checkout/req_abc123?token=xxx",
"created_at": "2025-01-01T00:00:00.000Z"
}
payment_type: "initial" は、このリクエストがカード保存を伴う初回決済であることを示します。payment_method_id は決済完了後の Webhook で受け取ってください。
顧客を payment_url にリダイレクトして決済を完了させます。
決済完了時のWebhook
{
"event": "payment.succeeded",
"transaction_id": "tx_abc123",
"status": "succeeded",
"amount": 1000,
"currency": "jpy",
"payment_method_id": "pmi_xyz789",
"save_card": true,
"is_recurring": false,
"customer_id": "cust_abc123",
...
}
payment_method_id(pmi_xxx 形式)を保存しておくと、2回目以降の決済で使用できます。
保存済みカードの確認
顧客の保存済みカードを一覧取得できます。
curl https://api.sandbox.zafapay.com/v1/customers/cust_abc123/payment-methods \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
レスポンス
{
"customer_id": "cust_abc123",
"payment_methods": [
{
"id": "pmi_xyz789",
"type": "card",
"connector_id": "conn_xxx",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028
},
"is_default": true,
"status": "active",
"created_at": "2025-01-01T00:00:00Z"
}
]
}
継続決済の実行
保存済みの payment_method_id を使用して、カード入力なしで決済できます。
リクエスト
curl -X POST https://api.sandbox.zafapay.com/v1/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 1000,
"currency": "jpy",
"customer_id": "cust_abc123",
"payment_method_id": "pmi_xyz789",
"external_id": "subscription_renewal_002",
"metadata": {
"plan": "premium",
"billing_cycle": 2
}
}'
レスポンス
継続決済は即時処理されます(payment_url は返りません)。
{
"id": "req_def456",
"transaction_id": "tx_def456",
"status": "succeeded",
"amount": 1000,
"currency": "jpy",
"payment_type": "recurring",
"created_at": "2025-02-01T00:00:00.000Z"
}
payment_type: "recurring" は、保存済みカードを使用した継続決済であることを示します。
決済完了時のWebhook(成功)
{
"event": "payment.succeeded",
"transaction_id": "tx_def456",
"status": "succeeded",
"amount": 1000,
"currency": "jpy",
"payment_method_id": "pmi_xyz789",
"is_recurring": true,
"customer_id": "cust_abc123",
...
}
継続決済のWebhookでは is_recurring: true が設定されます。
決済失敗時のWebhook
継続決済が失敗した場合、payment.failed イベントが送信されます。error オブジェクトに失敗の詳細が含まれます。
{
"event": "payment.failed",
"transaction_id": "tx_def456",
"status": "failed",
"amount": 1000,
"currency": "jpy",
"payment_method_id": "pmi_xyz789",
"is_recurring": true,
"customer_id": "cust_abc123",
"error": {
"code": "CARD_DECLINED",
"category": "hard_decline",
"message": "The card was declined",
"recommended_action": "contact_customer"
},
...
}
error.recommended_action の値に応じて、適切な対応を行ってください。| 値 | 説明 |
|---|
retry | 一時的なエラー。時間をおいてリトライ可能 |
contact_customer | 顧客への連絡が必要(残高不足等) |
use_different_card | 別のカードでの決済が必要(期限切れ等) |
none | 特別な対応は不要 |
決済方法の管理
カードのブロック
不正利用の疑いがある場合など、特定のカードをブロックできます。
curl -X POST https://api.sandbox.zafapay.com/v1/customers/payment-methods/pmi_xyz789/block \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "不正利用の疑い"
}'
ブロック解除
curl -X POST https://api.sandbox.zafapay.com/v1/customers/payment-methods/pmi_xyz789/unblock \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
カードの削除(Detach)
顧客の要求によりカード情報を完全に削除する場合は、detachを使用します。
curl -X POST https://api.sandbox.zafapay.com/v1/customers/payment-methods/pmi_xyz789/detach \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
レスポンス
{
"id": "pmi_xyz789",
"deleted": true
}
削除されたカードは復元できません。再度利用するには、顧客にカードを再登録してもらう必要があります。
ブロックと削除の違い:
- ブロック: 一時的に使用を停止(不正対策)。後でアンブロック可能
- 削除(Detach): 完全に削除(GDPR対応、顧客の削除要求)。復元不可
エラーハンドリング
継続決済がサポートされていない場合
{
"error": {
"type": "invalid_request_error",
"code": "recurring_not_supported",
"message": "Recurring payments are not supported for this connector",
"request_id": "req_xxx"
}
}
カードの有効期限切れ
{
"error": {
"type": "payment_error",
"code": "expired_card",
"message": "The card has expired",
"request_id": "req_xxx"
}
}
カードの有効期限が切れた場合や、カード会社によって拒否された場合は、顧客に新しいカードを登録してもらう必要があります。
ベストプラクティス
customer_id を一意に管理
加盟店側のユーザーIDと customer_id を1対1で紐付けてください。同じ顧客に異なる customer_id を使用すると、カード情報が分散してしまいます。
payment_method_id を永続化
Webhookで受け取った payment_method_id をデータベースに保存し、次回以降の決済に使用してください。
失敗時のリトライ戦略
カード決済は一時的なエラーで失敗することがあります。指数バックオフでリトライし、複数回失敗した場合は顧客に通知してください。
有効期限の管理
保存済みカードの有効期限を監視し、期限が近づいたら顧客にカード更新を促してください。