top of page

Stripe決済エラーが断続的に起きるとき、調査で確認する情報

9月13日
読了時間: 12分

Stripeを使った決済で、「普段は問題ないのに一部の利用者だけ失敗する」「決済画面ではエラーになったが、Stripeを見ると処理されている」「Stripeでは成功しているのに自社システムが未決済のまま」といった現象が起きることがあります。


このような断続障害では、エラー画面だけを見ても原因を判断できません。同じ1件の決済について、Stripe側の状態と自社システム側の状態を時系列で突き合わせることが調査の出発点になります。


Stripe決済エラー調査で、Stripeの取引状態・Webhook・自社バックエンドログ・注文DBを突き合わせる流れ

はじめに:決済の「失敗」と「反映されていない」は別の問題


利用者から見ると、どちらも「決済できなかった」に見えることがあります。


しかしシステム側では、少なくとも次のような状態を分けて考える必要があります。


  • Stripe側で決済自体が失敗している


  • Stripe側では決済が成立したが、自社システムへの反映に失敗している


  • 3Dセキュアなどの途中で処理が止まり、まだ最終状態になっていない


  • 自社システムがタイムアウトしたため結果を受け取れず、成功か失敗か判断できない


  • 同じ注文に対して再試行が行われ、複数の決済処理が作られている


特に注意したいのが、「自社画面でエラーになった=Stripeでも失敗した」とは限らないことです。


決済結果が不明なまま利用者や運用担当者が再度支払いを実行すると、すでに成立していた決済に加えて新しい決済が作られ、二重決済につながる可能性があります。


そのため、断続的な決済障害では、まず「最終的にStripe側でどうなっているか」を確認し、その後に自社システムの処理を追います。



結論:1件の決済について4つの証跡をそろえる


調査を始めるとき、最初からシステム全体のソースコードを読む必要はありません。


まず、問題が起きた決済を1件特定し、次の4つを対応付けます。


  1. 利用者・業務側の情報:発生日時、注文・予約ID、金額、決済方法、利用者が見た画面やエラー内容


  2. Stripe側の情報:PaymentIntentやChargeなどのID、現在の状態、エラーコード、APIリクエスト、イベント、Webhook配信履歴


  3. 自社バックエンドのログ:Stripe APIを呼んだ時刻と結果、3Dセキュア等からの戻り処理、Webhook受信、DB更新、例外、再試行


  4. 自社DBの状態:注文・予約の状態、決済状態、Stripe側ID、更新日時、再試行回数など


重要なのは、情報をたくさん集めることではなく、同じ決済を4つの層で追える状態にすることです。


すべての情報が揃っていなくても調査は始められます。ただし、「いつ・誰の・どの注文で起きたか」が分からないと、正常な決済と問題の決済を比較できません。


最低限、発生日時と業務側のIDを残しておくと調査しやすくなります。



日頃から残しておくと、障害調査で役に立つログ


決済障害は、起きてからログを増やしても、そのときの状態をさかのぼれないことがあります。


特に「100回に1回だけ失敗する」といった断続障害では、平常時から1件の決済をStripeと自社システムの両方で追跡できるようにしておくことが重要です。


最低限、次の情報を日頃から残しておくと調査しやすくなります。


1. 自社バックエンドの決済処理ログ


決済開始、Stripe API呼び出し、API応答、3Dセキュア後の戻り処理、決済結果反映、例外発生といった主要な処理点でログを残します。


その際、単に「決済失敗」とだけ書くのではなく、可能な範囲で次を一緒に記録します。


  • 自社の注文・予約ID


  • 自社側の決済試行ID


  • PaymentIntent ID等のStripe側ID


  • 処理日時とタイムゾーン


  • 処理名・処理結果・エラー種別


  • Stripe APIのHTTPステータスやエラーコード


  • StripeレスポンスのRequest-Id


  • POSTリクエストで送信したIdempotency-Key


  • 使用しているStripe APIバージョン(明示している場合)


  • アプリケーションのバージョンやリリース識別子


Stripeでは各APIリクエストにRequest-Idが付与され、レスポンスヘッダーやDashboard/Workbenchのリクエストログから確認できます。


自社ログにもこのIDを残しておくと、後から自社ログとStripe側のAPIリクエストを1対1で照合しやすくなります。



2. Webhookの「受信」だけでなく「処理結果」まで残す


Webhookは、届いたことだけ記録しても十分ではありません。


少なくとも、次の情報を追えるようにします。


  • Stripe Event ID


  • イベント種別


  • 対象のPaymentIntent等のID


  • 受信時刻


  • 自社エンドポイントが返したHTTPステータス


  • Webhook署名検証の成否


  • 重複イベントとして無視したか


  • DB更新を実行したか


  • DB更新前後の決済状態


  • 処理成功/失敗と例外内容


StripeはWebhookイベントの配信順序を保証しておらず、同じイベントが複数回届く場合もあります。


そのため、「受信した」という事実だけでなく、そのイベントを自社側でどう処理し、最終的にどの状態へ更新したかが分かるログが重要です。


3. DBの決済状態を変更した履歴


現在のDB状態だけでは、「なぜこの状態になったのか」が分からないことがあります。


可能であれば、次の情報を追跡できるようにします。


  • 注文・予約ID


  • 変更前の決済状態


  • 変更後の決済状態


  • 変更日時


  • 変更契機(API応答、Webhook、バッチ、管理画面操作など)


  • 関連するPaymentIntent IDやEvent ID


たとえば現在のDBが「未決済」でも、過去に一度「決済済」へ更新された後、遅れて届いたイベントによって状態が戻ったのであれば、現在値だけを見ても原因は分かりません。


状態そのものだけでなく、状態遷移の履歴を残すと調査力が大きく変わります。


4. Stripeと自社データを結び付けるID


ログが豊富でも、Stripeの取引と自社の注文を結び付けられなければ調査に時間がかかります。


自社DBでPaymentIntent ID等を保持することに加え、必要に応じてStripeオブジェクトのmetadataへ自社の注文IDなどを設定しておく方法もあります。


Stripe公式も、metadataの利用例として、自社システムの一意なIDをStripeオブジェクトへ関連付ける方法を案内しています。



ただし、metadataやログへカード番号、CVC、Secret Key、Webhook署名シークレット、アクセストークン、Authorizationヘッダー、Cookie等の秘密情報を記録してはいけません。


個人情報も必要最小限とし、調査に不要な値は記録しない、またはマスキングします。


ログには調査に必要な識別子を残し、秘密情報そのものは残さない設計にします。


5. システム全体を横断できる共通ID


複数のAPI、バックエンド、バッチ、Webhook処理をまたぐシステムでは、同じ処理を横断して検索できるrequest_idやtrace_idのような共通IDがあると有効です。


たとえば、注文ID・自社決済試行ID・trace_id・PaymentIntent IDを相互に検索できるようにし、1つのPaymentIntentに対して、API呼び出しごとの複数のRequest-Idや、状態変化ごとの複数のWebhook Event IDをたどれる状態にします。


一直線の1対1関係ではなく、PaymentIntent IDを軸に関連する証跡を束ねて追えることが重要です。


障害調査では「大量のログがあること」より、1件の決済について関連ログを数分で集められることの方が重要です。


また、直前のリリース、設定変更、APIバージョン変更等の履歴も残しておくと、「特定の変更以降だけ失敗率が上がった」といった比較ができます。



Stripe側ではPaymentIntentの現在地とエラー内容を見る


PaymentIntent(Stripeで1回の支払い処理のライフサイクルを追跡するオブジェクト)を使ったStripe連携では、支払い処理がどの段階にあるかをステータスで追跡できます。


たとえば、追加認証が必要な場合はrequires_action、処理が完了した場合はsucceeded、決済試行が失敗して別の支払い方法が必要になった場合はrequires_payment_methodになる場合があります。


Stripe公式のPaymentIntentライフサイクルには、各状態の意味が整理されています。


調査では、画面に表示されたエラー文だけではなく、次を確認します。


  • 問題の決済に対応するPaymentIntent ID


  • 現在のPaymentIntentステータス


  • last_payment_errorのcode、decline_code、message等


  • 関連するCharge


  • 決済が試行された時刻


  • 3Dセキュアなど追加操作が必要だったか


Stripe公式のDeclinesドキュメントでは、決済失敗の原因として、カード発行会社等による拒否、Stripe側でのブロック、不正なAPIリクエストなどを分けて説明しています。


そのため、「Stripeが不安定だった」とまとめるのではなく、決済自体が拒否されたのか、認証が完了していないのか、APIの呼び出しが不正だったのかをまず分けます。


なお、古いStripe連携ではPaymentIntent以外の方式が使われている場合があります。


その場合は、現在の実装方式を確認してから、追跡すべきオブジェクトやログを決めます。



Workbenchでは失敗したAPIだけでなく、成功した決済と比較する


StripeのWorkbenchでは、LogsタブでAPIリクエスト、Eventsタブでイベントと配信試行、WebhooksタブのEvent deliveriesでWebhookの配信状態や過去のHTTPステータスを確認できます。


断続障害では、失敗した1件だけを見るより、同じ処理で成功した決済と比較すると差分を見つけやすくなります。


たとえば、


  • 同じAPIエンドポイントへ送っているか


  • リクエストの時刻や送信元に違いがないか


  • 特定の決済方法・金額・利用条件だけで失敗していないか


  • 失敗したリクエストのレスポンスにどのエラーが返っているか


  • 問題発生時刻の前後にWebhook配信失敗がないか


といった点です。


Stripe側のログと自社ログの時刻を合わせて見ることで、「Stripe API呼び出し前に自社処理が失敗した」「Stripeからは成功レスポンスを受けているが、その後のDB更新で止まった」といった境界も見つけやすくなります。



Webhookは「届いたか」だけでなく、重複・再送・順序まで確認する


決済結果をWebhookで自社システムへ反映している場合、Webhookの配信履歴も重要です。


Stripe公式のWebhookドキュメントでは、配信に失敗した場合の自動再送があり、イベントの配信順序は保証されず、同じイベントを複数回受信することもあると案内しています。


そのため、調査では次を確認します。


  • Stripe側ではWebhookを送信しているか


  • 自社エンドポイントはどのHTTPステータスを返したか


  • 自社サーバーに受信ログが残っているか


  • 同じイベントIDを複数回処理していないか


  • 後から届いたイベントで古い状態へ戻していないか


  • Webhook受信後のDB更新で例外やロールバックが起きていないか


「Webhookが届かなかった」ように見えても、Stripe側では送信済みで、自社側の受信後処理だけが失敗している場合があります。


反対に、Webhookを受信したことだけをもって業務処理を完了させる設計でも、状態の不整合が起きることがあります。


通知の設計そのものについては、決済連携で見落としやすい「失敗・取消・再実行・通知」の設計で詳しく解説しています。



Stripe固有の問題と、自社システム側の状態不整合を分ける


ここまでのPaymentIntent、Workbench、WebhookはStripe固有の確認ポイントです。


一方で、決済障害の調査で本当に難しいのは、Stripe側の取引状態と、自社の注文・予約・会員等の業務状態が一致しているかを確認する部分です。


たとえば、


  • Stripe:succeeded / 自社DB:未決済


  • Stripe:失敗 / 自社DB:支払済


  • Stripe:1件 / 自社DB:同じ注文に対して複数の決済ID


  • Stripe:決済成立 / 自社DB:注文確定処理だけ失敗


のような組み合わせがないかを確認します。


当社がStripeからGMO-PGへ決済基盤を段階移行した案件では、新規決済を新しいPSPへ切り替える一方で、既存の未確定予約や既存サブスクリプションは一定期間Stripeで継続しました。


このとき重視したのは、どの予約・サブスクリプションをどちらのPSPへ送るかを誤らないこと、二重決済を防ぐこと、新旧の決済情報と自社DBが整合していることを確認することでした。


障害調査でも考え方は同じです。


Stripeのログだけ、自社DBだけを見るのではなく、外部PSPの状態と自社業務の状態を対応付けて確認します。



再試行する前に、すでに成立していないか確認する


通信タイムアウトや一時的なエラーが起きると、「もう一度APIを呼べば直る」と考えたくなります。


しかし決済では、処理自体はStripe側で完了しているのに、応答だけ自社システムへ届かなかった可能性を考える必要があります。


Stripe APIのIdempotent requestsでは、同じ操作を安全に再試行するためにIdempotency-Keyを利用できます。


これは、通信障害などで同じPOSTリクエストを再送するときに、意図しない重複操作を防ぐための仕組みです。


ただし、既存システムがどのようにIdempotency-Keyを発行・保存しているか、注文IDとPaymentIntentをどう対応付けているかは実装ごとに異なります。


そのため、本番障害時に仕組みを確認せず手作業で決済を再実行するのは避けます。


先に、


  • Stripe側の最終状態


  • 自社DBに保存された決済ID


  • 同じ注文に対する過去の試行


  • 再試行時の重複防止方法


を確認し、そのうえで再実行の可否を判断します。


なお、ここで扱っているのは障害発生後の確認手順です。


再試行・通知・状態遷移をどう設計するかについては、決済連携で見落としやすい「失敗・取消・再実行・通知」の設計で別途解説しています。



調査を依頼するときに渡せるとよい情報


開発会社へ調査を依頼する場合、次の情報があると初動が速くなります。


  • 発生日時。できれば時分秒とタイムゾーン


  • 対象の注文・予約・会員等の業務ID


  • 決済金額と決済方法


  • 利用者が見た画面・エラーメッセージ


  • PaymentIntent ID、Charge ID等のStripe側ID


  • Stripe Workbenchで確認できる該当ログ・イベント


  • 自社アプリケーションのログ


  • 自社DBの注文・決済レコード


  • 直前に行ったリリース、設定変更、Stripe連携変更があればその内容


  • 同時刻帯の成功例があれば、その決済を特定できる情報


一方で、カード番号全桁、CVC、StripeのSecret Key等をメールやチャットで送る必要はありません。


調査に必要な権限や機密情報は、作業開始後に安全な受け渡し方法を決めます。



1日調査で切り分けやすいケースと、追加対応が必要なケース


断続障害でも、発生した決済を特定でき、Stripe側の履歴、自社ログ、DB状態が残っていれば、短時間の調査で原因箇所をかなり絞れる場合があります。


たとえば、


  • Stripe側で拒否されているのか


  • 3Dセキュア等の途中状態で止まっているのか


  • Webhook受信・処理に問題があるのか


  • 自社DB更新時に失敗しているのか


  • 再試行や重複処理が起きているのか


といった切り分けです。


一方、ログが残っていない、発生条件が特定できない、古い決済連携で仕様が不明、複数システムをまたいで状態を補正する必要がある、といった場合は、1回の調査だけで原因確定・修正まで完了しないことがあります。


その場合でも、まず「どこまで分かったか」「次に何を確認すべきか」「安全に直すにはどの範囲を改修するか」を整理すると、いきなり大きな改修へ進むより判断しやすくなります。



まとめ:断続的な決済エラーは、1件の取引を最後まで追う


Stripe決済が断続的に失敗するときは、エラー画面やStripeのダッシュボードだけで原因を決めないことが重要です。


まず問題が起きた決済を1件特定し、


  • StripeのPaymentIntent・エラー・APIログ


  • Webhookの配信と自社側の受信処理


  • 自社バックエンドのログ


  • 注文・予約と決済のDB状態


を時系列で突き合わせます。


そして、Stripe側では成功しているのか、自社システムへの反映で止まっているのか、最終結果が不明なのかを切り分けます。


プレイリーソリューションズでは、Stripe等の決済障害について、まずIT不具合の1日調査・解決提案パックでログ・Webhook・DB状態・再現条件を確認し、原因箇所と影響範囲を整理できます。


調査の結果、決済設計、状態管理、Webhook処理、既存DB連携等に改修が必要な場合は、その後の決済改修・再構築まで別途対応します。


bottom of page