支付处理程序 API

W3C 工作草案

关于此文档的更多详细信息
此版本:
https://www.w3.org/TR/2025/WD-payment-handler-20250927/
最新发布版本:
https://www.w3.org/TR/payment-handler/
最新编辑草稿:
https://w3c.github.io/payment-handler/
历史:
https://www.w3.org/standards/history/payment-handler/
提交历史
测试套件:
https://wpt.live/payment-handler/
编辑:
Ian Jacobs (W3C)
Jinho Bang (受邀专家)
Stephen McGruer (Google)
前任编辑:
Andre Lyver (Shopify)
Tommy Thorsen (Opera)
Adam Roach (Mozilla)
Rouslan Solomakhin (Google)
Adrian Hope-Bailie (Coil)
反馈:
GitHub w3c/payment-handler (拉取请求, 新问题, 开放问题)

摘要

本规范定义了使 Web 应用能够处理支付请求的能力。

本文档状态

本节描述了本文件在发布时的状态。可在 W3C 标准和草案索引 中找到当前的 W3C 出版物列表以及本技术报告的最新修订版。

Web Payments 工作组维护着一份该工作组尚未处理的所有错误报告列表。本草案强调了一些仍待在工作组内讨论的未决问题。尚未就这些问题的结论作出决定,也未决定它们是否有效。强烈鼓励提交包含针对未决问题的规范文本建议的拉取请求。

本文档由Web Payments 工作组作为工作草案发布,使用了 Recommendation track

作为工作草案发布并不意味着得到 W3C 及其成员的认可。

这是一个草案文档,可能随时被更新、替换或被其他文档废弃。不宜将此文档引用为除正在进行的工作之外的资料。

本文档由在以下政策下运作的小组产生: W3C 专利政策W3C 维护一份与该小组交付物相关的任何专利披露的公开列表;该页面还包含披露专利的说明。任何实际知道并认为包含必要权利要求(Essential Claim(s))的专利的个人,必须按照 W3C 专利政策第6节的规定披露该信息。

本文档受2025年8月18日的 W3C 流程文档的约束。

1. 介绍

本节为非规范性内容。

本规范定义了若干新功能,使 Web 应用能够代表用户处理支付请求:

注意

本规范不涉及使用操作系统特定机制(即“原生应用”)构建的软件如何处理支付请求。

2. 概述

在本文档中,我们设想以下流程:

  1. 某个来源向用户请求权限,以处理一组受支持支付方式的支付请求。例如,访问零售或银行网站的用户可能会被提示注册来自该来源的支付处理程序。该来源确定权限的范围,但来源的能力可能会发展而不需要额外的用户同意。
  2. 支付处理程序service worker 代码中定义。
  3. 当商家(或其他 收款方)调用 [payment-request] 方法 canMakePayment()show() (例如,当用户 —— 付款方 —— 在结账页面上按下按钮时),用户代理会计算候选支付处理程序列表,将商家接受的支付方法与用户代理通过包括但不限于以下机制已知的支付方法进行比较:
    • 先前通过此 API 注册的支付处理程序。
    • 在交易过程中可能通过此 API 注册的支付处理程序,例如通过 payment method manifest 识别的那些。
    • 通过其他机制注册的支付处理程序,例如操作系统。
  4. 用户代理向用户显示一组选择:候选支付处理程序。用户代理使用在注册时提供的或 Web 应用可获得的信息(标签和图标)来显示这些选项。
  5. 付款方 选择某个支付处理程序时,用户代理会在所选支付处理程序的 service worker 中触发一个 PaymentRequestEvent(参见 用户交互任务源)。该 PaymentRequestEvent 包含来自 PaymentRequest(定义于 [payment-request])的一些信息以及额外的信息(例如收款方的来源)。
  6. 一旦被激活,支付处理程序会执行处理支付请求所需的各步骤,并向 收款方 返回适当的支付响应。如果需要与用户交互,支付处理程序 可以为此目的打开一个窗口。
  7. 用户代理在支付处理程序完成处理请求后异步接收响应。该响应成为 [payment-request] 的 PaymentResponse
注意

一个来源可能使用不止一个 service worker 实现支付应用,因此每个来源可能注册多个 支付处理程序。实际调用哪个处理程序由用户的选择决定。

2.1 处理支付请求

本节为非规范性内容。

一个 支付处理程序 是可以代表用户处理支付请求的 Web 应用。

支付处理程序的逻辑由其支持的支付方法驱动。有些支付方法对支付处理程序的处理要求很少或几乎没有,支付处理程序只需在响应中返回支付卡详情。随后由收款方网站使用返回的数据作为输入来处理支付。

相比之下,一些支付方法,例如加密货币支付或银行发起的信用转账,要求支付处理程序发起支付处理。在此类情况下,支付处理程序将返回一个支付参考、端点 URL 或其他数据,收款方网站可以使用这些数据来确定支付结果(而不是自己处理支付)。

处理支付请求可能包括大量交互:通过新窗口或其他 API(例如 Web Cryptography API)与用户交互,或通过网络请求或其它方式与其他服务和来源交互。

本规范不涉及在支付处理程序接受 PaymentRequestEvent 与支付处理程序返回响应之间发生的这些活动。所有可能需要配置支付处理程序和处理支付请求的活动均由支付处理程序的实现来完成,包括:

因此,一个来源将依赖许多由其他地方定义的 Web 技术来进行生命周期管理、安全、用户认证、用户交互等。

2.2 与其他类型支付应用的关系

本节为非规范性内容。

本规范不涉及第三方移动支付应用如何通过专有机制与用户代理交互,也不涉及用户代理自身如何提供简单的支付应用功能。

不同类型的支付应用。Payment Handler API 适用于 Web 应用。
Figure 1 Payment Handler API 使 Web 应用能够处理支付。其他类型的支付应用可能使用其他(专有的)机制。

3. 注册

可以通过即时注册(just-in-time, JIT)机制在用户代理中注册支付处理程序。

3.1 及时注册

如果在商家调用 show() 方法时未注册相应的支付处理程序,用户代理可以允许用户在交易过程中注册该支付处理程序(“及时注册”)。

本节其余内容为非规范性。

用户代理可以通过从商家请求的 基于 URL 的支付方法标识符 找到的 payment method manifest 推导出支付处理程序信息,从而执行即时安装。

4. 管理

本节描述了支付处理程序用于管理自身属性的功能。

4.1 ServiceWorkerRegistration 接口的扩展

WebIDLpartial interface ServiceWorkerRegistration {
  [SameObject] readonly attribute PaymentManager paymentManager;
};

paymentManager 属性公开了支付处理程序的管理功能。

4.2 PaymentManager 接口

WebIDL[SecureContext, Exposed=(Window)]
interface PaymentManager {
  attribute DOMString userHint;
  Promise<undefined> enableDelegations(sequence<PaymentDelegation> delegations);
};

PaymentManager支付处理程序 用于管理其支持的委托类型。

4.2.1 userHint 属性

在显示支付处理程序名称和图标时,用户代理可使用此字符串来提升用户体验。例如,用户提示 "**** 1234" 可以提醒用户该支付处理程序可用于特定的卡。

4.2.2 enableDelegations() 方法

此方法允许 支付处理程序 异步声明其支持的 PaymentDelegation 列表。

4.3 PaymentDelegation 枚举

WebIDLenum PaymentDelegation {
  "shippingAddress",
  "payerName",
  "payerPhone",
  "payerEmail"
};
"shippingAddress"
支付处理程序将在需要时提供收货地址。
"payerName"
支付处理程序将在需要时提供付款人的姓名。
"payerPhone"
支付处理程序将在需要时提供付款人的电话。
"payerEmail"
支付处理程序将在需要时提供付款人的电子邮件。

5. 可进行支付

如果 支付处理程序支持 CanMakePaymentEvent,则 用户代理可以使用它来帮助筛选可用的支付处理程序。

实现可以对开发者响应 CanMakePaymentEvent施加超时时间。如果超时到期, 则实现的行为等同于调用了 respondWith() 且参数为 false

5.1 ServiceWorkerGlobalScope 的扩展

WebIDLpartial interface ServiceWorkerGlobalScope {
  attribute EventHandler oncanmakepayment;
};

5.1.1 oncanmakepayment 属性

oncanmakepayment 属性是一个 事件处理程序, 其对应的事件处理程序事件类型为 "canmakepayment"。

5.2 CanMakePaymentEvent

CanMakePaymentEvent 用作信号,用于表明支付处理程序是否能够响应支付请求。

WebIDL[Exposed=ServiceWorker]
interface CanMakePaymentEvent : ExtendableEvent {
  constructor(DOMString type);
  undefined respondWith(Promise<boolean> canMakePaymentResponse);
};

5.2.1 respondWith() 方法

支付处理程序使用此方法来表明其是否可以响应支付请求。

5.3 处理 CanMakePaymentEvent

通过 PaymentRequest 收到请求后,用户代理必须(MUST) 运行以下步骤:

  1. 如果 用户代理 的设置禁止使用 CanMakePaymentEvent(例如在私密浏览模式下), 则终止这些步骤。
  2. registration 为一个 ServiceWorkerRegistration
  3. 如果未找到 registration,则终止这些步骤。
  4. 使用 触发功能事件 "canmakepayment",并在 registration 上使用 CanMakePaymentEvent

5.4 处理 CanMakePaymentEvent 的示例

本节为非规范性内容。

此示例展示如何编写一个监听 CanMakePaymentEvent 的 service worker。当收到 CanMakePaymentEvent 时,service worker 始终返回 true。

示例 1:处理 CanMakePaymentEvent
self.addEventListener("canmakepayment", function(e) {
  e.respondWith(new Promise(function(resolve, reject) {
    resolve(true);
  }));
});

5.5 支付处理程序的筛选

给定一个 PaymentMethodData 和一个依据 支付方法标识符匹配的支付处理程序,如果该支付处理程序可用于支付,则此算法返回 true

  1. methodName 为在 PaymentMethodData 中指定的 支付方法标识符字符串。
  2. methodDataPaymentMethodData 的支付方法特定数据。
  3. paymentHandlerOrigin 为支付处理程序的 ServiceWorkerRegistration 作用域 URL 的来源(origin)
  4. paymentMethodManifest 为针对 methodName摄取解析后的 支付方法清单
  5. 如果 methodName 是一个 基于 URL 的支付方法标识符, 且 paymentMethodManifest 中的受支持来源(supported origins)"*" 字符串,则返回 true
  6. 否则,如果 基于 URL 的支付方法标识符 methodNamepaymentHandlerOrigin 具有相同的来源,则在支付处理程序中触发 CanMakePaymentEvent 并返回其结果。
  7. 否则,如果 paymentMethodManifest 中的 受支持来源(supported origins) 是包含 paymentHandlerOrigin来源有序集合,则在支付处理程序中触发 CanMakePaymentEvent 并返回其结果。
  8. 否则,返回 false

6. 调用

一旦用户选择了支付处理程序,用户代理就会触发 PaymentRequestEvent,并使用随后得到的 PaymentHandlerResponse 来为 [payment-request] 创建一个 PaymentResponse

Issue 117: 将 Abort() 的支持委托给支付处理程序

Payment Request API 支持将管理中止的职责委托给支付应用。现有提议是在 Payment Handler 接口中添加 paymentRequestAborted 事件。该事件将包含一个 respondWith 方法,接受一个布尔参数,用以指示 paymentRequest 是否已成功中止。

6.1 ServiceWorkerGlobalScope 的扩展

本规范扩展了 ServiceWorkerGlobalScope 接口。

WebIDLpartial interface ServiceWorkerGlobalScope {
  attribute EventHandler onpaymentrequest;
};

6.1.1 onpaymentrequest 属性

onpaymentrequest 属性是一个 事件处理程序, 其对应的事件处理程序事件类型PaymentRequestEvent

6.2 PaymentRequestDetailsUpdate 字典

PaymentRequestDetailsUpdate 包含更新后的总额(可选地包含修饰符和配送选项)以及因用户选择支付方法、收货地址或配送选项而产生的可能错误,这些选择发生在支付处理程序中。

WebIDLdictionary PaymentRequestDetailsUpdate {
  DOMString error;
  PaymentCurrencyAmount total;
  sequence<PaymentDetailsModifier> modifiers;
  sequence<PaymentShippingOption> shippingOptions;
  object paymentMethodErrors;
  AddressErrors shippingAddressErrors;
};

6.2.1 error 成员

人类可读的字符串,用于解释为何用户选择的支付方法、收货地址或配送选项无法使用。

6.2.2 total 成员

基于变更后的支付方法、收货地址或配送选项而更新的总额。例如,总额可能因用户所选支付方法的账单地址改变了增值税(VAT)而变化;或者因用户所选/提供的配送选项/地址改变了配送费用而变化。

6.2.3 modifiers 成员

基于变更后的支付方法、收货地址或配送选项而更新的修饰符。例如,如果根据账单或收货地址总体总额增加了 €1.00,则每个修饰符中指定的总额也应增加 €1.00。

6.2.4 shippingOptions 成员

基于变更后的收货地址而更新的 shippingOptions。例如,对于用户提供的国家/地区,加急配送可能更昂贵或不可用。

6.2.5 paymentMethodErrors 成员

支付方法的校验错误(如有)。

6.2.6 shippingAddressErrors 成员

收货地址的校验错误(如有)。

6.3 PaymentRequestEvent

PaymentRequestEvent 表示在用户选择之后,支付处理程序可用的数据和方法。用户代理会将 PaymentRequest 中可用数据的一个子集传递给支付处理程序。

WebIDL[Exposed=ServiceWorker]
interface PaymentRequestEvent : ExtendableEvent {
  constructor(DOMString type, optional PaymentRequestEventInit eventInitDict = {});
  readonly attribute USVString topOrigin;
  readonly attribute USVString paymentRequestOrigin;
  readonly attribute DOMString paymentRequestId;
  readonly attribute FrozenArray<PaymentMethodData> methodData;
  readonly attribute object total;
  readonly attribute FrozenArray<PaymentDetailsModifier> modifiers;
  readonly attribute object? paymentOptions;
  readonly attribute FrozenArray<PaymentShippingOption>? shippingOptions;
  Promise<WindowClient?> openWindow(USVString url);
  Promise<PaymentRequestDetailsUpdate?> changePaymentMethod(DOMString methodName, optional object? methodDetails = null);
  Promise<PaymentRequestDetailsUpdate?> changeShippingAddress(optional AddressInit shippingAddress = {});
  Promise<PaymentRequestDetailsUpdate?> changeShippingOption(DOMString shippingOption);
  undefined respondWith(Promise<PaymentHandlerResponse> handlerResponsePromise);
};

6.3.1 topOrigin 属性

返回一个字符串,指示顶层 收款方网页的来源(origin)。 此属性由处理 PaymentRequestEvent进行初始化。

6.3.2 paymentRequestOrigin 属性

返回一个字符串,指示 PaymentRequest 被初始化的来源。 当 PaymentRequesttopOrigin 中初始化时,这两个属性具有相同的值;否则二者不同。 例如,当 PaymentRequest 在一个来源不同于 topOrigin 的 iframe 中初始化时,此属性值为该 iframe 的来源。此属性由 处理 PaymentRequestEvent进行初始化。

6.3.3 paymentRequestId 属性

获取时,paymentRequestId 属性返回与此 PaymentRequestEvent 对应的 PaymentRequest[[details]].id

6.3.4 methodData 属性

此属性包含若干 PaymentMethodData 字典,包含网站可接受的支付方法标识符以及任何相关的特定 支付方法数据。它由 PaymentRequest 使用下文定义的 方法数据填充算法进行填充。

6.3.5 total 属性

此属性表示请求支付的总金额。其类型为 PaymentCurrencyAmount 字典(定义见 [payment-request]),并使用在实例化相应 PaymentRequest 对象时所提供的 total 字段的副本进行初始化。

6.3.6 modifiers 属性

PaymentDetailsModifier 字典序列包含针对特定支付方法标识符的修饰符(例如,如果基于每种支付方法,支付金额或货币类型有所不同)。它由 PaymentRequest 使用下文定义的 修饰符填充算法进行填充。

6.3.7 paymentOptions 属性

相应 PaymentRequestPaymentOptions 的取值。 仅当请求收货地址和/或付款人联系信息的任意子集时可用。

6.3.8 shippingOptions 属性

相应 PaymentDetailsInit 字典中的 ShippingOptions 的取值。(PaymentDetailsInit 继承了 PaymentDetailsBase 的 ShippingOptions)。仅当请求收货地址时可用。

6.3.9 openWindow() 方法

支付处理程序使用此方法向用户显示一个窗口。调用时,将运行 打开窗口算法

6.3.10 changePaymentMethod() 方法

支付处理程序使用此方法,根据诸如账单地址等支付方法详情获取更新后的总额。调用时,将运行 变更支付方法算法

6.3.11 changeShippingAddress() 方法

支付处理程序使用此方法,基于 shippingAddress 获取更新后的支付详情。调用时,将运行 变更支付详情算法

6.3.12 changeShippingOption() 方法

支付处理程序使用此方法,依据 shippingOption 标识符获取更新后的支付详情。调用时,将运行 变更支付详情算法

6.3.13 respondWith() 方法

当支付成功完成时,支付处理程序使用此方法提供 PaymentHandlerResponse。调用时,将使用 eventhandlerResponsePromise 作为参数运行 响应 PaymentRequest 算法

Issue 123: 与支付应用共享用户数据?

是否应在用户明确同意的情况下,将存储在用户代理中的用户数据提供给支付应用?支付应用可以在安装时或首次被调用时请求该权限。

6.3.14 PaymentRequestEventInit 字典

WebIDLdictionary PaymentRequestEventInit : ExtendableEventInit {
  USVString topOrigin;
  USVString paymentRequestOrigin;
  DOMString paymentRequestId;
  sequence<PaymentMethodData> methodData;
  PaymentCurrencyAmount total;
  sequence<PaymentDetailsModifier> modifiers;
  PaymentOptions paymentOptions;
  sequence<PaymentShippingOption> shippingOptions;
};

topOriginpaymentRequestOriginpaymentRequestIdmethodDatatotalmodifierspaymentOptionsshippingOptions 成员与 PaymentRequestEvent 中的相应定义共享定义。

6.3.15 方法数据填充算法

为初始化 methodData 的值,用户代理 必须(MUST)执行以下步骤或等效步骤:

  1. registeredMethods 为被调用的支付处理程序所注册的 支付方法标识符集合。
  2. 创建一个新的空的 Sequence
  3. dataList 设为新创建的 Sequence
  4. 对于相应支付请求中的 PaymentRequest@[[methodData]] 中的每个条目,执行以下步骤:
    1. inData 设为当前条目。
    2. commonMethods 设为 inData.supportedMethodsregisteredMethods 的交集。
    3. 如果 commonMethods 为空,跳过余下子步骤并继续下一个条目(如果有)。
    4. 创建一个新的 PaymentMethodData 对象。
    5. outData 设为新创建的 PaymentMethodData
    6. outData.supportedMethods 设为包含 commonMethods 成员的列表。
    7. outData.data 设为 inData.data 的副本。
    8. outData 追加到 dataList
  5. methodData 设为 dataList

6.3.16 修饰符填充算法

为初始化 modifiers 的值,用户代理 必须(MUST)执行以下步骤或等效步骤:

  1. registeredMethods 为被调用的支付处理程序所注册的 支付方法标识符集合。
  2. 创建一个新的空的 Sequence
  3. modifierList 设为新创建的 Sequence
  4. 对于相应支付请求中的 PaymentRequest@[[paymentDetails]].modifiers 中的每个条目,执行以下步骤:
    1. inModifier 设为当前条目。
    2. commonMethods 设为 inModifier.supportedMethodsregisteredMethods 的交集。
    3. 如果 commonMethods 为空,跳过余下子步骤并继续下一个条目(如果有)。
    4. 创建一个新的 PaymentDetailsModifier 对象。
    5. outModifier 设为新创建的 PaymentDetailsModifier
    6. outModifier.supportedMethods 设为包含 commonMethods 成员的列表。
    7. outModifier.total 设为 inModifier.total 的副本。
    8. outModifier 追加到 modifierList
  5. modifiers 设为 modifierList

6.4 内部槽

PaymentRequestEvent 的实例会使用下表中的内部槽进行创建:

内部槽 默认值 说明(非规范性
[[windowClient]] null 当前活动的 WindowClient。若支付处理程序当前正向用户显示一个窗口,则会设置该值;否则为 null。
[[respondWithCalled]] false YAHO

6.5 处理 PaymentRequestEvent

通过 PaymentRequest.PaymentRequest.show() 接收到 PaymentRequest,并且用户随后选择了支付处理程序后,用户代理 必须(MUST)运行以下步骤:

  1. registration 为与用户所选支付处理程序对应的 ServiceWorkerRegistration
  2. 如果未找到 registration,则以一个 "InvalidStateError" DOMException 拒绝由 PaymentRequest.show() 创建的 Promise,并终止这些步骤。
  3. 使用 触发功能事件 "paymentrequest",并在 registration 上使用 PaymentRequestEvent,且具有以下属性:

    topOrigin
    顶层收款方网页来源的序列化
    paymentRequestOrigin
    PaymentRequest 被初始化所在上下文的来源的序列化
    methodData
    执行 方法数据填充算法 的结果。
    modifiers
    执行 修饰符填充算法 的结果。
    total
    来自相应 PaymentDetailsInit 的 total 字段的副本。
    paymentRequestId
    来自 PaymentRequest 的 \[\[details\]\].id
    paymentOptions
    传递给相应 PaymentRequest 构造函数的 paymentOptions 字典的副本。
    shippingOptions
    来自相应 PaymentDetailsInit 的 shippingOptions 字段的副本。

    然后与 dispatchedEvent 并行运行以下步骤:

    1. 等待 dispatchedEvent延长生命周期的 Promise 全部解析。
    2. 如果 支付处理程序未提供 PaymentHandlerResponse, 则以一个 "OperationError" DOMException 拒绝由 PaymentRequest.show() 创建的 Promise

7. 窗口

被调用的支付处理程序可能需要也可能不需要显示自身信息或请求用户输入。潜在的支付处理程序显示示例如下:

需要可视化显示和用户交互的支付处理程序可以调用 openWindow() 向用户显示页面。

注意

由于用户代理知道该方法与 PaymentRequestEvent 相连接,它们SHOULD以与流程一致且不让用户困惑的方式呈现窗口。所得到的窗口客户端会绑定到发起 PaymentRequest 的选项卡/窗口。单个支付处理程序SHOULD NOT被允许通过此方法打开超过一个的客户端窗口。

7.1 打开窗口算法

Issue 115: 打开窗口算法

此算法类似于 Service Workers 规范中的打开窗口算法

Issue 115: 打开窗口算法

我们是否应该引用 Service Workers 规范而不是复制其步骤?

  1. event 为此 PaymentRequestEvent
  2. 如果 eventisTrusted 属性为 false,则返回一个以 "InvalidStateError" DOMException 拒绝的 Promise
  3. request 为触发此 PaymentRequestEventPaymentRequest
  4. url 为对参数 url 进行解析的结果。
  5. 如果解析 url 时抛出异常,则返回以该异常拒绝的 Promise
  6. 如果 urlabout:blank,则返回以 TypeError 拒绝的 Promise
  7. 如果 url 的来源与与该支付处理程序关联的 service worker 的来源不同,则返回以 null 成功解决的 Promise
  8. promise 为一个新的 Promise
  9. 返回 promise 并并行执行余下步骤:
  10. 如果 event[[windowClient]] 不为 null,则:
    1. 如果 event[[windowClient]].visibilityState 不为 "unloaded",则以 "InvalidStateError" DOMException 拒绝 promise 并中止这些步骤。
  11. newContext 为一个新的顶层浏览上下文
  12. 导航 newContexturl,启用异常且启用替换。
  13. 如果导航抛出异常,则以该异常拒绝 promise 并中止这些步骤。
  14. 如果 newContext 的来源与与该支付处理程序关联的 service worker client 的来源不同,则:
    1. 以 null 解决 promise
    2. 中止这些步骤。
  15. client 为以 newContext 作为参数运行 create window client 算法的结果。
  16. event[[windowClient]] 设为 client
  17. client 解决 promise

7.2 处理 PaymentRequestEvent 的示例

本节为非规范性内容。

此示例展示如何编写一个监听 PaymentRequestEvent 的 service worker。当接收到 PaymentRequestEvent 时,service worker 会打开一个窗口与用户交互。

示例 2:处理 PaymentRequestEvent
async function getPaymentResponseFromWindow() {
  return new Promise((resolve, reject) => {
    self.addEventListener("message", listener = e => {
      self.removeEventListener("message", listener);
      if (!e.data || !e.data.methodName) {
        reject();
        return;
      }
      resolve(e.data);
    });
  });
}

self.addEventListener("paymentrequest", e => {
  e.respondWith((async() => {
    // Open a new window for providing payment UI to user.
    const windowClient = await e.openWindow("payment_ui.html");

    // Send data to the opened window.
    windowClient.postMessage({
      total: e.total,
      modifiers: e.modifiers
    });

    // Wait for a payment response from the opened window.
    return await getPaymentResponseFromWindow();
  })());
});

使用上述的简单方案,被加载到支付处理程序窗口中的一个简单 HTML 页面可能如下所示:

示例 3:简单的支付处理程序窗口
<form id="form">
<table>
  <tr><th>Cardholder Name:</th><td><input name="cardholderName"></td></tr>
  <tr><th>Card Number:</th><td><input name="cardNumber"></td></tr>
  <tr><th>Expiration Month:</th><td><input name="expiryMonth"></td></tr>
  <tr><th>Expiration Year:</th><td><input name="expiryYear"></td></tr>
  <tr><th>Security Code:</th><td><input name="cardSecurityCode"></td></tr>
  <tr><th></th><td><input type="submit" value="Pay"></td></tr>
</table>
</form>

<script>
navigator.serviceWorker.addEventListener("message", e => {
  /* Note: message sent from payment app is available in e.data */
});

document.getElementById("form").addEventListener("submit", e => {
  const details = {};
  ["cardholderName", "cardNumber", "expiryMonth", "expiryYear", "cardSecurityCode"]
  .forEach(field => {
    details[field] = form.elements[field].value;
  });

  const paymentAppResponse = {
    methodName: "https://example.com/pay",
    details
  };

  navigator.serviceWorker.controller.postMessage(paymentAppResponse);
  window.close();
});
</script>

8. 响应

8.1 PaymentHandlerResponse 字典

PaymentHandlerResponse 使用以下字典进行传达:
WebIDLdictionary PaymentHandlerResponse {
DOMString methodName;
object details;
DOMString? payerName;
DOMString? payerEmail;
DOMString? payerPhone;
AddressInit shippingAddress;
DOMString? shippingOption;
};

8.1.1 methodName 属性

用户为完成交易所选择的支付方法标识符,对应于该 支付方法

8.1.2 details 属性

一个可 JSON 序列化的对象,提供由商户用于处理交易并确定资金成功转移的特定于 支付方法的消息。

用户代理通过对应 PaymentRequestEvent 接口的 respondWith 函数所提供 Promise 的解决来接收来自支付处理程序的成功响应。应用应以包含支付响应的 PaymentHandlerResponse 实例来解决该 Promise。若发生用户取消或错误,应用可以通过拒绝该 Promise 来表示失败。

如果 Promise 被拒绝,用户代理MUST运行 支付应用失败算法。该算法的具体细节由实现者决定。可接受的行为包括但不限于:

  • 允许用户重试,使用相同的支付处理程序或不同的支付处理程序。
  • 拒绝由 PaymentRequest.show() 创建的 Promise。

8.1.3 payerName 属性

用户提供的付款人姓名。

8.1.4 payerEmail 属性

用户提供的付款人电子邮件。

8.1.5 payerPhone 属性

用户提供的付款人电话号码。

8.1.6 shippingAddress 属性

用户提供的收货地址。

8.1.7 shippingOption 属性

用户所选配送选项的标识符。

8.2 变更支付方法算法

当以 methodNamemethodDetails 参数调用此算法时,用户代理MUST运行以下步骤:

  1. 以给定的 methodNamemethodDetails 参数构造 PaymentMethodChangeEvent event,并运行 payment method changed algorithm
  2. 如果 eventupdateWith(detailsPromise) 未被运行,则返回 null
  3. 如果 eventupdateWith(detailsPromise) 抛出异常,则重新抛出该错误。
  4. 如果 eventupdateWith(detailsPromise) 超时(可选),则抛出 "InvalidStateError" DOMException
  5. event.updateWith(detailsPromise) 中的 detailsPromise 构造并返回一个 PaymentRequestDetailsUpdate

8.3 变更支付详情算法

当以 shippingAddressshippingOption 调用此算法时,用户代理MUST运行以下步骤:

  1. 使用更新后的详情(shippingAddressshippingOption)构造 PaymentRequestUpdateEvent event,并运行 PaymentRequest updated algorithm
  2. 如果 eventupdateWith(detailsPromise) 未被运行,则返回 null
  3. 如果 eventupdateWith(detailsPromise) 抛出异常,则重新抛出该错误。
  4. 如果 eventupdateWith(detailsPromise) 超时(可选),则抛出 "InvalidStateError" DOMException
  5. event.updateWith(detailsPromise) 中的 detailsPromise 构造并返回一个 PaymentRequestDetailsUpdate

8.4 响应 PaymentRequest 算法

当以 eventhandlerResponsePromise 参数调用此算法时,用户代理MUST运行以下步骤:

  1. 如果 eventisTrusted 为 false, 则抛出 "InvalidStateError" DOMException 并中止这些步骤。
  2. 如果 eventdispatch flag 未设置,则抛出 "InvalidStateError" DOMException 并中止这些步骤。
  3. 如果 event[[respondWithCalled]] 为 true,则抛出 "InvalidStateError" DOMException 并中止这些步骤。
  4. event[[respondWithCalled]] 设为 true。
  5. 设置 eventstop propagation flageventstop immediate propagation flag
  6. handlerResponsePromise 添加到 eventextend lifetime promises 中。
  7. eventpending promises count 增加 1。
  8. 当被拒绝时(Upon rejection), 针对 handlerResponsePromise
    1. 运行 支付应用失败算法 并中止这些步骤。
  9. 当被兑现时(Upon fulfillment), 针对 handlerResponsePromise
    1. handlerResponse 为将 value 转换为 IDL 值 的结果,类型为 PaymentHandlerResponse。若此操作抛出异常, 则运行 支付应用失败算法 并中止这些步骤。
    2. 校验 handlerResponse 中所有必需成员是否存在且格式正确。
      1. handlerResponsemethodName 不存在,或未设置为 eventmethodData 中的某个值,则运行 支付应用失败算法 并中止这些步骤。
      2. handlerResponsedetails 不存在或不是可 JSON 序列化的, 则运行 支付应用失败算法 并中止这些步骤。
      3. shippingRequired 为关联的 PaymentRequest 的 requestShipping 取值,来自 paymentOptions。 如果 shippingRequiredhandlerResponseshippingAddress 不存在,则运行 支付应用失败算法 并中止这些步骤。
      4. 如果 shippingRequiredhandlerResponseshippingOption 不存在,或未设置为来自 eventshippingOptions 中的某个配送选项标识符,则运行 支付应用失败算法 并中止这些步骤。
      5. payerNameRequired 为关联的 PaymentRequest 的 requestPayerName 取值,来自 paymentOptions。 如果 payerNameRequiredhandlerResponsepayerName 不存在,则运行 支付应用失败算法 并中止这些步骤。
      6. payerEmailRequired 为关联的 PaymentRequest 的 requestPayerEmail 取值,来自 paymentOptions。 如果 payerEmailRequiredhandlerResponsepayerEmail 不存在,则运行 支付应用失败算法 并中止这些步骤。
      7. payerPhoneRequired 为关联的 PaymentRequest 的 requestPayerPhone 取值,来自 paymentOptions。 如果 payerPhoneRequiredhandlerResponsepayerPhone 不存在,则运行 支付应用失败算法 并中止这些步骤。
    3. 序列化 handlerResponse 的必需成员(methodNamedetails 始终必需; 当 shippingRequired 为 true 时,shippingAddressshippingOption 必需; 当 payerNameRequiredpayerEmailRequiredpayerPhoneRequired 分别为 true 时,payerNamepayerEmailpayerPhone 必需):
      1. handlerResponse 中的每个 member,令 serializeMember 为调用 StructuredSerialize 处理 handlerResponse.member 的结果。若有异常则重新抛出。
    4. 用户代理MUST运行 [payment-request] 中定义的 user accepts the payment request algorithm,并以以下步骤替换其第 9-15 步或其等效步骤。
      1. 反序列化已序列化的成员:
        1. 对每个 serializeMember,令 member 为调用 StructuredDeserialize 处理 serializeMember 的结果。若有异常则重新抛出。
      2. 如果上述步骤中出现任何异常,则运行 支付应用失败算法 并中止这些步骤。
      3. methodName 赋予关联 PaymentRequest 的 response.methodName
      4. details 赋予关联 PaymentReqeust 的 response.details
      5. shippingRequired,则将关联 PaymentReqeust 的 shippingAddress 属性设为 shippingAddress;否则设为 null。
      6. shippingRequired,则将关联 PaymentReqeust 的 shippingOption 属性设为 shippingOption;否则设为 null。
      7. payerNameRequired,则将关联 PaymentReqeust 的 payerName 属性设为 payerName;否则设为 null。
      8. payerEmailRequired,则将关联 PaymentReqeust 的 payerEmail 属性设为 payerEmail;否则设为 null。
      9. payerPhoneRequired,则将关联 PaymentReqeust 的 payerPhone 属性设为 payerPhone;否则设为 null。
  10. 当被兑现(Upon fulfillment)当被拒绝(upon rejection) handlerResponsePromise 时, 将一个微任务排入队列以执行以下步骤:
    1. eventpending promises count 减一。
    2. registrationthis相关全局对象 所关联的 service workercontaining service worker registration
    3. 如果 registration 不为 null,则以 registration 调用 Try Activate

以下示例展示如何响应支付请求:

示例 4:发送支付响应
paymentRequestEvent.respondWith(new Promise(function(accept,reject) {
  /* ... processing may occur here ... */
  accept({
    methodName: "https://example.com/pay",
    details: {
      cardHolderName:   "John Smith",
      cardNumber:       "1232343451234",
      expiryMonth:      "12",
      expiryYear :      "2020",
      cardSecurityCode: "123"
     },
    shippingAddress: {
      addressLine: [
        "1875 Explorer St #1000",
      ],
      city: "Reston",
      country: "US",
      dependentLocality: "",
      organization: "",
      phone: "+15555555555",
      postalCode: "20190",
      recipient: "John Smith",
      region: "VA",
      sortingCode: ""
    },
    shippingOption: "express",
    payerEmail: "john.smith@gmail.com",
  });
}));
注意

[payment-request] 定义了一个 ID,生态系统中的各方(包括支付应用提供方和收款方)可在网络或其他故障后使用其进行对账。

9. 安全与隐私注意事项

9.1 地址

由于隐私问题,Web Payments 工作组从最初版本的 Payment Request API 中移除了对收货地址和账单地址的支持;参见 issue 842。为给仍继续支持此能力的实现提供文档,工作组现正恢复该功能,并期望同时解决隐私问题。在此过程中,工作组也可能基于其他 API 的演进(例如 Content Picker API)对 Payment Request API 进行更改。

9.2 有关用户环境的信息

9.5 用户对跨源共享数据的认知

9.6 安全通信

9.7 授权的支付应用

9.8 支持的来源

9.9 数据验证

9.10 私密浏览模式

10. 支付处理程序显示注意事项

本节为非规范性内容。

在排序支付处理程序时,预期用户代理应优先遵从用户偏好而非其他偏好。预期用户代理应允许手动配置选项,例如为某个来源或所有来源设置首选的支付处理程序显示顺序。

用户体验细节留给实现者决定。

11. 依赖关系

本规范依赖于若干其他底层规范。

Payment Request API
术语 payment methodPaymentRequestPaymentResponsesupportedMethodsPaymentCurrencyAmountpaymentDetailsModifierpaymentDetailsInitpaymentDetailsBasePaymentMethodDataPaymentOptionsPaymentShippingOptionAddressInitAddressErrorsPaymentMethodChangeEventPaymentRequestUpdateEventIDcanMakePayment()show()updateWith(detailsPromise)user accepts the payment request algorithmpayment method changed algorithmPaymentRequest updated algorithm 以及 JSON-serializable,均由 Payment Request API 规范 [payment-request] 定义。
ECMAScript
术语 internal slotJSON.stringify 由 [ECMASCRIPT] 定义。
Payment Method Manifest
术语 payment method manifestingest payment method manifestparsed payment method manifest supported origins,由 Payment Method Manifest 规范 [payment-method-manifest] 定义。
Service Workers
术语 service workerservice worker registrationservice worker clientServiceWorkerRegistrationServiceWorkerGlobalScopefire functional eventextend lifetime promisespending promises countcontaining service worker registrationTry Clear RegistrationTry ActivateExtendableEventExtendableEventInit、 和 scope URL 定义见 [SERVICE-WORKERS]。

12. 一致性

除了标记为非规范性的章节外,本规范中的所有编写指南、图表、示例和注记均为非规范性内容。除此之外,本规范中的其他内容均为规范性内容。

本文档中的关键词 MAYMUSTSHOULD、 和 SHOULD NOT 的解释方式如 BCP 14 [RFC2119] [RFC8174] 所述,仅当且仅当这些词以此处所示的全大写形式出现时适用。

只有一种产品类别可以宣称符合本规范:用户代理

只要最终结果与按照本规范算法所得结果不可区分,用户代理MAY以任何期望的方式实现本规范中给出的算法。

用户代理MAY对原本无限制的输入施加与实现相关的限制,例如为了防止拒绝服务攻击、避免内存耗尽或绕开平台特定限制。当某个输入超出与实现相关的限制时,用户代理MUST抛出,或在 promise 的上下文中以 TypeError 拒绝,并可选择性地告知开发者该输入如何超出了与实现相关的限制。

A. IDL 索引

WebIDLpartial interface ServiceWorkerRegistration {
  [SameObject] readonly attribute PaymentManager paymentManager;
};

[SecureContext, Exposed=(Window)]
interface PaymentManager {
  attribute DOMString userHint;
  Promise<undefined> enableDelegations(sequence<PaymentDelegation> delegations);
};

enum PaymentDelegation {
  "shippingAddress",
  "payerName",
  "payerPhone",
  "payerEmail"
};

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler oncanmakepayment;
};

[Exposed=ServiceWorker]
interface CanMakePaymentEvent : ExtendableEvent {
  constructor(DOMString type);
  undefined respondWith(Promise<boolean> canMakePaymentResponse);
};

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler onpaymentrequest;
};

dictionary PaymentRequestDetailsUpdate {
  DOMString error;
  PaymentCurrencyAmount total;
  sequence<PaymentDetailsModifier> modifiers;
  sequence<PaymentShippingOption> shippingOptions;
  object paymentMethodErrors;
  AddressErrors shippingAddressErrors;
};

[Exposed=ServiceWorker]
interface PaymentRequestEvent : ExtendableEvent {
  constructor(DOMString type, optional PaymentRequestEventInit eventInitDict = {});
  readonly attribute USVString topOrigin;
  readonly attribute USVString paymentRequestOrigin;
  readonly attribute DOMString paymentRequestId;
  readonly attribute FrozenArray<PaymentMethodData> methodData;
  readonly attribute object total;
  readonly attribute FrozenArray<PaymentDetailsModifier> modifiers;
  readonly attribute object? paymentOptions;
  readonly attribute FrozenArray<PaymentShippingOption>? shippingOptions;
  Promise<WindowClient?> openWindow(USVString url);
  Promise<PaymentRequestDetailsUpdate?> changePaymentMethod(DOMString methodName, optional object? methodDetails = null);
  Promise<PaymentRequestDetailsUpdate?> changeShippingAddress(optional AddressInit shippingAddress = {});
  Promise<PaymentRequestDetailsUpdate?> changeShippingOption(DOMString shippingOption);
  undefined respondWith(Promise<PaymentHandlerResponse> handlerResponsePromise);
};

dictionary PaymentRequestEventInit : ExtendableEventInit {
  USVString topOrigin;
  USVString paymentRequestOrigin;
  DOMString paymentRequestId;
  sequence<PaymentMethodData> methodData;
  PaymentCurrencyAmount total;
  sequence<PaymentDetailsModifier> modifiers;
  PaymentOptions paymentOptions;
  sequence<PaymentShippingOption> shippingOptions;
};

dictionary PaymentHandlerResponse {
DOMString methodName;
object details;
DOMString? payerName;
DOMString? payerEmail;
DOMString? payerPhone;
AddressInit shippingAddress;
DOMString? shippingOption;
};

B. 参考文献

B.1 规范性参考文献

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. Ecma International. URL: https://tc39.es/ecma262/multipage/
[HTML]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[payment-method-id]
Payment Method Identifiers. Marcos Caceres. W3C. 8 September 2022. W3C Recommendation. URL: https://www.w3.org/TR/payment-method-id/
[payment-method-manifest]
Payment Method Manifest. Dapeng(Max) Liu; Domenic Denicola; Zach Koch. W3C. 12 December 2017. FPWD. URL: https://www.w3.org/TR/payment-method-manifest/
[payment-request]
Payment Request API. Marcos Caceres; Rouslan Solomakhin; Ian Jacobs. W3C. 15 August 2025. CRD. URL: https://www.w3.org/TR/payment-request/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[SERVICE-WORKERS]
Service Workers. Yoshisato Yanagisawa; Monica CHINTALA. W3C. 6 March 2025. CRD. URL: https://www.w3.org/TR/service-workers/
[URL]
URL Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

B.2 参考资料

[WebCryptoAPI]
Web Cryptography API. Mark Watson. W3C. 26 January 2017. W3C Recommendation. URL: https://www.w3.org/TR/WebCryptoAPI/