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

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

当基于 Web 的支付应用收到付款请求并启动付款交易后,Service Worker 将充当商家与支付应用之间的通信中心。本文将介绍支付应用如何使用 Service Worker 将支付方式、送货地址或联系信息传递给商家。

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

告知商家

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

支付方式变更

支付应用可以支持多种支付方式,每种支付方式又可以支持多种支付工具。

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

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

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

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

借助基于 Web 的 Payment Handler API,支付应用可以通过 Service Worker 向商家发送“支付方式更改”事件,以通知新的支付方式标识符。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;
…

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

送货地址变更

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

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

此外,客户可以在其首选支付应用中注册地址信息,并在不同的商家中重复使用。

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

支付应用可以提供一个界面,供客户在支付交易中修改送货地址或选择预注册的地址信息。当送货地址被临时确定后,支付应用可以告知商家经过隐去处理的地址信息。这为商家带来了多重好处:

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

借助基于 Web 的 Payment Handler API,支付应用可以从 Service Worker 向商家发送“送货地址更改”事件,以通知新的送货地址。Service Worker 应使用新地址对象调用 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);
      …

Key-term: Redacted address. 在这种情况下,向商家告知完整送货地址是不必要的,并且会给客户的隐私带来风险。商家只会收到他们确定运费所需的那部分地址信息。具体而言,浏览器会先清除支付应用提供的地址中的 organization、phone、recipient、addressLine 字段,然后在商家的 DOM 中引发 shippingaddresschange 事件。

商家将收到来自 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 对象进行解析。

[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 的属性传播到支付应用的 Service Worker。

[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,支付应用可以从 Service Worker 向商家发送“配送选项更改”事件。Service Worker 应使用新的配送选项 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:人类可读的错误字符串。这是向客户显示的最好字符串。
  • shippingAddressErrors:AddressErrors 对象,包含每个地址属性的详细错误字符串。如果您想打开一个表单,让客户修改其地址,并且需要直接将他们引导至无效字段,则此功能非常有用。
  • paymentMethodErrors:特定于支付方式的错误对象。您可以要求商家提供结构化错误,但 Web Payments 规范作者建议将其保留为简单字符串。