使用安全支付确认

通过支付请求 API (Payment Request API) 可用的安全支付确认 (SPC) 提供了一种在结账过程中进行强客户身份验证的机制,从而有效防止在线支付欺诈。

概述

为了防止在线支付欺诈,通常需要对账户持有人进行身份验证。强身份验证可以降低欺诈风险,但也会增加结账过程中出现的阻碍导致购物车被放弃的可能性。因此,银行、商家、支付服务提供商以及支付生态系统中的其他实体在决定每次交易应使用何种类型和强度的身份验证时,会考虑多种因素,包括交易金额、购买的商品、用户的支付历史、欺诈发生时的责任方以及法规要求(例如 欧洲支付服务指令 2 关于强客户身份验证和用户同意证明的要求)。

目前有多种机制结合使用以实现强身份验证,包括密码、一次性短信验证码、移动应用程序和 Web 身份验证。每种方法都有其优缺点。例如,一次性短信验证码对用户来说已相当熟悉,但可能存在可用性问题(如设备不可用)和安全漏洞。Web 身份验证提供了更好的安全性,并且可在所有主流浏览器、所有现代移动设备和计算机上使用。然而,单独的 Web 身份验证并不能提供用户同意进行支付的证明。

SPC 的设计旨在实现各种支付系统中简化的强客户身份验证 (SCA),并提供加密证据证明用户已同意交易条款。当调用该 API 时,浏览器会在对话框中显示交易的要素:商户名称、支付工具、支付金额和货币。例如,这是 Chrome 浏览器(M118 版本)中 SPC 的交易对话框。

Chrome M118 transaction dialog for SPC

选择“验证”将启动 Web 身份验证流程。当用户成功通过身份验证后(例如,使用其手机或笔记本电脑上的生物识别身份验证器),浏览器会将对话框中显示的数据传递给身份验证器,身份验证器将对这些数据进行签名,并将其作为最终的 Web 身份验证断言的一部分返回。然后,该断言可以传递给依赖方进行验证。由于浏览器直接将显示的数据传递给身份验证器(没有任何 JavaScript 代码可以修改这些数据),因此依赖方可以高度确信用户已同意显示在对话框中的交易数据。

因此,SPC 在 Web 身份验证的基础上,使网站能够执行简化的强身份验证并提供用户同意的证明。SPC 通常将作为给定支付系统的身份验证框架的一部分使用。例如,SPC 同时支持 EMV® 3-D Secure(2.3.1 版本)和 EMV® Secure Remote Commerce(1.3 版本),并且被设计用于支持各种支付类型,包括“推送支付”,如直接信用转账和钱包支付。

支付请求方法

安全支付确认利用了支付请求 API 的底层功能。安全支付确认支付处理程序的标准化支付方法标识符是 "secure-payment-confirmation"

Web 身份验证扩展

安全支付确认定义了一个 Web 身份验证扩展,称为 payment,它在传统 Web 身份验证的基础上增加了三个支付特定的功能。

  1. 当依赖方选择启用时,它允许除依赖方以外的实体使用依赖方的凭据启动支付身份验证流程。SPC 将身份验证流程与身份验证结果的验证分离开来。这使得商家(或其支付服务提供商在跨域 iframe 中)能够控制身份验证的用户体验,而无需将用户(通过重定向)转发到另一个网站或移动应用程序。例如,如果依赖方是银行,这就可以让商家管理身份验证的用户体验,同时银行仍然可以验证身份验证的结果。各方之间(凭据和身份验证结果)的通信通常通过特定于支付系统的渠道进行,例如 EMV® 3-D Secure。
  2. 强制要求用户代理正确地向用户传达他们正在进行交易身份验证以及交易的详细信息。这些详细信息随后将包含在身份验证器签名的断言中。
  3. 允许在跨域 iframe 中调用 navigator.credentials.create,前提是 iframe 上设置了“payment”权限策略。注意:此功能现在是 WebAuthn Level 3 的一部分,它使用“publickey-credential-create”权限策略。建议开发者在可用时使用后者,而不是依赖 SPC 的“payment”权限。

示例

创建凭据

在安全支付确认中创建凭据的操作与 Web 身份验证的 navigator.credentials.create() 调用相同,但需要指定 payment 扩展。

js
const publicKey = {
  challenge: Uint8Array.from(randomStringFromServer, (c) => c.charCodeAt(0)),
  rp: {
    name: "Fancy Bank",
  },
  user: {
    // Assuming that userId is ASCII-only
    id: Uint8Array.from(userId, (c) => c.charCodeAt(0)),
    name: "jane.doe@example.org",
    displayName: "Jane Doe",
  },
  pubKeyCredParams: [
    {
      type: "public-key",
      alg: -7, // "ES256"
    },
    {
      type: "public-key",
      alg: -257, // "RS256"
    },
  ],
  authenticatorSelection: {
    userVerification: "required",
    residentKey: "required",
    authenticatorAttachment: "platform",
  },
  timeout: 60000, // 1 minute
  extensions: {
    payment: {
      isPayment: true,
    },
  },
};
navigator.credentials
  .create({ publicKey })
  .then((newCredentialInfo) => {
    // Send new credential info to server for verification and registration.
  })
  .catch((err) => {
    // No acceptable authenticator or user refused consent. Handle appropriately.
  });

在跨域 iframe 中创建凭据

SPC 允许在跨域 iframe 中创建凭据(例如,如果 merchant.com 嵌入了来自 bank.com 的 iframe)。

在此流程中,作为交易的一部分,依赖方(例如银行)通过除 SPC 以外的某种机制(例如,使用一次性密码或其他机制)对账户持有人进行身份验证。然后,依赖方向用户提供注册 SPC 凭据以简化未来交易的选项。用户向依赖方注册 SPC 凭据。为了使这些步骤能在商家上下文中进行(即,无需重定向),跨域 iframe 必须设置 payment 权限策略。

例如

html
<!-- Assume parent origin is merchant.com -->
<!-- Inside this cross-origin iframe, script would be allowed to create a SPC credential for example.org -->
<iframe src="https://example.org" allow="payment"></iframe>

验证支付

某个源可以调用带有 "secure-payment-confirmation" 支付方法的支付请求 API,以提示用户验证由任何其他源创建的安全支付确认凭据。浏览器将显示一个原生用户界面(“交易对话框”),其中包含交易详情(例如,支付货币和金额以及收款方源)。

注意:根据支付请求 API 的规定,如果在跨域 iframe 中使用 PaymentRequest(例如,如果 merchant.com 嵌入了来自 psp.com 的 iframe,而 psp.com 希望使用 PaymentRequest),则该 iframe 必须设置 payment 权限策略。

js
const request = new PaymentRequest(
  [
    {
      supportedMethods: "secure-payment-confirmation",
      data: {
        // List of credential IDs obtained from the Account Provider.
        credentialIds,
        // The challenge is also obtained from the Account Provider.
        challenge: new Uint8Array(randomStringFromServer, (c) =>
          c.charCodeAt(0),
        ),
        instrument: {
          displayName: "Fancy Card ****1234",
          icon: "https://fancybank.com/card-art.png",
        },
        payeeOrigin: "https://merchant.com",
        timeout: 60000, // 1 minute
      },
    },
  ],
  {
    total: {
      label: "Total",
      amount: {
        currency: "USD",
        value: "5.00",
      },
    },
  },
);
try {
  // NOTE: canMakePayment() checks only public information for whether the SPC
  // call is valid. To preserve user privacy, it does not check whether any
  // passed credentials match the current device.
  const canMakePayment = await request.canMakePayment();
  if (!canMakePayment) {
    throw new Error("Cannot make payment");
  }
  const response = await request.show();
  await response.complete("success");
  // response.details is a PublicKeyCredential, with a clientDataJSON that
  // contains the transaction data for verification by the issuing bank.
  // send response.details to the issuing bank for verification
} catch (err) {
  // SPC cannot be used; merchant should fallback to traditional flows
}

在开始支付流程之前,可以通过调用 PaymentRequest.securePaymentConfirmationAvailability() 静态方法来确定 SPC 是否可用。例如:

js
async function spcSupport() {
  const support = await PaymentRequest.securePaymentConfirmationAvailability();
  if (support === "available") {
    // Commence SPC payment flow
  } else {
    // Fallback to traditional flows
  }
}

规范

规范
Payment Request API
支付方法标识符
Web Authentication:访问公钥凭证的 API - 第 3 级

另见