使用 Payment Request API

Payment Request API” 提供了一種基於瀏覽器的、將使用者及其首選支付系統和平臺連線到他們想要為商品和服務付費的商家的方法。本文件將引導你使用“Payment Request API”,並提供示例和建議的最佳實踐。

支付的基本流程

本節詳細介紹使用 Payment Request API 進行支付的基本流程。

注意: 本節中的程式碼片段來自我們的功能檢測支援演示

建立新的支付請求物件

支付請求始於建立一個新的 PaymentRequest 物件 — 使用 PaymentRequest() 建構函式。它需要兩個必需引數和一個可選引數:

  • methodData — 包含有關支付提供商的資訊的物件,例如支援的支付方式等。
  • details — 包含有關具體支付的資訊的物件,例如總支付金額、稅費、運費等。
  • options (可選) — 包含與支付相關的其他選項的物件。

例如,你可以像這樣建立一個新的 PaymentRequest 例項:

js
const request = new PaymentRequest(
  buildSupportedPaymentMethodData(),
  buildShoppingCartDetails(),
);

建構函式內部呼叫的函式會返回所需的引數物件。

js
function buildSupportedPaymentMethodData() {
  // Example supported payment methods:
  return [{ supportedMethods: "https://example.com/pay" }];
}

function buildShoppingCartDetails() {
  // Hardcoded for demo purposes:
  return {
    id: "order-123",
    displayItems: [
      {
        label: "Example item",
        amount: { currency: "USD", value: "1.00" },
      },
    ],
    total: {
      label: "Total",
      amount: { currency: "USD", value: "1.00" },
    },
  };
}

啟動支付流程

建立 PaymentRequest 物件後,在其上呼叫 PaymentRequest.show() 方法以發起支付請求。如果支付成功,它將返回一個解析為 PaymentResponse 物件的 Promise。

js
request.show().then((paymentResponse) => {
  // Here we would process the payment. For this demo, simulate immediate success:
  paymentResponse.complete("success").then(() => {
    // For demo purposes:
    introPanel.style.display = "none";
    successPanel.style.display = "block";
  });
});

此物件為開發者提供了訪問詳細資訊的許可權,可用於完成支付完成後的邏輯步驟,例如客戶的電子郵件地址、發貨地址等。在上面的程式碼中,你會看到我們呼叫了 PaymentResponse.complete() 方法來表示互動已完成 — 你可以使用它來執行收尾步驟,例如更新使用者介面告知使用者交易已完成等。

其他有用的支付請求方法

還有一些其他值得了解的有用支付請求方法。

PaymentRequest.canMakePayment() 可用於在開始支付流程之前檢查 PaymentRequest 物件是否能夠進行支付。它返回一個解析為布林值的 Promise,指示是否可以進行支付,例如:

js
// Dummy payment request to check whether payment can be made
new PaymentRequest(buildSupportedPaymentMethodData(), {
  total: { label: "Stub", amount: { currency: "USD", value: "0.01" } },
})
  .canMakePayment()
  .then((result) => {
    if (result) {
      // Real payment request
      const request = new PaymentRequest(
        buildSupportedPaymentMethodData(),
        checkoutObject,
      );
      request.show().then((paymentResponse) => {
        // Here we would process the payment.
        paymentResponse.complete("success").then(() => {
          // Finish handling payment
        });
      });
    }
  });

PaymentRequest.abort() 可用於在需要時中止支付請求。

檢測 Payment Request API 的可用性

你可以透過檢查使用者瀏覽器是否支援 PaymentRequest 來有效地檢測 Payment Request API 的支援情況,即 if (window.PaymentRequest)

在下面的程式碼片段中,商家頁面執行了此檢查,如果返回 true,則將結賬按鈕更新為使用 PaymentRequest 而非傳統的 Web 表單。

js
const checkoutButton = document.getElementById("checkout-button");
if (window.PaymentRequest) {
  let request = new PaymentRequest(
    buildSupportedPaymentMethodNames(),
    buildShoppingCartDetails(),
  );
  checkoutButton.addEventListener("click", () => {
    request
      .show()
      .then((paymentResponse) => {
        // Handle successful payment
      })
      .catch((error) => {
        // Handle cancelled or failed payment. For example, redirect to
        // the legacy web form checkout:
        window.location.href = "/legacy-web-form-checkout";
      });

    // Every click on the checkout button should use a new instance of
    // PaymentRequest object, because PaymentRequest.show() can be
    // called only once per instance.
    request = new PaymentRequest(
      buildSupportedPaymentMethodNames(),
      buildShoppingCartDetails(),
    );
  });
}

注意: 有關完整程式碼,請參閱我們的功能檢測支援演示

檢查使用者是否可以進行支付

檢查使用者是否可以進行支付始終是有用的。以下是幾種相關技術。

自定義支付按鈕

一個有用的技術是根據使用者是否可以進行支付來定製支付請求按鈕。

在下面的程式碼片段中,我們正是這樣做的 — 根據使用者是能夠進行快速結賬還是需要先新增支付憑據,結賬按鈕的標題會在“使用 W3C 快速結賬”和“設定 W3C 結賬”之間切換。在這兩種情況下,結賬按鈕都會呼叫 PaymentRequest.show()

js
const checkoutButton = document.getElementById("checkout-button");
checkoutButton.innerText = "Loading…";
if (window.PaymentRequest) {
  const request = new PaymentRequest(
    buildSupportedPaymentMethodNames(),
    buildShoppingCartDetails(),
  );
  request
    .canMakePayment()
    .then((canMakeAFastPayment) => {
      checkoutButton.textContent = canMakeAFastPayment
        ? "Fast Checkout with W3C"
        : "Setup W3C Checkout";
    })
    .catch((error) => {
      // The user may have turned off the querying functionality in their
      // privacy settings. The website does not know whether they can make
      // a fast payment, so pick a generic title.
      checkoutButton.textContent = "Checkout with W3C";
    });
}

注意: 有關完整程式碼,請參閱我們的自定義支付按鈕演示

在所有價格已知之前進行檢查

如果結賬流程需要在所有行專案及其價格都已知之前就知道 PaymentRequest.canMakePayment() 是否會返回 true,則可以使用虛擬資料例項化 PaymentRequest 並預先查詢 .canMakePayment()。如果你多次呼叫 .canMakePayment(),請記住 PaymentRequest 建構函式的第一個引數應包含相同的支付方式名稱和資料。

js
// The page has loaded. Should the page use PaymentRequest?
// If PaymentRequest fails, should the page fallback to manual
// web form checkout?
const supportedPaymentMethods = [
  /* supported methods */
];

let shouldCallPaymentRequest = true;
let fallbackToLegacyOnPaymentRequestFailure = false;
new PaymentRequest(supportedPaymentMethods, {
  total: { label: "Stub", amount: { currency: "USD", value: "0.01" } },
})
  .canMakePayment()
  .then((result) => {
    shouldCallPaymentRequest = result;
  })
  .catch((error) => {
    console.error(error);

    // The user may have turned off query ability in their privacy settings.
    // Let's use PaymentRequest by default and fallback to legacy
    // web form based checkout.
    shouldCallPaymentRequest = true;
    fallbackToLegacyOnPaymentRequestFailure = true;
  });

// User has clicked on the checkout button. We know
// what's in the cart, but we don't have a `Checkout` object.
function onCheckoutButtonClicked(lineItems) {
  callServerToRetrieveCheckoutDetails(lineItems);
}

// The server has constructed the `Checkout` object. Now we know
// all of the prices and shipping options.
function onServerCheckoutDetailsRetrieved(checkoutObject) {
  if (shouldCallPaymentRequest) {
    const request = new PaymentRequest(supportedPaymentMethods, checkoutObject);
    request
      .show()
      .then((paymentResponse) => {
        // Post the results to the server and call `paymentResponse.complete()`.
      })
      .catch((error) => {
        console.error(error);
        if (fallbackToLegacyOnPaymentRequestFailure) {
          window.location.href = "/legacy-web-form-checkout";
        } else {
          showCheckoutErrorToUser();
        }
      });
  } else {
    window.location.href = "/legacy-web-form-checkout";
  }
}

注意: 有關完整程式碼,請參閱我們的在價格已知前檢查使用者是否可以進行支付演示

當用戶沒有支付應用時,推薦支付應用

如果你在此商家頁面上選擇使用 BobPay 演示支付提供商進行支付,它會嘗試呼叫 PaymentRequest.show(),同時攔截 NotSupportedError DOMException。如果該支付方式不受支援,它將重定向到 BobPay 的註冊頁面。

程式碼看起來大致是這樣的:

js
checkoutButton.addEventListener("click", () => {
  const request = new PaymentRequest(
    buildSupportedPaymentMethodData(),
    buildShoppingCartDetails(),
  );
  request
    .show()
    .then((paymentResponse) => {
      // Here we would process the payment. For this demo, simulate immediate success:
      paymentResponse.complete("success").then(() => {
        // For demo purposes:
        introPanel.style.display = "none";
        successPanel.style.display = "block";
      });
    })
    .catch((error) => {
      if (error.name === "NotSupportedError") {
        window.location.href = "https://bobpay.xyz/#download";
      } else {
        // Other kinds of errors; cancelled or failed payment. For demo purposes:
        introPanel.style.display = "none";
        legacyPanel.style.display = "block";
      }
    });
});

注意: 有關完整程式碼,請參閱我們的當用戶沒有應用時推薦支付應用演示

支付成功後顯示額外的使用者介面

如果商家希望收集 API 未包含的額外資訊(例如,額外的配送說明),商家可以在結賬後顯示一個包含額外 <input type="text"> 欄位的頁面。

js
request
  .show()
  .then((paymentResponse) => paymentResponse.complete("success"))
  .then(() => {
    // Process payment here.
    // Close the UI:
    // Request additional shipping address details.
    const additionalDetailsContainer = document.getElementById(
      "additional-details-container",
    );
    additionalDetailsContainer.style.display = "block";
    window.scrollTo(additionalDetailsContainer.getBoundingClientRect().x, 0);
  })
  .catch((error) => {
    // Handle error.
  });

注意: 有關完整程式碼,請參閱我們的支付成功後顯示額外使用者介面演示

預授權交易

某些用例(例如,在服務站支付燃油費)涉及預授權支付。一種方法是透過支付處理程式(請參閱Payment Handler API)。在撰寫本文時,該規範包含一個 canmakepayment 事件,支付處理程式可以利用該事件返回授權狀態。

商家程式碼應如下所示:

js
const paymentRequest = new PaymentRequest(
  [{ supportedMethods: "https://example.com/preauth" }],
  details,
);

// Send `CanMakePayment` event to the payment handler.
paymentRequest
  .canMakePayment()
  .then((res) => {
    if (res) {
      // The payment handler has pre-authorized a transaction
      // with some static amount, e.g., USD $1.00.
    } else {
      // Pre-authorization failed or payment handler not installed.
    }
  })
  .catch((err) => {
    // Unexpected error occurred.
  });

支付處理程式將包含以下程式碼:

js
self.addEventListener("canmakepayment", (evt) => {
  // Pre-authorize here.
  const preAuthSuccess = true;
  evt.respondWith(preAuthSuccess);
});

此支付處理程式需要位於作用域為 https://example.com/preauth 的服務工作執行緒中。

注意: 有關完整程式碼,請參閱我們的預授權交易演示

另見