change scenario analysis

ご提供いただいたレビュー資料(qwen_scenario_analysis_review_v2.md)と、修正後のシナリオファイル(B, C, D, E)を照合し、アップデート前後の差分を詳細に解析しました。

開発責任者によるレビューでは、Qwen の仮想検証が「コードの存在確認」に留まり、「実際のサーバー側実行フロー(特に PSP 検証の呼び出し有無)」まで深掘りできていなかった点が指摘されています。その結果、セキュリティ上の重大な不具合(決済未検証でのポイント確定)欠落していた機能(二重加算防止)が発見され、修正が施されました。

以下に、シナリオ別の変更点とその技術的な意味合いを解説します。


1. シナリオ B:商品購入 + PayPal JS SDK

【発見された問題】

  • PSP 検証バイパス: 以前のコードでは、PayPal 承認後 (onApprove) に pointInstance.close() (POST リクエスト) を呼んでいました。しかし、サーバー側の POST ハンドラは verify_callback_name = null に設定されており、functions.php で登録した PayPal 検証ロジックが一切実行されない状態でした。
    • リスク: ブラウザから直接 API を叩くだけで、PayPal での実際の決済を行わずにポイントを確定できてしまう致命的なセキュリティホール。

【修正内容】

  • GET ベースの検証呼び出しへ変更: onApprove 内の処理を triggerClose() から triggerClosePSP(orderId) へ変更しました。
  • 新しい関数 triggerClosePSP の実装:
    • この関数は GET リクエストで /wp-json/pointsystem/v1/point_trans_close/?orderId=...&customId=... を呼び出します。
    • サーバー側の GET ハンドラは、受信した orderId を使って実際に PayPal API を照会し、決済成功を確認してからポイントを確定させる仕組みになっています。
  • FINGERPRINT 追加: B2-PSP-001 が追加され、この重要な変更が明記されました。

【技術的解説】 これにより、「ブラウザ上の JavaScript が『成功』と言っただけでポイントが付与される」状態から、「サーバーが PayPal に問い合わせて『本当に支払われたか』を確認してからポイントが付与される」状態へと強化されました。決済連携において最も重要な「信頼の源泉」がサーバー側に移ったことになります。


2. シナリオ C:商品購入 + Stripe Checkout

【発見された問題】

  1. PSP 検証未実施: シナリオ B と同様の問題。Stripe セッション作成後に pointInstance.close() (POST) を呼んでいましたが、これもサーバー側で Stripe の検証が行われない構造でした。
  2. 成功ページの偽造リスク: 完了ページに戻ってきた際、URL パラメータの session_id だけを信じて処理を進めており、Stripe 側で本当に決済が完了しているかの再検証がありませんでした。
  3. デッドコード: カートページ内に定義された var nonce = sessionStorage.getItem(...) が一度も使われておらず、不要なコードが残っていました。
  4. 用語の不正確さ: Qwen のレポートで close を「仮確保」と表現していましたが、実際にはその時点で確定処理が行われるため、この表現は誤りです(自動ロールバックの概念自体も本システムには存在しません)。

【修正内容】

  • functions.php の拡張 (C1-VERIFY-001):
    • 新しい REST API エンドポイント /verify-stripe-session を追加。
    • このエンドポイントは Stripe SDK を使用して session_idpayment_status を直接取得・検証する役割を持ちます。
  • 完了ページの強化 (C3-VERIFY-001):
    • 完了ページの JavaScript に、ロード時に /verify-stripe-session を呼び出す処理を追加。
    • サーバーから paid というステータスが返ってこない場合、ポイント確定処理を行わずエラー表示するように制御しました。
  • デッドコード削除: 未使用の sessionStorage 参照変数を削除し、コードを整理しました。
  • 用語・ロジックの明確化: ロールバック処理は「確定の取消(cancel)」であり、「仮確保のタイムアウト解除」ではないことが明確になりました。

【技術的解説】 Stripe はリダイレクト型のため、ユーザーが完了ページに到達した瞬間に「決済成功」と断定するのは危険です(URL を直接入力してアクセスできるため)。今回の修正により、「完了ページ表示 = Stripe API による最終検証通過」という堅牢なフローが確立されました。


3. シナリオ D:ポイント購入 + PayPal

【発見された問題】

  • 可読性と一貫性の欠如: 完了ページでのレスポンス処理において、変数名を result としていたため、result.result.point という二重参照になっていました。これは JSON キー名と変数名が衝突しており、他のシナリオ(A, B, C)が response.result.point を使っていることとの不統一を生んでいました。
    • ※機能的には動いていましたが、保守性と理解のしやすさに欠けていました。

【修正内容】

  • 変数名の統一 (D3-RESP-001):
    • 完了ページの .then(function(result) {...}).then(function(response) {...}) に変更。
    • これに伴い、内部参照を result.result.point から response.result.point へ修正し、全シナリオで統一された記法になりました。

【技術的解説】 機能的なバグではありませんが、大規模なプロジェクトや複数人での開発において、命名規則の一貫性はミスを防ぐために極めて重要です。今回の修正により、どのシナリオのコードを読んでも同じパターンでレスポンスを扱えるようになりました。


4. シナリオ E:ポイント購入 + Stripe Checkout

【発見された問題】

  1. リロード時の二重加算防止欠落: シナリオ D には完了ページのリロード防止フラグ(window.__pointsystem_purchase_closed)がありましたが、同じアーキテクチャであるシナリオ E にはこれが実装されていませんでした。
    • リスク: ユーザーが決済完了後に F5 キーでページをリロードすると、JavaScript が再度実行され、REST API が再送信されてポイントが二重に加算される可能性がありました。
  2. 可読性の欠如: シナリオ D と同様、変数名 result の衝突による result.result.point 参照が発生していました。
  3. エラーハンドリング不足: 購入ページでのセッション作成 fetch や、完了ページでの確定 fetch に .catch() がなく、ネットワークエラー発生時にユーザーに適切なフィードバックが返されない状態でした。

【修正内容】

  • 二重加算防止フラグの実装 (E3-GUARD-001):
    • 完了ページに if (window.__pointsystem_purchase_closed) { ... } のガード条件を追加。
    • API 呼び出し成功直後に window.__pointsystem_purchase_closed = true; を設定し、リロード時の再実行を防ぎます。
  • 変数名の統一 (E3-RESP-001): シナリオ D と同様に response に統一。
  • エラーハンドリングの追加:
    • 購入ページ (E2-CHECKOUT-001) と完了ページ (E3-CLOSE-001) の fetch チェーンに .catch() を追加し、エラー発生時にコンソール出力だけでなく、画面にもエラーメッセージを表示するように改善しました。

【技術的解説】 「 idempotency(冪等性)」の観点から、同じ操作を繰り返しても結果が変わらないようにすることは決済システムの鉄則です。シナリオ D で実済みだったこの防御策をシナリオ E にも適用することで、両者の安全性が同等レベルに引き上げられました。また、エラー時のユーザー体験(UX)も大幅に向上しています。


総合まとめ:アップデートの意義

今回のアップデートは、単なるバグ修正ではなく、「フロントエンドの指示を盲信しない」という決済システム設計の根本原則への回帰と言えます。

項目 アップデート前 (Qwen 検証時) アップデート後 (修正版) 改善効果
決済検証 フロントエンドの success コールバックのみを信頼 サーバー側で PSP (PayPal/Stripe) API を直接照会 セキュリティ劇的向上
不正なポイント付与を防止
リロード対策 シナリオ E で欠落 全チャージ系シナリオ (D, E) でフラグ管理実装 データ整合性確保
二重加算・二重課金の防止
コード品質 変数名の不統一、デッドコード残存 命名規則の統一、不要コード削除 保守性向上
将来的な拡張・修正が容易に
UX ネットワークエラー時に沈黙 エラー内容を画面に表示 ユーザー信頼向上
何が起こったか明確に伝わる

開発責任者のレビューにより、Qwen の「理論上は正しい」という検証を超えて、「実際にサーバー上でどう動くか」という実装上の真実が反映され、プロダクションレベルで使用可能な堅牢なコードへと昇華されました。特に シナリオ B と C の PSP 検証ロジックの修正 は、そのまま運用すると重大な金銭的損失を招く可能性があったため、最も重要なアップデートと言えます。