使用 Service Worker 编排付款事务

如何将您的基于 Web 的付款应用改为适用于 Web Payments,并为客户提供更好的用户体验。

注册付款应用后,您就可以接受商家的付款请求了。本文介绍了如何在运行时(即显示窗口且用户与其互动时)通过服务工件协调付款交易。

使用 Service Worker 编排付款交易
使用服务工件编排付款交易

“运行时付款参数更改”是指一组事件,可让商家和付款处理方在用户与付款处理方互动时交换消息。如需了解详情,请参阅使用服务工处理可选付款信息

接收来自商家的付款请求事件

当客户选择使用您的基于 Web 的付款应用付款,并且商家调用了 PaymentRequest.show() 时,您的服务工件将收到 paymentrequest 事件。向服务工作线程添加事件监听器,以捕获事件并准备执行后续操作。

[付款处理脚本] service-worker.js:


let payment_request_event
;
let resolver
;
let client
;

// `self` is the global object in service worker
self
.addEventListener('paymentrequest', async e => {
 
if (payment_request_event) {
   
// If there's an ongoing payment transaction, reject it.
    resolver
.reject();
 
}
 
// Preserve the event for future use
  payment_request_event
= e;

保留的 PaymentRequestEvent 包含有关此事务的重要信息:

属性名称 说明
topOrigin 一个字符串,表示顶级网页的来源(通常是收款商家)。用于识别商家来源。
paymentRequestOrigin 指示调用方来源的字符串。如果商家直接调用 Payment Request API,此值可以与 topOrigin 相同,但如果第三方(例如付款网关)从 iframe 中调用该 API,则此值可能会有所不同。
paymentRequestId 提供给 Payment Request API 的 PaymentDetailsInitid 属性。如果商家省略此参数,浏览器将提供自动生成的 ID。
methodData 商家在 PaymentMethodData 中提供的特定于付款方式的数据。用于确定付款交易详情。
total 商家在 PaymentDetailsInit 中提供的总金额。使用此方法构建界面,以便客户了解应付总金额。
instrumentKey 用户选择的插桩键。这反映了您预先提供的 instrumentKey。空字符串表示用户未指定任何仪器。

打开付款处理程序窗口以显示基于 Web 的付款应用前端

收到 paymentrequest 事件后,付款应用可以通过调用 PaymentRequestEvent.openWindow() 来打开付款处理程序窗口。付款处理程序窗口会向客户显示您的付款应用界面,客户可以在其中进行身份验证、选择送货地址和选项,以及授权付款。我们将在在付款前端处理付款(即将推出)中介绍如何编写前端代码。

使用基于 Web 的付款应用的结账流程。

将保留的 promise 传递给 PaymentRequestEvent.respondWith(),以便您日后使用付款结果解析它。

[付款处理脚本] service-worker.js:


self
.addEventListener('paymentrequest', async e => {

 
// Retain a promise for future resolution
 
// Polyfill for PromiseResolver is provided below.
  resolver
= new PromiseResolver();

 
// Pass a promise that resolves when payment is done.
  e
.respondWith(resolver.promise);
 
// Open the checkout page.
 
try {
   
// Open the window and preserve the client
    client
= await e.openWindow(checkoutURL);
   
if (!client) {
     
// Reject if the window fails to open
     
throw 'Failed to open window';
   
}
 
} catch (err) {
   
// Reject the promise on failure
    resolver
.reject(err);
 
};
});

您可以使用方便的 PromiseResolver polyfill 在任意时间解析 promise。

class PromiseResolver {
  constructor
() {
   
this.promise_ = new Promise((resolve, reject) => {
     
this.resolve_ = resolve;
     
this.reject_ = reject;
   
})
 
}
 
get promise() { return this.promise_ }
 
get resolve() { return this.resolve_ }
 
get reject() { return this.reject_ }
}

与前端交换信息

付款应用的服务工件可以通过 ServiceWorkerController.postMessage() 与付款应用的前端交换消息。如需从前端接收消息,请监听 message 事件。

[付款处理脚本] service-worker.js:

// Define a convenient `postMessage()` method
const postMessage = (type, contents = {}) => {
 
if (client) client.postMessage({ type, ...contents });
}

从前端接收准备就绪信号

付款处理程序窗口打开后,服务工作器应等待付款应用前端发送的准备就绪状态信号。服务工件准备就绪后,可以将重要信息传递给前端。

[payment handler] frontend:

navigator.serviceWorker.controller.postMessage({
  type
: 'WINDOW_IS_READY'
});

[付款处理脚本] service-worker.js:


// Received a message from the frontend
self
.addEventListener('message', async e => {
  let details
;
 
try {
   
switch (e.data.type) {
     
// `WINDOW_IS_READY` is a frontend's ready state signal
     
case 'WINDOW_IS_READY':
       
const { total } = payment_request_event;

将交易详情传递给前端

现在,请将付款详情发回给我们。在这种情况下,您只需发送付款请求的总金额,但您也可以根据需要传递更多详细信息。

[付款处理脚本] service-worker.js:


       
// Pass the payment details to the frontend
        postMessage
('PAYMENT_IS_READY', { total });
       
break;

[payment handler] frontend:

let total;

navigator
.serviceWorker.addEventListener('message', async e => {
 
switch (e.data.type) {
     
case 'PAYMENT_IS_READY':
       
({ total } = e.data);
       
// Update the UI
        renderHTML
(total);
       
break;

返回客户的付款凭据

当客户授权付款后,前端可以向服务工作器发送一条后置消息以继续操作。您可以解析传递给 PaymentRequestEvent.respondWith() 的 promise,以将结果发送回商家。传递 PaymentHandlerResponse 对象。

属性名称 说明
methodName 用于付款的付款方式标识符。
details 特定于付款方式的数据,可为商家提供处理付款所需的信息。

[payment handler] frontend:

  const paymentMethod = 

  postMessage
('PAYMENT_AUTHORIZED', {
    paymentMethod
,              // Payment method identifier
 
});

[付款处理脚本] service-worker.js:


// Received a message from the frontend
self
.addEventListener('message', async e => {
  let details
;
 
try {
   
switch (e.data.type) {
     

     
case 'PAYMENT_AUTHORIZED':
       
// Resolve the payment request event promise
       
// with a payment response object
       
const response = {
          methodName
: e.data.paymentMethod,
          details
: { id: 'put payment credential here' },
       
}
        resolver
.resolve(response);
       
// Don't forget to initialize.
        payment_request_event
= null;
       
break;
     

取消付款交易

为了允许客户取消交易,前端可以向服务工件发送消息以执行此操作。然后,服务工件可以使用 null 解析传递给 PaymentRequestEvent.respondWith() 的 promise,以向商家表明交易已取消。

[payment handler] frontend:

  postMessage('CANCEL_PAYMENT');

[付款处理脚本] service-worker.js:


// Received a message from the frontend
self
.addEventListener('message', async e => {
  let details
;
 
try {
   
switch (e.data.type) {
     

     
case 'CANCEL_PAYMENT':
       
// Resolve the payment request event promise
       
// with null
        resolver
.resolve(null);
       
// Don't forget to initialize.
        payment_request_event
= null;
       
break;
     

示例代码

本文档中显示的所有示例代码均摘自以下可运行的示例应用:

https://paymenthandler-demo.glitch.me

[payment handler] service worker

[payment handler] frontend

如需试用,请执行以下操作:

  1. 前往 https://paymentrequest-demo.glitch.me/
  2. 前往页面底部。
  3. 添加付款方式按钮。
  4. 付款方式标识符字段中输入 https://paymenthandler-demo.glitch.me
  5. 按该字段旁边的付款按钮。

后续步骤

在本文中,我们学习了如何通过服务工作线程协调付款交易。下一步是了解如何向 Service Worker 添加一些更高级的功能。