# 支付

> PWA 内的 Payment Request API、Apple Pay、Google Pay 与服务商 SDK：各引擎从哪个版本起支持哪种方式，以及结账面板弹出前浏览器要求什么。

import CompatTable from '@components/CompatTable.astro';

PWA 收款用的是与任何网页相同的三层：浏览器的 Payment Request API、接入它的钱包（Safari 中的 Apple Pay，Chromium 等浏览器中的 Google Pay），以及支付服务商的 JavaScript SDK，后者在不支持时回退到托管表单。哪一层可用取决于引擎及其版本，与页面是否已安装无关。

## 工作原理

结账面板归浏览器所有。你的代码描述接受的支付方式与总额，引擎决定能展示哪些钱包；服务商 SDK 位于其上，在该面板与自己的表单之间做选择。

### Payment Request API

`new PaymentRequest(methods, details)` 后调用 `show()` 即打开浏览器面板。兼容性数据（BCD `api.PaymentRequest`）记录：桌面 Chrome 60、Android 版 Chrome 53、Edge 15、macOS 与 iOS 上的 Safari 11.1、跟随 Android 版 Chrome 的 Samsung Internet，以及 Android WebView 136。Firefox 55 含有实现，但默认关闭在 `dom.payments.request.enabled` 与 `dom.payments.request.supportedRegions` 国家列表之后，所以 Firefox 用户看不到面板。该 API 仅限安全上下文，跨源 `<iframe>` 只有带 `allowpaymentrequest` 属性才能使用。

<CompatTable feature="payment-request" />

检测分两步，因为浏览器可能暴露构造函数却不支持你的任何一种方式：

```js
async function startCheckout(methods, details) {
  if (!('PaymentRequest' in window)) return openProviderForm();
  const request = new PaymentRequest(methods, details);
  if (!(await request.canMakePayment())) return openProviderForm();
  try {
    const response = await request.show();
    await response.complete('success');
  } catch (err) {
    if (err.name === 'AbortError') return; // 用户关闭了面板
    return openProviderForm();
  }
}
```

### Apple Pay

在 Safari 中，Payment Request 背后唯一的方式是 Apple Pay；面板与 Apple Pay JS 打开的是同一个。Apple 的规划页面写明：任何地区的商户都能接受 Apple Pay，「as long as their payment service provider supports Apple Pay」；「in China mainland, Apple Pay on the web is supported in Safari on iOS only」；平台通过 Apple Pay Web Merchant Registration API 注册商户网站。在 `SFSafariViewController` 中展示的网页可以像在 Safari 中一样使用 Apple Pay；`WKWebView` 不行，Apple 要求这类应用把请求移到原生代码中。

### Google Pay

Google 的设置指南列出 Google Pay API for web 可运行的浏览器：「Google Chrome, Mozilla Firefox, Apple Safari, Microsoft Edge, Opera, or UCWeb UC Browser」，并要求「an HTTPS webpage with a TLS domain-validated certificate」。该 API 为所选方式返回一个支付令牌，由你的后端转交给处理商。Google 把 Firefox 与 Safari 列入支持名单，尽管这两者的 Payment Request 并不呈现 Google Pay。

### 服务商 SDK

服务商 SDK 是普通 JavaScript，在 PWA 中与在标签页中一样加载。Stripe 曾用 Payment Request Button 封装 `PaymentRequest` 来展示 Apple Pay、Google Pay 或 Link，该组件在 Stripe 文档中已标记为弃用，由 Express Checkout Element 取代。已安装的 PWA 额外带来的问题是导航：基于重定向的 3-D Secure 或银行流程一旦离开 manifest 的 `scope`，就会在浏览器的超出 scope 界面下运行（Microsoft 记录了从 Store 安装的 Edge PWA 会显示 URL 与标题栏），因此发布独立窗口应用时，优先选择在弹窗或内嵌框架中完成验证的服务商。

| 层 | 可用范围 | 前提 |
|---|---|---|
| Payment Request API | Chrome 60 / Android 53、Edge 15、Safari 11.1、Samsung Internet、WebView 136 | 安全上下文；`show()` 需要瞬时用户激活 |
| Apple Pay | Safari（macOS、iOS）；`SFSafariViewController` | 商户注册；支持 Apple Pay 的 PSP |
| Google Pay | Chrome、Firefox、Safari、Edge、Opera、UC 浏览器 | 带 TLS 域名验证证书的 HTTPS |
| 服务商 SDK | SDK 支持的任何引擎 | 服务商账户；为不支持的钱包准备回退表单 |

## 实测行为

:::observed
`PaymentRequest.show()` 不抛出异常，而是拒绝 Promise。MDN 记录了各名称：`NotSupportedError`「if the user agent does not support the payment methods specified when the `PaymentRequest` constructor was called」；`SecurityError`「if the call to `show()` was not in response to a user action, such as a `click` or `keyup` event」；用户取消或已有面板打开时为 `AbortError`；同一请求展示两次为 `InvalidStateError`。同一页面指出，虽然规范禁止，Firefox 仍允许同时存在多个活动的支付请求。
:::

## 另请参阅

- [Payment Request API](https://www.w3.org/TR/payment-request/)（w3.org）
- [PaymentRequest: show() method](https://developer.mozilla.org/en-US/docs/Web/API/PaymentRequest/show)（developer.mozilla.org）
- [Set up Google Pay API for web](https://developers.google.com/pay/api/web/guides/setup)（developers.google.com）
- [Payment Request API 兼容性](/zh/compatibility/payment-request/)
- [Payment Request API 参考](/zh/reference/capabilities/payment-request/)
- [变现](/zh/ecosystem/monetization/)
- [Digital Goods API](/zh/reference/capabilities/digital-goods/)

← 返回[生态](/zh/ecosystem/)总览。