使用支付请求 API

安全上下文:此功能仅在安全上下文(HTTPS)中可用,在某些或所有支持的浏览器中可用。

支付请求 API提供了一种基于浏览器的连接用户及其首选支付系统和平台与他们希望支付商品和服务的商家的方法。本文是使用支付请求 API的指南,其中包含示例和建议的最佳实践。

支付的基本知识

本节详细介绍了使用支付请求 API 进行支付的基本知识。

注意:本节中的代码片段来自我们的功能检测支持演示

创建新的支付请求对象

支付请求始终以创建新的PaymentRequest对象开始 - 使用PaymentRequest()构造函数。这需要两个必填参数和一个可选参数

  • methodData - 一个包含有关支付提供商的信息的对象,例如支持哪些支付方式等。
  • details - 一个包含有关特定支付的信息的对象,例如总支付金额、税费、运费等。
  • options(可选) - 一个包含与支付相关的其他选项的对象。

例如,您可以像这样创建一个新的PaymentRequest实例

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

构造函数内部调用的函数返回所需的object参数。

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()可用于在需要时中止支付请求。

检测支付请求 API 的可用性

您可以通过检查用户的浏览器是否支持PaymentRequest(即if (window.PaymentRequest))来有效地检测对支付请求 API 的支持。

在以下代码段中,商家页面执行此检查,如果返回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 `paymeResponse.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) => {
    // Process payment here.
    // Close the UI:
    paymentResponse.complete('success').then(() => {
      // 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.
  });

注意:请参阅我们的付款成功后显示其他用户界面演示以获取完整代码。

预授权交易

某些用例(例如,在加油站加油)涉及预授权支付。一种方法是通过支付处理程序(请参阅支付处理程序 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范围内的服务工作线程中。

注意:请参阅我们的预授权交易演示以获取完整代码。

另请参阅