CONTENT
ここから
第21章では、決済システムのセキュリティについて学びました。
カード情報を自社サーバーに持たないこと、Access Tokenを守ること、Webhook署名を検証すること、金額改ざんを防ぐことが重要でした。
この章では、実際のEC運用で起きるエラー処理と障害対応を整理します。
ECでは、どれだけ丁寧に作ってもエラーは起きます。
大切なのは、エラーをゼロにすることではありません。
エラーが起きても、注文状況を追えるようにしておくことです。
エラーが起きる
↓
何が起きたか分かる
↓
どこまで処理されたか分かる
↓
管理者が確認できる
↓
必要なら手動対応できる
SquareのWebhookは同じ通知が複数回届く可能性があり、Squareは重複処理を避けるためにevent_idを使うことを推奨しています。また、Webhook受信後はできるだけ早く2xxで応答し、処理が重い場合は内部処理を分ける設計が安全です。(Square)
攻略本風に言えば、第22章は**「トラブルが起きても全滅しないための回復ステージ」**です。
22.1 ECで起きる代表的なエラー
まず、ECで起きやすいエラーを整理します。
エラーには、ユーザー側で見えるものと、裏側で起きるものがあります。
ユーザー側で見えるエラーです。
・購入ボタンを押しても決済画面へ進めない
・決済後に完了ページが表示されない
・カートの商品数がおかしい
・売り切れ商品を購入しようとしてしまう
・メールが届かない
裏側で起きるエラーです。
・Square API呼び出しに失敗する
・決済リンク作成に失敗する
・Webhookが届かない
・Webhookが重複して届く
・在庫更新に失敗する
・注文ステータス更新に失敗する
・メール送信に失敗する
・管理者通知に失敗する
ECで特に注意すべきなのは、決済後のエラーです。
なぜなら、購入者がお金を払った可能性があるからです。
決済前のエラー:
購入できないだけで済むことが多い
決済後のエラー:
お金は動いたが、注文処理が止まる可能性がある
決済後のエラーでは、すぐにキャンセル扱いにしてはいけません。
まず、Square側の決済状況と自前DBの注文状況を確認します。
Squareでは決済完了
自前DBではpendingのまま
この場合:
注文を失敗扱いにせず、確認対象にする
エラー対応の基本は、次の3つです。
1. 何が起きたか記録する
2. どこまで処理できたか記録する
3. 自動で無理なら手動確認に回す
ECでは、完璧に自動化するより、確認できる状態を作ることが大切です。
22.2 決済リンク作成失敗
Square Checkout APIを使う場合、購入ボタンを押したあとに決済リンクを作成します。
SquareのCreate payment link APIは、Squareがホストするチェックアウトページを作成するAPIです。利用には注文や決済に関する権限が必要です。(Square)
決済リンク作成の流れは、次の通りです。
購入ボタンを押す
↓
サーバー側で商品・在庫・金額を確認する
↓
Squareへ決済リンク作成リクエストを送る
↓
決済URLを受け取る
↓
ユーザーをSquare決済画面へ移動する
この途中で失敗することがあります。
代表的な原因です。
・Access Tokenが間違っている
・Location IDが間違っている
・SquareのSandboxと本番が混ざっている
・注文金額が不正
・商品データが見つからない
・在庫が足りない
・Square APIが一時的に失敗している
・ネットワークエラー
決済リンク作成に失敗した場合、購入者には分かりやすく表示します。
購入手続きへ進めませんでした。
恐れ入りますが、時間をおいて再度お試しください。
開発者向けのエラー内容を、そのまま画面に出してはいけません。
悪い例:
SQUARE_ACCESS_TOKEN is invalid
よい例:
決済画面を準備できませんでした。
管理者向けには、ログを残します。
error_type: create_payment_link_failed
order_id: ord_xxxxx
product_ids: product_xxxxx
reason: square_api_error
created_at: 2026-07-06 16:30
購入者にはやさしく、管理者には原因を追える情報を残します。
購入者向け:
分かりやすい案内
管理者向け:
調査できるログ
決済リンク作成失敗では、まだ決済は完了していません。
そのため、注文ステータスはpendingまたはfailedとして扱います。
決済リンク作成前に失敗
→ 決済は発生していない
→ 注文はfailedまたはpending
ただし、同じ注文で何度も決済リンクを作る場合は、二重作成に注意します。
Square APIでは、同じ処理を安全に再試行するためにidempotency_keyを使う設計が案内されています。同じ冪等性キーで再試行すると、重複作成を避けやすくなります。(Square)
同じ注文ID
↓
同じidempotency_key
↓
二重作成を防ぐ
決済リンク作成は、購入導線の入口です。
失敗したときに、購入者が不安にならない表示と、管理者が追えるログを用意します。
22.3 決済完了後にWebhookが届かない
ECで怖いのは、決済は完了したのにWebhookが届かない、または処理できないケースです。
ユーザーがSquareで支払う
↓
決済は完了
↓
しかしWebhookが届かない
↓
自前DBはpendingのまま
この状態を放置すると、店舗側は注文に気づけません。
購入者はお金を払ったのに、注文完了メールが届かない可能性があります。
Webhookが届かない原因には、次のようなものがあります。
・Webhook URLが間違っている
・本番とSandboxのURLを間違えている
・Webhookのイベント種別が設定されていない
・サーバーがエラーを返している
・署名検証に失敗して処理を止めている
・処理に時間がかかりすぎている
・ネットワーク障害
まず確認するのは、Square側のWebhookログです。
SquareのWebhook管理では、通知URLや購読イベントをDeveloper Console上で設定します。Webhookの運用では、受信側が2xxで応答できているか、重複イベントをevent_idで処理済み判定しているかが重要です。(Square)
自前DB側では、pendingのまま一定時間経過した注文を抽出します。
pendingのまま10分以上
pendingのまま30分以上
pendingのまま1時間以上
このような注文は、手動確認対象にします。
注文ステータス:
manual_review
理由:
決済完了Webhook未確認
管理画面では、次のように表示します。
手動確認が必要です。
理由:
決済リンク作成後、Webhookが未確認です。
確認項目:
Square DashboardでPayment IDを確認してください。
Webhookが届かない場合の基本フローです。
pending注文を検出
↓
Square Dashboardで決済状況を確認
↓
決済完了ならpaidへ変更
↓
在庫・メール・通知を手動または再実行
↓
対応履歴を残す
ここで大切なのは、自動で勝手にpaidにしないことです。
Square側で決済完了が確認できてから進めます。
22.4 在庫更新失敗
決済完了後に、在庫を減らす処理が失敗することがあります。
決済完了
↓
注文をpaidへ変更
↓
在庫を減らす
↓
在庫更新に失敗
在庫更新失敗の原因は、次のようなものです。
・在庫が足りない
・同時購入が発生した
・DB更新に失敗した
・商品IDが見つからない
・在庫数が不正な値になっている
・在庫管理対象外の商品だった
特に注意すべきなのは、同時購入です。
在庫:1個
Aさんが購入
Bさんもほぼ同時に購入
両方とも決済完了
↓
在庫が足りない
在庫更新では、DB側で安全に条件付き更新を行います。
UPDATE products
SET stock = stock - 1
WHERE id = 'product_id'
AND stock >= 1;
更新件数が0件の場合は、在庫不足と判断します。
更新成功:
在庫を減らせた
更新0件:
在庫不足または商品なし
在庫更新に失敗した場合、注文はすぐにキャンセルしません。
なぜなら、決済は完了している可能性があるからです。
決済は完了
在庫更新は失敗
この場合:
manual_reviewにする
管理者が確認すべき内容です。
・Squareで決済は完了しているか
・商品在庫は本当に足りないか
・代替商品を案内できるか
・返金が必要か
・購入者へ連絡が必要か
購入者への連絡文例です。
このたびはご注文いただき、誠にありがとうございます。
ご注文内容を確認したところ、在庫状況の確認が必要な状態となっております。
確認のうえ、改めてご案内いたします。
ご不安をおかけし申し訳ございません。
在庫更新失敗では、スピードよりも正確さが大切です。
自動で無理に処理せず、手動確認へ回します。
22.5 メール送信失敗
注文完了メールや発送完了メールは、購入者の安心につながります。
しかし、メール送信も失敗することがあります。
・メールアドレスが間違っている
・メールAPIの障害
・APIキー設定ミス
・送信ドメイン設定ミス
・迷惑メール判定
・一時的な通信エラー
ここで大切なのは、メール送信失敗と注文失敗を分けることです。
決済成功
注文paid
メール送信失敗
この場合:
注文はpaidのまま
メールだけfailed
メールステータスを分けて管理します。
customer_email_status:
pending
sent
failed
admin_email_status:
pending
sent
failed
TypeScriptで考えると、次のようになります。
type EmailStatus = 'pending' | 'sent' | 'failed';
type OrderEmailState = {
orderId: string;
customerEmailStatus: EmailStatus;
adminEmailStatus: EmailStatus;
};
/**
* 役割: 注文に関するメール送信状況を更新する
* 入力: 注文ID、顧客向けメール状況、管理者向けメール状況
* 出力: 更新が成功したかどうか
*/
async function updateOrderEmailState(
input: OrderEmailState
): Promise<boolean> {
// 実際の実装では、DBにメール送信状態を保存する
return true;
}
メール送信失敗時には、管理画面から再送できるようにします。
注文詳細
↓
メール送信状況:失敗
↓
[注文完了メールを再送する]
再送時には、確認を入れます。
この注文完了メールを再送しますか?
すでに購入者へ届いている可能性があります。
再送履歴も残します。
2026-07-06 16:30 自動送信 failed
2026-07-06 16:45 管理者が再送 sent
メール送信失敗は、ECではよくあるトラブルです。
注文処理と切り離して管理することで、落ち着いて対応できます。
22.6 二重注文
二重注文とは、同じ内容の注文が2回作られてしまうことです。
原因はいくつかあります。
・購入ボタンを連打した
・通信が遅くて再送された
・戻るボタンで再度購入した
・Webhookが重複して届いた
・決済リンク作成を複数回実行した
・管理者が手動処理を重複した
二重注文で怖いのは、次のような状態です。
同じ商品を2回決済
在庫を2回減らす
メールを2回送る
発送を2回する
二重注文を防ぐには、冪等性を意識します。
冪等性とは、同じ処理が複数回実行されても、結果が1回分になるようにする考え方です。
SquareのAPIでは、同じ作成処理を安全に再試行するためにidempotency_keyを使うパターンが説明されています。たとえば、同じidempotency_keyで再度リクエストした場合、重複した決済や注文作成を避ける設計にできます。(Square)
自前DBでも、同じ考え方を使います。
同じ注文ID
同じSquare Payment ID
同じWebhook event_id
これらは一度だけ処理する
Webhookでは、event_idを保存して、処理済みならスキップします。
Webhook受信
↓
event_idを確認
↓
処理済みならスキップ
↓
未処理なら実行
↓
event_idを保存
注文処理でも、Square Payment IDに一意制約を付けると安全です。
square_payment_id:
同じ値を複数注文に紐づけない
二重注文が疑われる場合は、手動確認に回します。
同じメールアドレス
同じ商品
同じ金額
短時間に複数注文
↓
manual_review
二重注文は、完全にゼロにはできません。
しかし、冪等性、処理済み判定、一意制約、手動確認でかなり防げます。
22.7 ユーザーの戻るボタン問題
ECでは、ユーザーがブラウザの戻るボタンを押すことがあります。
これは普通の行動です。
しかし、決済や注文処理では注意が必要です。
購入ボタンを押す
↓
決済画面へ進む
↓
戻るボタンを押す
↓
もう一度購入ボタンを押す
このとき、注文が二重に作られる可能性があります。
また、決済完了後に戻るボタンを押して、古いカート画面に戻ることもあります。
決済完了
↓
完了ページ
↓
戻る
↓
カート画面
↓
また購入できてしまう
この問題を防ぐために、注文ステータスを確認します。
注文がpaid
→ 再購入処理を止める
注文がpending
→ 決済状況を確認する
注文がfailed
→ 再試行を許可する
購入ボタンを押したあとには、同じ注文で何度も決済リンクを作らないようにします。
注文IDを作る
↓
その注文IDに決済リンクを紐づける
↓
再度押された場合は既存リンクを使うか、状態を確認する
カートも、決済完了後に空にします。
Webhookでpaid確認
↓
注文完了
↓
カートを空にする
ただし、Webhookと完了ページのタイミングがズレる場合があります。
そのため、完了ページでは次のように表示を分けます。
pending:
決済情報を確認しています
paid:
ご注文が完了しました
manual_review:
ご注文内容を確認しています
failed:
決済を確認できませんでした
戻るボタンは防げません。
防ぐのではなく、戻っても壊れない設計にします。
ユーザーは戻る。
ユーザーは連打する。
ユーザーは途中で閉じる。
それでも壊れないように作る。
これが実務的なEC設計です。
22.8 管理者が確認すべきログ
エラーが起きたとき、管理者が確認できるログが必要です。
ログがないと、何が起きたのか分かりません。
注文がpaidになっていない
↓
なぜか分からない
↓
SquareなのかDBなのかメールなのか分からない
↓
対応できない
ECで残すべきログは、次の通りです。
・注文作成ログ
・決済リンク作成ログ
・Webhook受信ログ
・Webhook処理ログ
・在庫更新ログ
・メール送信ログ
・管理者操作ログ
・エラーログ
Webhookログでは、最低限次の情報を残します。
・event_id
・event_type
・Square Payment ID
・Square Order ID
・自前注文ID
・処理結果
・受信日時
ただし、ログに秘密情報を残してはいけません。
第21章で扱った通り、Access Token、Webhook署名キー、カード情報、パスワード、セッションTokenなどはログに出してはいけません。
保存してよい:
event_id
order_id
result
error_type
保存してはいけない:
Access Token
署名キー
カード情報
パスワード
ログの例です。
2026-07-06 16:30
type: webhook_received
event_id: evt_xxxxx
event_type: payment.updated
order_id: ord_xxxxx
result: success
エラーログの例です。
2026-07-06 16:31
type: stock_update_failed
order_id: ord_xxxxx
product_id: product_xxxxx
reason: not_enough_stock
result: manual_review
管理者操作ログも重要です。
2026-07-06 16:45
admin@example.com
注文ステータスをmanual_reviewからpaidへ変更
理由:Square Dashboardで決済完了を確認
ログは、システムの記憶です。
障害対応では、この記憶を見ながら復旧します。
22.9 復旧手順を決めておく
障害が起きたときに、その場で考えると混乱します。
そのため、事前に復旧手順を決めておきます。
復旧手順とは、トラブルが起きたときに何を確認し、どの順番で対応するかをまとめたものです。
エラー発生
↓
ログ確認
↓
Square確認
↓
自前DB確認
↓
注文ステータス修正
↓
購入者へ連絡
↓
対応履歴を残す
代表的な復旧手順を用意します。
決済完了後に注文がpendingのままの場合です。
1. 注文IDを確認する
2. Square DashboardでPayment IDを確認する
3. 決済ステータスがCOMPLETEDか確認する
4. Webhookログを確認する
5. 自前DBの注文ステータスを確認する
6. 問題なければpaidへ変更する
7. 在庫更新を実行する
8. 注文完了メールを送る
9. 対応履歴を残す
在庫更新に失敗した場合です。
1. 注文内容を確認する
2. 商品在庫を確認する
3. Squareで決済状況を確認する
4. 代替商品を用意できるか確認する
5. 購入者へ連絡する
6. 返金または代替対応を決める
7. 対応履歴を残す
メール送信失敗の場合です。
1. 注文ステータスを確認する
2. メールアドレスを確認する
3. メールAPIの送信ログを確認する
4. 必要なら管理画面から再送する
5. 再送履歴を残す
復旧手順は、管理画面や社内ドキュメントに置いておきます。
障害対応マニュアル
↓
管理者が見る
↓
順番に確認する
↓
対応漏れを防ぐ
復旧手順があるだけで、トラブル時の不安はかなり減ります。
22.10 手動対応フローを用意する
最後に、手動対応フローを用意します。
ECでは、すべてを自動で処理しようとすると危険です。
判断が難しい注文は、人が確認できるようにします。
自動処理できる注文
→ 通常フロー
判断が必要な注文
→ manual_review
手動対応に回すべきケースは、次の通りです。
・Squareでは決済完了だが、自前DBがpending
・Webhookが届いていない
・在庫更新に失敗した
・二重注文の可能性がある
・金額が一致しない
・配送先情報が不足している
・メール送信に何度も失敗している
・管理者操作で矛盾が起きた
手動対応フローは、次のようにします。
manual_review注文を確認
↓
Squareの決済状況を確認
↓
自前DBの注文内容を確認
↓
在庫・配送・メール状況を確認
↓
対応方針を決める
↓
購入者へ連絡
↓
ステータスを更新
↓
対応メモを残す
管理画面には、手動確認用の一覧を用意します。
手動確認が必要な注文
ORDER-0008
理由:在庫更新失敗
ORDER-0009
理由:Webhook未確認
ORDER-0010
理由:二重注文の可能性
注文詳細には、対応メモを残せるようにします。
対応メモ:
2026-07-06 17:00
Square Dashboardで決済完了を確認。
在庫不足のため、購入者へ代替商品を案内。
返信待ち。
手動対応で大切なのは、対応者の記憶に頼らないことです。
誰が
いつ
何を確認し
どう対応したか
これを残します。
また、購入者への連絡テンプレートも用意します。
ご注文内容を確認しております。
このたびはご注文いただき、誠にありがとうございます。
現在、ご注文内容について確認が必要な状態となっております。
確認が完了次第、改めてご案内いたします。
ご不安をおかけし申し訳ございません。
手動対応フローは、ECの最後の安全網です。
自動処理で迷ったら、無理に進めず、人が確認できる状態にします。
第22章のまとめ
ECでは、エラーを完全になくすことはできません。
大切なのは、エラーが起きても注文状況を追えること、復旧できること、購入者へ誠実に案内できることです。
この章で覚えておきたいポイントは、次の通りです。
1. ECでは決済前より決済後のエラーに注意する
2. 決済リンク作成失敗では、購入者には分かりやすく表示する
3. Webhookが届かない場合は、pending注文を手動確認に回す
4. 在庫更新失敗では、決済済み注文をすぐキャンセルしない
5. メール送信失敗と注文失敗は分けて考える
6. 二重注文を防ぐには冪等性と処理済み判定が重要
7. 戻るボタンや連打があっても壊れない設計にする
8. 管理者が確認できるログを残す
9. 障害時の復旧手順を事前に決めておく
10. 自動で判断できない注文はmanual_reviewに回す
エラー対応の基本は、次の一言にまとめられます。
止まらないことより、追えること。
トラブルが起きても、どこまで処理されたか分かる。
管理者が確認できる。
必要なら手動で復旧できる。
この状態を作っておけば、小規模ECでも安心して運用できます。
第23章では、本番公開前に必ず行うべきテスト戦略を整理していきます。