题干与适用场景
一个多商户支付平台希望在结账时让用户确认收款方、金额和币种,并生成可由银行或支付服务验证的密码学证据。团队考虑采用 W3C 2026 年 7 月 2 日发布的 Secure Payment Confirmation Candidate Recommendation Draft(SPC)。商户页面、支付编排服务和发卡行认证页可能来自不同来源,旧浏览器仍需继续使用现有认证方式。
请设计从 SPC 凭据注册到认证断言验证的完整系统,说明第三方如何代表依赖方发起认证、哪些数据必须由服务端绑定、如何处理 API 不可用、用户取消、重复支付和回滚。该规范仍是草案,不能把草案状态当成全浏览器已稳定支持。
面试官考察点
面试官看重候选人是否把“用户看到的交易明细”“服务端批准的订单”和“认证断言”绑定在同一笔交易上,能否解释 SPC 与 WebAuthn 的关系及跨来源调用的风险。
强回答还会覆盖 payment Permission Policy、securePaymentConfirmationAvailability() 的隐私限制、凭据隔离、幂等键、风控降级、证据留存和灰度开关,而不是只描述弹出一个生物识别对话框。
回答前需要澄清的问题
- SPC 凭据由谁注册,是否与登录凭据分离?
- 发卡行、商户和支付编排服务各自是什么来源,谁是 WebAuthn Relying Party?
- 认证数据是否包含金额、收款方、币种、订单号和过期时间?
- 不支持 SPC 或用户取消时,现有认证路径能否安全接管?
- 重试、重复回调、退款和争议处理需要保存哪些证据?
30 秒回答框架
“我会先把订单和认证交易建立不可变的服务端记录,再注册专用 SPC 凭据并限制调用来源。结账时由服务端生成一次性 challenge 和订单摘要,商户把已批准的支付数据传给 SPC,回调后由服务端验证来源、challenge、凭据、用户验证结果和订单状态。能力不可用或用户取消时走原有认证,但不能把‘没有 SPC’当成支付成功。所有状态用幂等键推进,先小流量验证失败率、争议率和回退率,异常时关闭 SPC 新请求并保留运行中交易证据。”
分步骤深入解答
先划分来源与凭据所有权
SPC 建立在 WebAuthn 之上,但允许第三方代表依赖方触发认证。平台应为支付凭据使用专用 Relying Party ID 或支付子域,避免登录凭据与支付凭据混用。注册接口只接受服务端签发的用户和商户绑定令牌,记录凭据 ID、来源、创建时间、撤销状态和设备绑定属性;绝不让浏览器提交任意 Relying Party ID 作为可信事实。
把订单状态绑定到 challenge
服务端创建 payment_attempt,保存订单版本、金额、币种、收款方、challenge、过期时间和幂等键。challenge 必须一次性消费,订单修改、币种转换或收款方变化都要生成新尝试。客户端展示的明细来自同一份服务端签名或摘要,不能让商户前端自行拼接金额后直接调用 SPC。
type PaymentAttempt = {
id: string;
orderVersion: number;
amountMinor: bigint;
currency: string;
payeeOrigin: string;
challenge: Uint8Array;
expiresAt: Date;
status: "created" | "authorizing" | "approved" | "cancelled" | "expired";
};设计跨来源调用边界
SPC 的关键变化是第三方可以使用另一依赖方的凭据发起认证,因此必须把 caller、Relying Party、订单和允许的认证来源写入服务端策略。嵌入支付 iframe 前配置 payment Permission Policy,并校验顶层页面与商户白名单。跨来源断言回到第三方后,第三方只能得到完成该笔交易所需的结果,不得借机请求任意 WebAuthn 扩展或读取登录凭据。
做能力探测与兼容回退
优先调用 PaymentRequest.securePaymentConfirmationAvailability(),把 available 与不可用原因分开记录;该接口可能返回模糊原因以降低指纹风险。能力可用仍不代表特定凭据存在,因此不能把探测结果当作授权。回退链可按风控等级选择 SPC、银行认证、一次性验证码或人工复核;每条路径都必须重新校验订单状态和 challenge,禁止“SPC 失败就直接标记成功”。
验证断言和交易语义
服务端验证客户端数据来源、challenge、Relying Party ID、签名、用户验证标志、凭据状态和订单摘要。支付服务收到断言后先以条件更新抢占 created 状态,再执行扣款;重复回调只返回同一结果。若金额或收款方摘要不匹配,标记安全失败并阻止重试放大攻击面。断言本身只证明用户完成了认证,不等同于资金已结算。
处理取消、超时和故障
用户取消、无可用认证器、浏览器关闭和银行超时都进入可区分的终态,客户端可以重试但必须生成新的 attempt。支付网关超时不能自动重放原 challenge;通过查询幂等键确认扣款结果,再决定展示待确认、成功或人工介入。支付服务、商户和发卡行之间的事件要带版本号,拒绝旧事件覆盖新状态。
观测风控与证据留存
按浏览器版本、来源组合、认证方式和风险分层统计 SPC 可用率、用户取消率、断言验证失败、重复回调、回退率、扣款延迟和争议率。日志保存订单摘要哈希、凭据 ID 的不可逆标识、attempt ID、策略版本和验证结果,不记录原始生物特征或私钥。对跨来源异常、相同凭据高频尝试和金额突变设置限流与二次复核。
灰度、回滚与安全测试
先在内部商户和低风险金额开启,随后按浏览器、来源和地区分层扩大。测试跨来源 iframe、缺失 Permission Policy、用户拒绝、凭据不存在、challenge 重放、订单修改、重复回调、网关超时和退款竞态。回滚时关闭新 attempt 的 SPC 创建,保留已进入认证的交易并让它们按状态机完成;新交易切回旧认证,不能删除断言和订单证据。
高质量示范回答
“SPC 是认证证据生成器,不是支付结算本身。我会用服务端 payment_attempt 把订单版本、金额、收款方、challenge、来源策略和幂等键绑定起来;支付凭据与登录凭据分离,并通过 Permission Policy 限制跨来源调用。认证后验证 WebAuthn 断言和订单摘要,再以条件更新推进扣款状态。能力不可用、取消或超时都走显式回退,重复回调只返回已确定结果。灰度阶段观测断言失败、回退、争议和跨来源异常;发现问题就停止新 SPC 请求,保留在途证据并将新交易切回旧路径。”
常见错误
- 把
available当成有凭据 → 用户仍可能无法认证 → 把能力探测和凭据可用分开处理。 - 让前端决定金额 → 用户确认内容与扣款金额不一致 → 由服务端订单摘要和 challenge 绑定。
- 把跨来源调用当成普通登录 → 可能泄露登录凭据或扩大扩展权限 → 隔离支付凭据并限制 caller。
- SPC 失败就标记支付成功 → 认证失败被误当成结算成功 → 认证、扣款、结算分别建状态。
- 重试复用 challenge → 断言可被重放 → 一次性消费并为新 attempt 生成新 challenge。
- 回滚删除全部记录 → 争议和重复扣款无法追溯 → 冻结新请求,保留在途状态与证据。
追问及应对
追问一:为什么不直接复用登录 passkey?
支付与登录的威胁模型、来源和授权目的不同。规范允许 WebAuthn 凭据参与 SPC,但平台仍可用支付子域和独立凭据降低登录攻击面;若复用,必须明确断言用途、Relying Party 和服务端校验边界。
追问二:如何避免商户把 1 元展示给用户却扣 100 元?
商户前端不能成为金额真源。支付服务根据订单版本生成摘要并传给认证流程,扣款服务只接受同一 attempt 的摘要和金额;断言验证通过后再次读取不可变订单,任何字段变化都要求新 attempt。
追问三:securePaymentConfirmationAvailability() 返回不可用原因有什么风险?
过细原因可能形成设备或配置指纹,因此用户代理可以返回 unavailable-unknown-reason。业务只把结果用于体验选择,不把原因作为用户画像、风控分数或授权依据。
追问四:支付服务超时后能否再次调用 SPC?
先按幂等键查询扣款和认证状态。若原 attempt 仍未终态,不能盲目重放;需要明确取消或过期后创建新 attempt,并重新生成 challenge 和订单摘要。