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

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

一旦基于 Web 的支付应用收到付款请求并启动付款交易,服务工作线程就会充当商家与支付应用之间通信的枢纽。本文将介绍支付应用如何使用服务工作线程将付款方式、送货地址或联系信息传递给商家。

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

通知商家

请务必告知商家以下变更。

付款方式更改

支付应用可以支持多种支付工具,并提供不同的支付方式。

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

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

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

付款应用可以在发送完整付款响应之前,让商家知道客户选择了哪种付款方式。当商家想要针对特定付款方式品牌开展折扣广告系列时,此功能非常有用。

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

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

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

[付款处理程序] 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 对象。

[付款处理程序] 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()

告知商家送货地址已更改
告知商家送货地址已更改

[付款处理程序] 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);
      

关键术语:已遮盖的地址。在这种情况下,向商家提供完整的送货地址是不必要的,并且会给客户的隐私带来风险。商家只会收到确定运费所需的地址信息。具体而言,浏览器会在商家 DOM 中引发 shippingaddresschange 事件之前,从付款应用提供的地址中清除 organizationphonerecipientaddressLine 字段。

商家将从 Payment Request 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 对象。

[付款处理程序] 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()

通知商家送货选项变更
通知商家配送选项已更改

[付款处理程序] 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 对象。

[付款处理程序] 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 规范作者建议将其保留为简单的字符串。

示例代码

您在本文档中看到的大部分示例代码都摘自一个示例应用。

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

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