Payment Request API 能否添加“货到付款”选项?
结论
不能只靠 methodData 在 Payment Request API 中添加“货到付款”(Cash on Delivery)选项。
methodData 用于声明网站接受的支付方式,并向相应的支付处理器传递配置。它不会创建新的支付方式,也不能让浏览器把任意字符串显示为付款选项。
普通电商网站通常应在自己的结账页面中提供货到付款。用户选定后,网站后端直接创建 COD 订单,不需要调用 Payment Request API。
为什么 methodData 不能直接实现
Payment Request API 不处理资金,也不定义网站的付款流程。浏览器会根据 supportedMethods 查找对应的支付处理器,例如:
const methodData = [
{
supportedMethods: "https://example.com/pay"
}
];
这里的 URL 不是显示名称,而是支付方式标识符。使用它需要相应的支付处理器、清单和浏览器支持。
因此,下面的代码不能直接添加货到付款:
const methodData = [
{
supportedMethods: "cash-on-delivery"
}
];
cash-on-delivery 不能作为普通展示选项任意注册。实际结果取决于浏览器实现。浏览器可能在创建 PaymentRequest 时因标识符无效而报错,也可能在调用 show() 时因为找不到可用的支付处理器而失败。
推荐实现方式
货到付款应放在网站自己的结账流程中:
- 用户填写或选择收货地址。
- 后端判断该地址是否支持货到付款。
- 前端显示“货到付款”选项。
- 用户确认后,将付款方式提交为
cash_on_delivery。 - 后端校验商品、库存、配送范围和 COD 资格,然后创建订单。
- 订单的初始状态应为“待发货”或“待收款”,不能记为“已支付”。
例如:
<form id="checkout-form">
<label>
<input
type="radio"
name="payment_method"
value="cash_on_delivery"
required
>
货到付款
</label>
<button type="submit">提交订单</button>
</form>
const form = document.querySelector("#checkout-form");
form.addEventListener("submit", async (event) => {
event.preventDefault();
const formData = new FormData(form);
const paymentMethod = formData.get("payment_method");
const response = await fetch("/api/orders", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
paymentMethod
})
});
if (!response.ok) {
throw new Error("订单创建失败");
}
const order = await response.json();
window.location.href = `/orders/${order.id}`;
});
后端必须再次验证付款方式,不能直接信任前端提交的值。下面是一个简化示例:
app.post("/api/orders", async (req, res) => {
const { paymentMethod } = req.body;
if (paymentMethod !== "cash_on_delivery") {
return res.status(400).json({
error: "Unsupported payment method"
});
}
const eligible = await checkCashOnDeliveryEligibility(req.user);
if (!eligible) {
return res.status(400).json({
error: "Cash on delivery is not available"
});
}
const order = await createOrder({
userId: req.user.id,
paymentMethod: "cash_on_delivery",
paymentStatus: "pending"
});
res.status(201).json(order);
});
能否通过自定义支付处理器实现
理论上,可以使用基于 URL 的支付方式标识符,并实现配套的 Payment Handler。之后再通过 methodData 引用该标识符:
const methodData = [
{
supportedMethods: "https://pay.example.com/cash-on-delivery"
}
];
const details = {
id: "order-123",
displayItems: [
{
label: "商品金额",
amount: {
currency: "CNY",
value: "99.00"
}
}
],
total: {
label: "应付金额",
amount: {
currency: "CNY",
value: "99.00"
}
}
};
const request = new PaymentRequest(methodData, details);
不过,这项方案并非增加一条配置就能完成。它还需要支付方式清单、可安装或已经安装的支付处理器,并依赖目标浏览器对相关机制的支持。不同浏览器和运行环境的支持情况可能不同,所以它不适合作为通用的 COD 实现方案。
如果还要支持在线支付
可以先让用户在结账页选择付款方式:
async function submitCheckout(paymentMethod) {
if (paymentMethod === "cash_on_delivery") {
return createCashOnDeliveryOrder();
}
if (paymentMethod === "online") {
return startOnlinePayment();
}
throw new Error("Unsupported payment method");
}
在线支付分支可以调用 Payment Request API,货到付款分支则直接创建订单。这样更符合 COD 的实际业务流程,也不会把创建订单错误地建模为浏览器支付处理器。
注意事项
- 后端应根据配送地址、订单金额、商品类型和用户风险状态判断订单是否支持货到付款。
- COD 订单创建时不能标记为已支付。收到款项后,再更新支付状态。
- 不要用虚假的在线支付成功结果来模拟货到付款。
- 订单金额、运费和 COD 服务费都必须由后端计算并校验。
- 提交接口需要处理重复请求,避免用户多次点击后生成多个订单。
- 只有在明确了解目标浏览器的兼容性,并且能够维护完整支付处理器的情况下,才值得考虑自定义 Payment Handler。对于一般网站,把货到付款作为独立的结账选项更简单,也更可靠。
备注:内容仅供参考。
