先に押さえること
PaymentIntent は「支払いが今どの段階にいるか」を表す箱です。
成功したかどうかだけを見る設計 にすると、再認証・失敗・再試行・Webhook再送のどこかで破綻します。
最低限の状態イメージ
requires_payment_method
-> requires_confirmation
-> requires_action
-> processing
-> succeeded
別ルート:
requires_confirmation
-> requires_capture
-> succeeded
終端:
canceled
失敗やキャンセルの分岐もあるため、アプリ側では「最終成功」だけでなく「いま何待ちか」を持つ必要があります。
Stripe の公式ステータスはもう少し細かく、特に見落としやすいのが次の2つです。
requires_capture: オーソリのみ取り、後から capture するフローcanceled: ユーザー放棄、重複、Fraud 判定などで終了した状態
さらに、すべての決済がこの順番で必ず全部の状態を通るわけではありません。
アプリは「想定していた矢印」ではなく、受け取った status を正として扱う方が安全です。
実装で特に危ない状態
processing
processing は「だいたい成功」ではありません。
支払手段によっては、ここから成功にも失敗にも進みえます。
requires_capture
別途 capture する設計では、succeeded ではなく requires_capture で一度止まります。
このケースを知らずに「認証済みだから売上計上」とすると、未 capture の売上が混ざります。
canceled
Checkout Session と違って、PaymentIntent 自体はキャンセルされることがあります。
内部注文でも failed と canceled を分けておくと、再購入導線や CS 対応が整理しやすいです。
実装で事故になりやすい点
1. succeeded 以前に受注確定してしまう
認証待ちや processing の段階で内部注文を確定すると、返金や取消の整合性が崩れます。
2. webhook と同期レスポンスの責務が重なる
画面レスポンスで注文確定もやり、Webhookでも確定すると二重反映が起きます。
3. 冪等性キーと内部注文IDが結びついていない
再送や再試行のたびに別注文として扱うと、運用時に追跡できません。
冪等性キー は「API 再実行の安全性」、内部注文 ID は「業務上の一意性」で、役割が違います。
4. requires_capture を見落とす
手動 capture を使うのに succeeded だけで分岐していると、認証済み未売上の注文が漏れます。
5. 3Dセキュアを状態ではなく画面挙動で判定する
3Dセキュア は UI 上ではモーダルや別画面に見えますが、サーバーから見ると requires_action を経由する決済状態です。
見た目ではなく status と Webhook イベントで判断してください。
内部注文モデルへの写し方
Stripe のステータスをそのまま UI に見せるより、社内では少し抽象化した方が運用しやすいです。
| Stripe 側 | 内部状態の例 | 補足 |
|---|---|---|
requires_payment_method | pending_payment_method | 入力不足や失敗後の再入力待ち |
requires_action | pending_customer_action | 3DS 認証など利用者操作待ち |
processing | pending_settlement | 非同期確定待ち |
requires_capture | authorized | 後 capture 前提 |
succeeded | paid | 初めて受注確定候補になる |
canceled | canceled | abandoned / duplicate など |
実務でのすすめ方
- 画面側: 「次に進めるか」を返す
- サーバー側:
PaymentIntentと内部注文を紐づける - Webhook 側: 最終反映の責務を集約する
- 管理画面: 状態遷移を追跡できるようにする
併読推奨
よくある質問
失敗時に新しい PaymentIntent を作るべきですか?
ケース次第ですが、元の Intent をどう扱うかを先に決めずに再生成すると整合性事故が起きやすいです。
requires_confirmation は必ず通りますか?
いいえ。確認方法や UI によっては見えにくいことがあります。全ステータスを必ず順番通りに通る前提にはしないでください。