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

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

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

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

通知商家付款方式变更

付款应用可支持多种付款方式和不同的付款方式。

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

例如,在上表中,客户 A 的网络钱包有两张信用卡和一家银行账户。在本例中,应用处理的是三种付款方式(****1234****4242******123)和两种付款方式(信用卡发卡机构 1 和银行 X)。在付款交易中,付款应用可以让客户选择其中一种付款方式,并使用该付款方式向商家付款。

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

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

借助 Payment Handler API,付款应用可以通过 Service Worker 向商家发送“付款方式更改”事件,以通知新的付款方式标识符。Service Worker 应使用新的付款方式信息调用 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 对象进行响应。

[商家]

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,付款应用可通过 Service Worker 向商家发送“送货地址更改”事件,以通知新的送货地址。Service Worker 应使用新地址对象调用 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);
      …

商家将收到来自 Payment Request API 的 shippingaddresschange 事件,以便能使用更新后的 PaymentDetailsUpdate 进行响应。

[商家]

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

[商家]

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()

通知商家配送方式变更
通知商家配送方式变更

[付款处理程序] 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 进行响应。

[商家]

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:付款方式特有的错误对象。您可以要求商家提供结构化错误,但网站付款规范的作者建议保留简单的字符串。

示例代码

您在本文档中看到的大多数示例代码摘录自以下正在运行的示例应用:

https://paymenthandler-demo.glitch.me

[付款处理程序] Service Worker

[付款处理程序] 前端

若要试用,请执行以下操作:

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