與服務工作人員協調付款交易

如何將網頁式付款應用程式調整為網路付款模式,並為消費者提供更優質的使用者體驗。

付款應用程式註冊後,即可開始接受商家的付款要求。本文說明如何在執行階段 (例如顯示視窗和使用者進行互動) 從 Service Worker 協調付款交易。

透過服務工作人員協調付款交易
透過 Service Worker 自動化調度管理付款交易

「執行階段付款參數變更」是指一組事件,可讓商家和付款處理常式在使用者與付款處理常式互動時交換訊息。詳情請參閱使用 Service Worker 處理選用付款資訊

接收商家的付款要求事件

顧客選擇透過您網頁式付款應用程式付款,並叫用 PaymentRequest.show() 時,您的服務工作站會收到 paymentrequest 事件。將事件監聽器新增至 Service Worker,以擷取事件並為下一個動作做好準備。

[付款處理常式] 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 的 PaymentDetailsInit id 屬性。如果商家省略這項資訊,瀏覽器將自動產生一組 ID。
methodData 商家在 PaymentMethodData 中提供的付款方式專屬資料。 你可以使用這個欄位來判斷付款交易明細。
total 商家在 PaymentDetailsInit 中提供的總金額。 使用此方法建構 UI,讓客戶瞭解須支付的金額。
instrumentKey 使用者選取的付款方式金鑰。這反映了您先前提供的 instrumentKey。空字串表示使用者未指定任何檢測方式。

開啟付款處理常式視窗,顯示網頁式付款應用程式前端

收到 paymentrequest 事件時,付款應用程式可以呼叫 PaymentRequestEvent.openWindow() 來開啟付款處理常式視窗。付款處理常式視窗會顯示付款應用程式的介面,客戶可以在其中進行驗證、選擇運送地址和選項,以及授權付款。我們會在「處理付款前端的付款前端」一文中說明如何編寫前端程式碼 (即將推出)。

網頁式付款應用程式的結帳流程。

將保留的承諾傳遞至 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 隨時隨地解決承諾。

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 });
}

接收來自前端的就緒信號

付款處理常式視窗開啟後,服務工作站應等待來自付款應用程式前端的就緒狀態信號。服務工作站可以在準備就緒時,將重要資訊傳送至前端。

[付款處理常式] 前端:

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;
…

[付款處理常式] 前端:

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() 的承諾,將結果傳回給商家。傳遞 PaymentHandlerResponse 物件。

資源名稱 說明
methodName 付款時使用的付款方式 ID。
details 付款方式專屬資料,可提供商家處理付款的必要資訊。

[付款處理常式] 前端:

  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() 的承諾,向商家表明交易已取消。

[付款處理常式] 前端:

  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

[付款處理常式] Service Worker

[付款處理常式] 前端

試用方法如下:

  1. 前往 https://paymentrequest-demo.glitch.me/
  2. 瀏覽至網頁底部。
  3. 按下「新增付款方式」按鈕
  4. 在「付款方式 ID」欄位中輸入 https://paymenthandler-demo.glitch.me
  5. 按一下欄位旁邊的「付款」按鈕

後續步驟

在本文中,我們學到如何從服務工作站自動化調度管理付款交易。下一步是瞭解如何在 Service Worker 中新增一些進階功能。