商家可以将安全付款确认 (SPC) 作为针对给定信用卡或银行账户的增强型客户身份验证 (SCA) 流程的一部分。WebAuthn 执行身份验证(通常通过生物识别)。您必须预先注册 WebAuthn,如需了解详情,请参阅注册安全付款确认。
典型实现的工作原理
SPC 最常见的用途是,当客户在商家网站上购物时,信用卡发卡机构或银行要求进行付款人身份验证。
我们来详细了解一下身份验证流程:
- 客户向商家提供其付款凭据(例如信用卡信息)。
- 如果付款人需要单独进行身份验证,商家会询问付款凭据的相应发卡机构或银行(依赖方或 RP)。例如,在 EMV® 3-D Secure 中就可能会发生这种交换。
- 如果 RP 希望商家使用 SPC,并且用户之前已注册,则 RP 会响应付款人注册的凭据 ID 列表和质询。
- 如果不需要进行身份验证,商家可以继续完成交易。
- 如果需要进行身份验证,商家会确定浏览器是否支持 SPC。
- 如果浏览器不支持 SPC,请继续使用现有身份验证流程。
- 商家调用 SPC。浏览器会显示一个确认对话框。
- 如果 RP 未传递任何凭据 ID,则回退到现有身份验证流程。成功完成身份验证后,不妨考虑使用 SPC 注册来简化日后的身份验证。
- 用户解锁设备,确认并验证付款金额和收款人。
- 商家会从身份验证中收到凭据。
- RP 从商家接收凭据并验证其真实性。
- RP 会将验证结果发送给商家。
- 商家会向用户显示一条消息,指明付款是成功还是失败。
功能检测
如需检测浏览器是否支持 SPC,您可以向 canMakePayment()
发送虚假调用。
复制并粘贴以下代码,以便在商家网站上检测 SPC 功能。
const isSecurePaymentConfirmationSupported = async () => { if (!'PaymentRequest' in window) { return [false, 'Payment Request API is not supported']; } try { // The data below is the minimum required to create the request and // check if a payment can be made. const supportedInstruments = [ { supportedMethods: "secure-payment-confirmation", data: { // RP's hostname as its ID rpId: 'rp.example', // A dummy credential ID credentialIds: [new Uint8Array(1)], // A dummy challenge challenge: new Uint8Array(1), instrument: { // Non-empty display name string displayName: ' ', // Transparent-black pixel. icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==', }, // A dummy merchant origin payeeOrigin: 'https://non-existent.example', } } ]; const details = { // Dummy shopping details total: {label: 'Total', amount: {currency: 'USD', value: '0'}}, }; const request = new PaymentRequest(supportedInstruments, details); const canMakePayment = await request.canMakePayment(); return [canMakePayment, canMakePayment ? '' : 'SPC is not available']; } catch (error) { console.error(error); return [false, error.message]; } }; isSecurePaymentConfirmationSupported().then(result => { const [isSecurePaymentConfirmationSupported, reason] = result; if (isSecurePaymentConfirmationSupported) { // Display the payment button that invokes SPC. } else { // Fallback to the legacy authentication method. } });
对用户进行身份验证
如需对用户进行身份验证,请使用 secure-payment-confirmation
和 WebAuthn 参数调用 PaymentRequest.show()
方法:
PublicKeyCredentialRequestOptions
- 商家平台上的其他付款专用参数。
以下是您应向付款方式的 data
属性 SecurePaymentConfirmationRequest
提供的参数。
请查看以下示例代码:
// After confirming SPC is available on this browser via a feature detection, // fetch the request options cross-origin from the RP server. const options = fetchFromServer('https://rp.example/spc-auth-request'); const { credentialIds, challenge } = options; const request = new PaymentRequest([{ // Specify `secure-payment-confirmation` as payment method. supportedMethods: "secure-payment-confirmation", data: { // The RP ID rpId: 'rp.example', // List of credential IDs obtained from the RP server. credentialIds, // The challenge is also obtained from the RP server. challenge, // A display name and an icon that represent the payment instrument. instrument: { displayName: "Fancy Card ****1234", icon: "https://rp.example/card-art.png", iconMustBeShown: false }, // The origin of the payee (merchant) payeeOrigin: "https://merchant.example", // The number of milliseconds to timeout. timeout: 360000, // 6 minutes } }], { // Payment details. total: { label: "Total", amount: { currency: "USD", value: "5.00", }, }, }); try { const response = await request.show(); // response.details is a PublicKeyCredential, with a clientDataJSON that // contains the transaction data for verification by the issuing bank. // Make sure to serialize the binary part of the credential before // transferring to the server. const result = fetchFromServer('https://rp.example/spc-auth-response', response.details); if (result.success) { await response.complete('success'); } else { await response.complete('fail'); } } catch (err) { // SPC cannot be used; merchant should fallback to traditional flows console.error(err); }
.show()
函数会生成 PaymentResponse
对象,但 details
包含一个包含公钥凭据的 clientDataJSON
,其中包含要由 RP 进行验证的事务数据 (payment
)。
生成的凭据必须跨源传输到 RP 并进行验证。
RP 如何验证交易
在 RP 服务器上验证交易数据是付款流程中最重要的一步。
如需验证交易数据,RP 可以遵循 WebAuthn 的身份验证断言验证流程。此外,他们还需要验证 payment
。
clientDataJSON
的示例载荷:
{ "type":"payment.get", "challenge":"SAxYy64IvwWpoqpr8JV1CVLHDNLKXlxbtPv4Xg3cnoc", "origin":"https://spc-merchant.glitch.me", "crossOrigin":false, "payment":{ "rp":"spc-rp.glitch.me", "topOrigin":"https://spc-merchant.glitch.me", "payeeOrigin":"https://spc-merchant.glitch.me", "total":{ "value":"15.00", "currency":"USD" }, "instrument":{ "icon":"https://cdn.glitch.me/94838ffe-241b-4a67-a9e0-290bfe34c351%2Fbank.png?v=1639111444422", "displayName":"Fancy Card 825809751248" } } }
rp
与 RP 的来源匹配。topOrigin
与 RP 预期的顶级来源(上例中的商家来源)匹配。payeeOrigin
与应向用户显示的收款人来源一致。total
与应向用户显示的交易金额一致。instrument
与应向用户显示的付款工具详细信息相匹配。
const clientData = base64url.decode(response.clientDataJSON); const clientDataJSON = JSON.parse(clientData); if (!clientDataJSON.payment) { throw 'The credential does not contain payment payload.'; } const payment = clientDataJSON.payment; if (payment.rp !== expectedRPID || payment.topOrigin !== expectedOrigin || payment.payeeOrigin !== expectedOrigin || payment.total.value !== '15.00' || payment.total.currency !== 'USD') { throw 'Malformed payment information.'; }
通过所有验证条件后,RP 可以告知商家交易已成功。
后续步骤
- 阅读安全付款确认概览
- 了解如何注册使用安全付款确认