Basic Web Payments Request API 示例无法弹出支付窗口
结论
paymentRequest.show() 没有弹出信用卡选择窗口,通常与收款账户无关,更可能是以下原因:
- 主流浏览器已逐步弃用或移除
"basic-card",当前浏览器中可能没有可用的支付处理程序。 show()没有由用户点击等有效的用户激活操作直接触发。- 页面不在安全上下文中,例如通过普通 HTTP 访问。
show()返回的 Promise 被拒绝,但代码没有捕获异常,所以看起来像是“什么也没发生”。
Payment Request API 不负责完成扣款。它用于调用浏览器的支付界面并收集支付信息,实际的授权、扣款和入账仍要通过支付服务商或支付网关处理。
先把实际错误显示出来
paymentRequest.show() 返回 Promise。原代码没有处理失败情况,至少应该加入 try...catch:
<button id="pay-button" type="button">Pay here</button>
<script>
document.getElementById("pay-button").addEventListener("click", async () => {
if (!window.PaymentRequest) {
console.error("This browser does not support the Payment Request API.");
return;
}
const supportedPaymentMethods = [
{
supportedMethods: "basic-card"
}
];
const paymentDetails = {
total: {
label: "Total Cost",
amount: {
currency: "GBP",
value: "1.00"
}
}
};
try {
const paymentRequest = new PaymentRequest(
supportedPaymentMethods,
paymentDetails
);
const paymentResponse = await paymentRequest.show();
console.log("Payment response:", paymentResponse);
// 实际应用中,应先把支付数据提交到服务器处理。
await paymentResponse.complete("success");
} catch (error) {
console.error("Payment request failed:", error.name, error.message);
}
});
</script>
这段代码不能保证当前浏览器支持 "basic-card",但能把具体的失败原因显示出来。开发者工具控制台中常见的错误有 NotSupportedError、AbortError,以及与用户激活或安全上下文有关的错误。
为什么 "basic-card" 可能无法使用
PaymentRequest 是一个调用支付界面的通用 API,并不表示浏览器一定内置信用卡支付能力。浏览器要显示支付界面,通常需要同时满足两个条件:
- 浏览器支持 Payment Request API。
- 浏览器或操作系统中存在与
supportedMethods匹配的支付处理程序。
检测到 window.PaymentRequest,只能说明 API 入口存在,不能证明 "basic-card" 可用。特别是 "basic-card",已经不适合作为现代浏览器新项目的支付方案。它能否使用,取决于浏览器、版本、操作系统和用户配置。
可以调用 canMakePayment() 检查支付能力:
const paymentRequest = new PaymentRequest(
supportedPaymentMethods,
paymentDetails
);
try {
const canPay = await paymentRequest.canMakePayment();
if (!canPay) {
console.warn("No compatible payment handler is available.");
return;
}
const paymentResponse = await paymentRequest.show();
console.log(paymentResponse);
} catch (error) {
console.error(error);
}
不过,canMakePayment() 只能作为参考,不能代替对 show() 的异常处理。它的返回结果也可能受浏览器隐私策略影响。
show() 必须由用户操作触发
支付窗口通常需要在按钮点击等有效的用户激活事件中直接打开。不要在页面加载时自动调用,也不要等点击后执行完较长的异步流程再调用:
payButton.addEventListener("click", async () => {
try {
const response = await paymentRequest.show();
// ...
} catch (error) {
console.error(error);
}
});
下面的写法可能因为用户激活状态已经失效而失败:
payButton.addEventListener("click", async () => {
await someLongAsyncOperation();
await paymentRequest.show();
});
如果必须先获取价格等数据,最好提前准备好,再让用户点击支付按钮。
页面必须处于安全上下文
Payment Request API 通常要求页面处于安全上下文:
- 正式环境应使用 HTTPS。
- 本地开发通常可以使用
http://localhost。 - 直接打开
file://文件或通过普通 HTTP 域名访问时,API 可能无法正常调用。
可以这样检查:
console.log("Secure context:", window.isSecureContext);
如果结果为 false,应先处理 HTTPS 或本地开发服务器问题。
参数还需要注意两处细节
金额的 value 应该是符合支付金额格式的字符串:
value: "1.00"
不建议使用数字:
value: 1
变量名也必须保持一致。如果声明的是:
const supportedPaymentMethods = [];
构造时就要传入同一个变量:
new PaymentRequest(
supportedPaymentMethods,
paymentDetails,
options
);
如果误写为未定义的 supportedMethods,代码会直接产生 ReferenceError,不会执行到 show()。
下一步应该如何配置
生产环境不应只依赖 "basic-card"。通常需要先选择支付服务商,再按照服务商文档配置对应的支付方法。例如,某些支付方式会使用 URL 作为标识:
const supportedPaymentMethods = [
{
supportedMethods: "https://payment-provider.example/pay",
data: {
merchantId: "YOUR_MERCHANT_ID"
}
}
];
这里的地址和字段只是结构示意,不能直接用于支付。实际配置必须由所选支付服务商提供,通常包括:
- 商户账户和商户标识;
- 允许使用的支付方式;
- 域名验证或商户认证;
- 前端支付方法配置;
- 服务端支付授权与扣款接口;
- 支付成功、失败和取消的处理;
- 在服务端重新校验订单金额。
典型的支付流程如下:
- 用户点击支付按钮。
- 浏览器显示可用的支付方式。
show()返回PaymentResponse。- 前端把必要的支付数据发送到自己的服务器。
- 服务器调用支付服务商接口完成授权或扣款。
- 根据服务端返回的结果调用
response.complete("success")或response.complete("fail")。
因此,即使没有商户账户时能够显示支付窗口,也只能测试界面和数据收集流程,无法完成真实收款。PaymentResponse 不能直接证明支付成功,最终结果必须以服务器和支付服务商返回的信息为准。
备注:内容仅供参考。
