オンライン決済実装研究所
実装ガイド

決済 API の冪等性を実装事故ベースで理解する

Stripe を中心に、冪等性キーが必要な理由、注文 ID とどう結びつけるか、再送やタイムアウト時に何を保証できるかを整理します。

この記事の要点

決済 API の事故で多いのは、通信失敗後の再実行を『もう一度 POST するだけ』で済ませることです。冪等性キーを内部注文 ID と束ねると、二重課金と調査コストを大きく減らせます。

公開日: 2026/4/12 更新日: 2026/4/12 著者: オンライン決済実装研究所 編集部

結論

冪等性は「高級な最適化」ではなく、決済 API を本番で壊さないための最低限の安全装置 です。

Stripe の公式 API でも、create / update 系の安全な再試行のために Idempotency-Key が前提になっています。
PayPal や Square でも同様の仕組みがあり、主要な決済 API では共通の基本知識です。

何を防ぐのか

典型的な事故は次の 3 つです。

  1. 決済 API への POST がタイムアウトした
  2. ユーザーが購入ボタンを連打した
  3. ワーカーやジョブが失敗して同じ処理を再実行した

冪等性がないと、このとき「サーバー側では成功していたが、アプリは失敗だと思って再送する」という最悪の二重課金パターンが起きます。

Stripe での基本ルール

Stripe の idempotent requests では、同じ意味の create / update リクエストを 同じキーで再送 します。

押さえるべき点は以下です。

Stripe は最初のレスポンス結果を保存し、同じキーの再送には同じ結果を返します。
成功だけでなく、失敗や 500 も含めて同じ結果が返る点が重要です。

内部注文 ID とどう結びつけるか

おすすめは、内部注文 ID を先に作ってから、その注文に対する API リクエストの冪等性キーを派生させる ことです。

order_20260412_001
  -> checkout-session:order_20260412_001
  -> refund:order_20260412_001:1

こうしておくと、調査時に「このキーはどの注文のどの操作か」がすぐ分かります。

どこで使うべきか

create 系 API

update 系 API

一方で、GET や自然に idempotent な処理は別問題です。

実装パターン

悪い例

await stripe.checkout.sessions.create(payload);

通信エラー時にこのまま再送すると、同じ意図の Session が複数できる余地があります。

良い例

const order = await db.orders.insert({ ... });

const session = await stripe.checkout.sessions.create(payload, {
  idempotencyKey: `checkout-session:${order.id}`
});

これで「この注文の checkout session 作成」という論理操作が一意になります。

冪等性があっても防げないこと

業務ロジックの重複

同じ Session を一回しか作らなくても、Webhook を二回処理すれば二重反映は起きます。
Webhook 側は event.id で別に重複防止が必要です。

意味の違う再実行

同じキーでパラメータだけ変えて再送するのは危険です。
「同じ論理操作」かどうかをサーバー側で決める必要があります。

UI 連打の完全防止

フロント側でもボタン disable や pending 表示は必要です。
冪等性は最後の砦であって、UI 改善の代わりではありません。

主要サービスの違い

サービス代表的な仕組みメモ
StripeIdempotency-Keycreate / update の再試行を安全にする
PayPalPayPal-Request-Id同じ POST の再送で最新状態を返す
Squareidempotency_keyCreatePayment などで必須の場面がある

つまづきポイント

キーをクライアントが自由に決める

ユーザー操作単位で安定しないキーになると、同じ注文でも違うキーで飛びやすくなります。

返金と購入で同じキー設計を使い回す

購入と返金は別操作です。
refund:{orderId}:{attempt} のように意味単位で分けた方が安全です。

冪等性を入れたから内部注文の unique 制約は不要だと思う

それは別レイヤーです。
DB の unique 制約、注文 ID、Webhook の重複防止は残ります。

併読推奨

よくある質問

冪等性キーと注文 ID は同じものですか?

違います。注文 ID は業務上の一意性、冪等性キーは API 再試行の安全性のために使います。

Webhook の重複防止にも冪等性キーを使いますか?

Webhook 側は通常 event ID で重複処理を防ぎます。API リクエスト側の冪等性とは役割が違います。

次に読む記事

障害運用

Stripe Webhook で起きやすい失敗を運用視点で10個に整理する

Stripe Webhook で起きやすい失敗を、署名検証、再送、冪等性、順不同イベント、監視不足の観点から整理します。

Webhook障害の多くは、イベントを一回だけ来る前提で処理していることが原因です。署名、保存、冪等性、再処理導線までを最初から含めると、後の運用が大きく楽になります。

2026/4/10 Stripe / Webhook / 冪等性
実装比較

アプリに決済機能を組み込む方法を Stripe 中心に比較する

VibeCoding 初学者向けに、Stripe を軸に PayPal・Square・PAY.JP・KOMOJU を比較し、どの導入方式を選ぶと失敗しにくいか整理します。

最初に選ぶべきは料金表ではなく、Hosted checkout、埋め込みフォーム、定期課金、複数事業者対応のどれが必要かです。Stripe は守備範囲が広く、国内手段や既存ユーザー基盤が重要なら他候補も十分ありえます。

2026/4/12 Stripe / 決済代行 / PAY.JP
実装ガイド

Stripe Billingとサブスクリプションを実装目線で整理する

Billing・Subscription・Invoice・Customer Portal の役割分担と、PaymentIntent ベースの単発決済との境界、運用でハマりやすい点を整理します。

定期課金は「プラン」「請求サイクル」「失効カード」の3つがセットです。Billing を中心に置きつつ、顧客ポータルと請求書をどう位置づけるかを実務の言葉でまとめます。

2026/4/12 Stripe / Billing / Subscription