使用 Service Worker 处理可选的付款信息

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

基于网络的付款应用收到付款请求并发起付款交易后,服务工件将充当商家与付款应用之间的通信枢纽。本文将介绍付款应用如何使用服务工件将付款方式、送货地址或联系信息传递给商家。

使用 Service Worker 处理可选付款信息
使用服务工处理可选付款信息

告知商家付款方式变更

付款应用可以支持多种采用不同付款方式的付款工具。

客户 付款方式 付款方式
A 信用卡发卡机构 1 ****1234
信用卡发卡机构 1 ****4242
银行 X ******123
B 信用卡发卡机构 2 ****5678
银行 X ******456

例如,在上表中,客户 A 的基于 Web 的钱包中注册了两张信用卡和一个银行账户。在本例中,应用会处理三种付款工具(****1234****4242******123)和两种付款方式(信用卡发卡机构 1 和银行 X)。在付款交易中,付款应用可以让客户选择一种付款工具,并使用该工具向商家付款。

付款方式选择器界面
付款方式选择器界面

付款应用可以在发送完整付款响应之前告知商家客户选择了哪种付款方式。例如,当商家想要为特定付款方式品牌投放折扣广告系列时,此属性非常有用。

借助 Payment Handler API,付款应用可以通过服务工件向商家发送“付款方式更改”事件,以通知新的付款方式标识符。Service Worker 应使用新的付款方式信息调用 PaymentRequestEvent.changePaymentMethod()

告知商家付款方式变更
告知商家付款方式变更

付款应用可以将 methodDetails 对象作为 PaymentRequestEvent.changePaymentMethod() 的可选第二个参数传递。此对象可以包含商家处理更改事件所需的任意付款方式详细信息。

[payment handler] service-worker.js

…
// Received a message from the frontend
self.addEventListener('message', async e => {
  let details;
  try {
    switch (e.data.type) {
      …
      case 'PAYMENT_METHOD_CHANGED':
        const newMethod = e.data.paymentMethod;
        const newDetails = e.data.methodDetails;
        // Redact or check that no sensitive information is passed in
        // `newDetails`.
        // Notify the merchant of the payment method change
        details =
          await payment_request_event.changePaymentMethod(newMethod, newDetails);
      …

当商家从 Payment Request API 收到 paymentmethodchange 事件时,可以更新付款详情并使用 PaymentDetailsUpdate 对象进行响应。

[merchant]

request.addEventListener('paymentmethodchange', e => {
  if (e.methodName === 'another-pay') {
    // Apply $10 discount for example.
    const discount = {
      label: 'special discount',
      amount: {
        currency: 'USD',
        // The value being string complies the spec
        value: '-10.00'
      }
    };
    let total = 0;
    details.displayItems.push(discount);
    for (let item of details.displayItems) {
     total += parseFloat(item.amount.value);
    }
    // Convert the number back to string
    details.total.amount.value = total.toString();
  }
  // Pass a promise to `updateWith()` and send updated payment details
  e.updateWith(details);
});

当商家做出回复时,PaymentRequestEvent.changePaymentMethod() 返回的 promise 将通过 PaymentRequestDetailsUpdate 对象解析。

[payment handler] service-worker.js

…
        // Notify the merchant of the payment method change
        details = await payment_request_event.changePaymentMethod(newMethod, newDetails);
        // Provided the new payment details,
        // send a message back to the frontend to update the UI
        postMessage('UPDATE_REQUEST', details);
        break;
…

使用该对象更新前端的界面。请参阅反映更新后的付款信息

告知商家送货地址变更

付款应用可以在付款交易中向商家提供客户的送货地址。

这对商家而言非常有用,因为他们可以将地址收集工作委托给付款应用。此外,由于地址数据将以标准数据格式提供,因此商家可以预计收到结构一致的送货地址。

此外,客户可以通过首选付款应用注册地址信息,并在不同的商家处重复使用这些信息。

送货地址选择器界面
送货地址选择器界面

付款应用可以提供界面,以便在付款交易中修改送货地址或为客户选择预注册的地址信息。临时确定送货地址后,付款应用可以告知商家隐去部分信息的地址信息。这为商家带来了多项益处:

  • 商家可以确定客户是否符合配送商品的地区性限制(例如,仅限国内配送)。
  • 商家可以根据收货地址所在的区域更改配送方式列表(例如,国际经济配送或快递)。
  • 商家可以根据地址应用新的运费并更新总价。

借助 Payment Handler API,付款应用可以通过服务工件向商家发送“送货地址更改”事件,以通知新的送货地址。服务工件应使用新地址对象调用 PaymentRequestEvent.changeShippingAddress()

告知商家送货地址变更
告知商家送货地址变更

[payment handler] service-worker.js

...
// Received a message from the frontend
self.addEventListener('message', async e => {
  let details;
  try {
    switch (e.data.type) {
      …
      case 'SHIPPING_ADDRESS_CHANGED':
        const newAddress = e.data.shippingAddress;
        details =
          await payment_request_event.changeShippingAddress(newAddress);
      …

商家将从 PaymentRequest API 收到 shippingaddresschange 事件,以便使用更新后的 PaymentDetailsUpdate 进行响应。

[merchant]

request.addEventListener('shippingaddresschange', e => {
  // Read the updated shipping address and update the request.
  const addr = request.shippingAddress;
  const details = getPaymentDetailsFromShippingAddress(addr);
  // `updateWith()` sends back updated payment details
  e.updateWith(details);
});

当商家做出回复时,返回的 promise PaymentRequestEvent.changeShippingAddress() 将解析为 PaymentRequestDetailsUpdate 对象。

[payment handler] service-worker.js

…
        // Notify the merchant of the shipping address change
        details = await payment_request_event.changeShippingAddress(newAddress);
        // Provided the new payment details,
        // send a message back to the frontend to update the UI
        postMessage('UPDATE_REQUEST', details);
        break;
…

使用该对象更新前端的界面。请参阅反映更新后的付款信息

告知商家送货方式发生变化

配送选项是商家将购买的商品寄送给客户时所使用的配送方式。常见的配送方式包括:

  • 免运费
  • 极速配送
  • 全球送货
  • 优质国际配送

每种方法都有各自的成本。通常,速度更快的方法/选项的费用更高。

使用 Payment Request API 的商家可以将此选择委托给付款应用。付款应用可以使用这些信息构建界面,让客户选择配送方式。

配送方式选择器界面
配送方式选择器界面

商家在 Payment Request API 中指定的配送方式列表会作为 PaymentRequestEvent 的属性传播到付款应用的服务工件。

[merchant]

const request = new PaymentRequest([{
  supportedMethods: 'https://bobbucks.dev/pay',
  data: { transactionId: '****' }
}], {
  displayItems: [{
    label: 'Anvil L/S Crew Neck - Grey M x1',
    amount: { currency: 'USD', value: '22.15' }
  }],
  shippingOptions: [{
    id: 'standard',
    label: 'Standard',
    amount: { value: '0.00', currency: 'USD' },
    selected: true
  }, {
    id: 'express',
    label: 'Express',
    amount: { value: '5.00', currency: 'USD' }
  }],
  total: {
    label: 'Total due',
    amount: { currency: 'USD', value : '22.15' }
  }
}, {  requestShipping: true });

付款应用可以让商家知道客户选择了哪种配送方式。这对商家和客户而言都很重要,因为更改配送方式也会更改总价格。商家需要获知最新价格,以便日后进行付款验证,客户也需要知道价格变动。

借助 Payment Handler API,付款应用可以通过服务工件向商家发送“配送方式更改”事件。服务工作器应使用新的配送方式 ID 调用 PaymentRequestEvent.changeShippingOption()

告知商家送货方式发生变化
告知商家配送方式发生了变化

[payment handler] service-worker.js

…
// Received a message from the frontend
self.addEventListener('message', async e => {
  let details;
  try {
    switch (e.data.type) {
      …
      case 'SHIPPING_OPTION_CHANGED':
        const newOption = e.data.shippingOptionId;
        details =
          await payment_request_event.changeShippingOption(newOption);
      …

商家将从 Payment Request API 收到 shippingoptionchange 事件。商家应使用这些信息更新总价,然后使用更新后的 PaymentDetailsUpdate 做出回复。

[merchant]

request.addEventListener('shippingoptionchange', e => {
  // selected shipping option
  const shippingOption = request.shippingOption;
  const newTotal = {
    currency: 'USD',
    label: 'Total due',
    value: calculateNewTotal(shippingOption),
  };
  // `updateWith()` sends back updated payment details
  e.updateWith({ total: newTotal });
});

当商家做出回复时,PaymentRequestEvent.changeShippingOption() 返回的 promise 将解析为 PaymentRequestDetailsUpdate 对象。

[payment handler] service-worker.js

…
        // Notify the merchant of the shipping option change
        details = await payment_request_event.changeShippingOption(newOption);
        // Provided the new payment details,
        // send a message back to the frontend to update the UI
        postMessage('UPDATE_REQUEST', details);
        break;
…

使用该对象更新前端的界面。请参阅反映更新后的付款信息

反映更新后的付款详情

商家更新付款详情后,从 .changePaymentMethod().changeShippingAddress().changeShippingOption() 返回的 Promise 将使用一个通用的 PaymentRequestDetailsUpdate 对象解析。付款处理脚本可以使用该结果将更新后的总价格和配送选项反映到界面中。

商家可能会出于以下几种原因返回错误:

  • 付款方式不符合要求。
  • 送货地址不在其支持的地区内。
  • 送货地址包含无效信息。
  • 配送选项因所提供的送货地址或其他原因而不可选。

使用以下属性来反映错误状态:

  • error:直观易懂的错误字符串。这是向客户显示的最佳字符串。
  • shippingAddressErrorsAddressErrors 对象,其中包含每个地址属性的详细错误字符串。如果您想打开一个表单,让客户修改地址,并且需要直接将客户引导至无效字段,这项功能会很有用。
  • paymentMethodErrors:特定于付款方式的错误对象。您可以要求商家提供结构化错误,但 Web Payments 规范作者建议将其保持为简单字符串。

示例代码

本文档中显示的大多数示例代码均摘自以下实用示例应用:

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. 按该字段旁边的付款按钮。