プロンプトとスコープ
あるマルチマーチャント決済プラットフォームは、購入者がチェックアウト時に受取人、金額、通貨を確認し、銀行や決済サービスが検証できる暗号化エビデンスを生成できるようにしたいと考えています。チームは2026年7月2日に公開されたW3C Secure Payment Confirmation Candidate Recommendation Draftの採用を検討しています。加盟店ページ、決済オーケストレーター、イシュアー認証ページはそれぞれ異なるオリジンを持つ場合があり、古いブラウザは既存の認証パスを維持する必要があります。
SPCクレデンシャルの登録からアサーションの検証までシステムを設計してください。サードパーティがリライングパーティに代わって認証を開始する方法、サーバーがバインドしなければならないフィールド、API利用不可、キャンセル、二重決済、およびロールバックへの対処方法を説明してください。この仕様はまだドラフト段階です。そのステータスをユニバーサルなブラウザサポートと見なしてはなりません。
面接官が評価するポイント
面接官は、ユーザーに表示されるトランザクション詳細、サーバーが承認した注文、および認証アサーションを1つの決済試行にバインドしているかどうかを確認します。SPCとWebAuthnの関係、およびクロスオリジン呼び出しのセキュリティ上の影響を説明する必要があります。
優れた回答では、単に生体認証ダイアログを説明するだけでなく、payment Permission Policy、securePaymentConfirmationAvailability()のプライバシー制限、クレデンシャルの分離、冪等性キー、リスクベースのフォールバック、エビデンスの保持、および可逆的なロールアウトについても網羅します。
最初に確認すべき明確化の質問
- SPCクレデンシャルを登録するのは誰ですか?また、それはログインクレデンシャルとは分離されていますか?
- イシュアー、加盟店、決済オーケストレーターのオリジンは何であり、どのパーティがWebAuthn Relying Partyですか?
- 認証データには金額、受取人、通貨、注文ID、有効期限が含まれている必要がありますか?
- SPCが利用できない、またはキャンセルされた場合、既存の認証パスへ安全に引き継ぐことができますか?
- リトライ、重複コールバック、返金、および異議申し立て(チャージバック)にはどのようなエビデンスが必要ですか?
30秒での回答
「認証前にサーバー側で不変の決済試行レコードを作成し、決済専用のSPCクレデンシャルを登録して、許可された呼び出し元オリジンを制限します。サーバーは使い捨てのチャレンジと注文ダイジェストを発行し、加盟店はサーバー承認済みの決済データのみをSPCに渡し、コールバックはオリジンポリシー、チャレンジ、クレデンシャル、ユーザー検証、および注文状態と照合されます。利用不可やキャンセル時は明示的なレガシーパスに従い、決して成功ショートカットにはしません。状態遷移には冪等性キーを使用します。低リスクのトラフィックから開始し、アサーション失敗、異議申し立て、フォールバックを測定し、異常が検出された場合はインフライトのエビデンスを保持しながら新規SPC試行を無効化します。」
ステップバイステップの解決策
オリジンとクレデンシャルの所有権を定義する
SPCはWebAuthnをベースにしていますが、サードパーティがリライングパーティのためにセレモニーをトリガーすることを許可します。ログインクレデンシャルと決済クレデンシャルが暗黙的に互換とならないよう、決済専用のRelying Party IDまたは決済サブドメインを使用します。登録時は、サーバーが発行したユーザーおよび加盟店のバインディングトークンのみを受け入れます。クレデンシャルID、オリジン、作成日時、失効状態、およびデバイスバインディングプロパティを保存し、ブラウザから提供されたRelying Party IDを認可情報として決して信用してはなりません。
注文状態をチャレンジにバインドする
注文バージョン、金額、通貨、受取人、チャレンジ、有効期限、および冪等性キーを含むpayment_attemptを作成します。チャレンジは1回のみ消費します。注文内容の編集、通貨換算、または受取人の変更が発生した場合は、新しい試行を作成します。ユーザーにレンダリングされる金額は、同一のサーバー承認済みレコードまたはダイジェストから取得する必要があります。加盟店がブラウザ上で値を連結して直接SPCを呼び出してはなりません。
type PaymentAttempt = {
id: string;
orderVersion: number;
amountMinor: bigint;
currency: string;
payeeOrigin: string;
challenge: Uint8Array;
expiresAt: Date;
status: "created" | "authorizing" | "approved" | "cancelled" | "expired";
};クロスオリジン境界を強制する
SPCの重要な変更点は、サードパーティが別のリライングパーティに属するクレデンシャルを使用できることです。呼び出し元、リライングパーティ、注文、および許可された認証オリジンをサーバーポリシーに設定します。決済iframeを埋め込む前にpayment Permission Policyを設定し、トップレベルオリジンを加盟店の許可リストと照合して検証します。クロスオリジンアサーションが返された後、呼び出し元はそのトランザクションに必要な結果のみを受け取ります。任意のWebAuthn拡張機能を要求したり、ログインクレデンシャルにアクセスしたりしてはなりません。
機能を検出しフォールバックを選択する
PaymentRequest.securePaymentConfirmationAvailability()を優先し、availableを利用不可の理由と分けて記録します。ユーザーエージェントはフィンガープリンティングを減らすために意図的に曖昧な理由を返すことがあります。利用可能であることは、特定のクレデンシャルが存在することを証明するものではありません。リスクベースのフォールバックにより、SPC、銀行認証、ワンタイムコード、または手動レビューを選択できます。すべてのパスで注文とチャレンジが再検証されるため、SPCのエラーが決済成功に化けることはありません。
アサーションと決済セマンティクスを検証する
サーバーは、クライアントデータのオリジン、チャレンジ、Relying Party ID、署名、ユーザー検証フラグ、クレデンシャル状態、および注文ダイジェストを検証します。請求(キャプチャ)を行う前に、条件付き更新を使用してcreated試行をクレーム(排他取得)します。重複するコールバックは同じ結果を返します。ダイジェストの不一致はセキュリティ違反であり、無制限にリトライしてはなりません。アサーションは認証が完了したことを証明するものであり、資金の決済(清算)が完了したことを証明するものではありません。
キャンセル、有効期限切れ、障害を処理する
キャンセル、認証器なし、ブラウザの終了、イシュアーのタイムアウトは、それぞれ個別の終端状態として扱います。リトライ時は新しい試行とチャレンジを作成します。ゲートウェイのタイムアウト時に古いチャレンジを再利用してはなりません。まず冪等性キーを照会し、結果が保留中か、成功したか、または人間によるレビューが必要かを判断します。古いイベントが新しい状態を上書きできないよう、決済サービス、加盟店、イシュアー間のイベントにはバージョンを付与します。
リスクを監視しエビデンスを保持する
SPCの可用性、ユーザーキャンセル、アサーション失敗、重複コールバック、フォールバック率、決済レイテンシ、および異議申し立てを、ブラウザ、オリジンの組み合わせ、認証方法、リスク層ごとにセグメント化します。注文ダイジェストのハッシュ、不可逆なクレデンシャル識別子、試行ID、ポリシーバージョン、および検証結果を保持します。生体認証情報や秘密鍵はログに記録しないでください。クロスオリジンの異常、繰り返されるクレデンシャル試行、急激な金額変更にはレート制限を設けます。
段階的展開、テスト、およびロールバック
社内加盟店や低リスクの金額から開始し、ブラウザ、オリジン、地域ごとに拡大します。クロスオリジンiframe、Permission Policyの欠落、ユーザーの拒絶、クレデンシャルの欠落、チャレンジのリプレイ、注文編集、重複コールバック、ゲートウェイタイムアウト、返金との競合状態をテストします。ロールバック時は、新規SPC試行の作成を停止し、インフライトのトランザクションはステートマシンを通じて完了させ、新規トランザクションはレガシーパスへルーティングします。異議申し立ての分析のためにアサーションと注文のエビデンスは保持します。
質の高い模範解答
「SPCは認証のエビデンスを生成するものであり、決済清算そのものではありません。不変のpayment_attemptを注文バージョン、金額、受取人、チャレンジ、オリジンポリシー、および冪等性キーにバインドします。決済クレデンシャルはログインクレデンシャルから分離し、Permission Policyによりクロスオリジンの呼び出し元を制限します。認証後、サーバーはWebAuthnアサーションと注文ダイジェストを検証し、条件付きで請求状態を進めます。利用不可、キャンセル、タイムアウトには明示的なフォールバックを使用し、重複コールバックには確定済みの結果を返します。段階的ロールアウト中はアサーション失敗、フォールバック、異議申し立て、クロスオリジンの異常を監視し、ロールバック時はインフライトのエビデンスを保持しながら新規SPC試行を停止します。」
よくある間違い
availableをクレデンシャルとして扱う → ユーザーが使用可能なクレデンシャルを保持していない可能性があります → 機能検出とクレデンシャルの有無を分離します。- ブラウザに金額を選択させる → 確認画面と請求額が乖離する可能性があります → サーバー側の注文ダイジェストとチャレンジをバインドします。
- クロスオリジン呼び出しを通常のログインとして扱う → ログインクレデンシャルや拡張データが漏洩するリスクがあります → 決済クレデンシャルを分離し、呼び出し元を制限します。
- SPCの失敗を決済成功に変えてしまう → 認証と決済清算が混同されています → 認証、請求、清算を個別にモデル化します。
- リトライ時にチャレンジを再利用する → アサーションがリプレイされる可能性があります → 1回のみ消費し、試行ごとに新しいチャレンジを発行します。
- ロールバック時にレコードを削除する → 異議申し立てや二重請求の追跡が不可能になります → 新規リクエストを凍結し、インフライトのエビデンスを保持します。
フォローアップの質問と回答
フォローアップ1:なぜログイン用のパスキーを再利用しないのですか?
決済とログインでは、脅威モデル、オリジン、および認可の目的が異なります。仕様上はSPCでWebAuthnクレデンシャルを使用できますが、決済サブドメインとクレデンシャルの分離によりログインへの攻撃対象領域を減らすことができます。クレデンシャルを共有する場合、アサーションの目的、Relying Party、およびサーバー側のチェックを明示的にする必要があります。
フォローアップ2:加盟店が1ドルと表示して100ドル請求するのをどう防ぎますか?
加盟店のUIは金額の決定権を持ちません。決済サービスが注文バージョンからダイジェストを作成し、認証フローがそのダイジェストを伝送し、請求サービスは同一の試行のみを受け入れます。フィールドが変更されると新しい試行が作成されます。
フォローアップ3:詳細な可用性の理由を返すことのリスクは何ですか?
詳細な理由はデバイスや構成のフィンガープリントになり得るため、ユーザーエージェントはunavailable-unknown-reasonを返すことがあります。ビジネス側はこの結果をユーザー体験の選択にのみ使用し、ユーザープロファイル、リスクスコア、または認可の事実として使用してはなりません。
フォローアップ4:タイムアウト後に決済サービスがSPCを再呼び出しすることはできますか?
まず冪等性キーによって請求および認証の状態を照会します。以前の試行が未解決の場合は、盲目的に再試行してはなりません。明示的にキャンセルまたは失効させた後、新しいチャレンジと注文ダイジェストを使用して新しい試行を作成します。