Mistake #64 : Double Submit

連打された
「購入ボタン」の悲劇

「あれ、反応しないな?」カチカチ...
たった数回のクリックが、ユーザーのクレジットカード明細を
同じ金額の請求で埋め尽くすことになりました。

💸 お金に関わる危険度チェック
  • 送信ボタンを押しても、「処理中」のアニメーションが出ない
  • 送信ボタンを一度押した後も、ボタンが押せる状態の(グレーにならない)ままである
  • 完了画面が出る前にブラウザの「戻る」ボタンを押すと、エラーになる
  • 過去に「二重登録されてます」という問い合わせを受けたことがある
⚠️ 警告:これは「システムバグ」以前の欠陥です。
ユーザーに金銭的被害を与える可能性があり、訴訟リスクに直結します。

01. インターネットは不安定である

領収書
ID: 001
金額: ¥10,000
[決済完了]
領収書
ID: 002
金額: ¥10,000
[決済完了]

「ボタンを押せば、すぐに画面が切り替わる」
それは開発環境(ローカル)だけの話です。
地下鉄の中や、電波の悪いカフェ、混雑したWi-Fi環境では、ボタンを押してから反応が返ってくるまでに数秒〜数十秒かかることはザラにあります。

この「空白の時間」にユーザーは何をするでしょうか?
「押せてないのかな?」と不安になり、もう一度ボタンを押します。
その結果、システムは律儀に「2回目の購入処理」を実行してしまいます。
悪意などありません。ただユーザーは不安だっただけなのです。

「ユーザーはボタンを一回しか押さない」
という性善説(願望)を捨てろ。

02. 二重決済を防ぐ3つの防壁

「連打禁止」と画面に書くのは対策ではありません。
システム側で物理的に防ぐ必要があります。

対策A:ボタンの無効化(フロントエンド)

最も基本かつ効果的な対策です。
ボタンがクリックされた瞬間に、JavaScriptでボタンを「無効化(Disabled)」状態にします。
また、ボタントップに「処理中...」などのグルグル回るアイコン(スピナー)を表示することで、
「ちゃんと動いていますよ」とユーザーを安心させることができます。

対策B:トークンによる制御(バックエンド)

画面を表示した時に、システム内部で「合言葉(ワンタイムトークン)」を発行しておきます。
ボタンが押されたら、その合言葉をサーバーで確認し、一度使われた合言葉は無効にします。
万が一2回目のリクエストが来ても、「この合言葉は使用済みです」としてエラーにすることができます。

対策C:ユニーク制約(データベース)

最後の砦です。
注文データなどに「同じ注文IDなら1つしか登録できない」という制約(ユニーク制約)をデータベースにかけておきます。
こうすれば、プログラムがミスをして2回登録しようとしても、データベース側で「重複エラー」として弾くことができます。

03. 信頼は「一瞬」で消える

ユーザーは「ちゃんと購入できたかな?」とドキドキしながらボタンを押します。
その緊張感の中で「二重決済」という裏切りが発生すれば、もう二度とそのサイトは使われません。
「当たり前のことを、当たり前に処理する」。
決済システムにおける最低限の品質保証なのです。

執筆:決済システム監査チーム

年間流通額1,000億円規模のECサイト構築経験を持つエンジニア集団。
「1円のズレも許さない」堅牢なトランザクション設計で、貴社の信頼を守ります。