通过 W3C Web Payments API 实现 Square 多方交易
结论
仅靠 W3C Web Payments API,无法实现或开启 Square 的多方交易功能。
Payment Request API 用于统一浏览器端的支付信息收集和支付方式选择,并不处理分账、平台佣金、子商户结算或多收款方路由。多方交易仍要依靠 Square 提供的账户能力和服务端 API。
如果需要在 Web Payments 的交互流程中提供这项功能,可以开发自定义支付方式和 Payment Handler。不过,它只是前端入口,底层仍需调用 Square API。Square 如果没有向当前账户或集成模式开放相应能力,Payment Handler 也无法绕过限制。
为什么 Web Payments API 无法直接完成分账
一笔典型的多方交易至少需要处理以下问题:
- 识别平台、卖家等参与方
- 计算平台手续费或应用费
- 确定资金进入哪个商户账户
- 处理退款、拒付和争议责任
- 遵守 KYC、税务及结算规则
- 管理服务端幂等和交易状态
这些属于支付服务商的账户和资金处理逻辑。
W3C Web Payments API 中的 PaymentRequest 只描述订单金额、展示项目和浏览器支持的支付方式。例如:
const request = new PaymentRequest(
[
{
supportedMethods: "basic-card"
}
],
{
total: {
label: "订单总额",
amount: {
currency: "USD",
value: "100.00"
}
}
}
);
const response = await request.show();
这里的 total 是消费者看到并授权的总金额。它无法表达卖家获得 90 美元、平台获得 10 美元,也不会替开发者执行分账。
另外,basic-card 已不适合新项目作为通用方案使用,浏览器支持情况也需要单独确认。
推荐的实现方式
比较稳妥的做法是明确划分前后端职责:
- 前端使用 Square 支持的 Web 支付组件收集支付凭证。
- 后端调用 Square API 创建交易,并传入卖家、平台费用等多方交易信息。
- 后端保存订单和 Square 交易标识,同时处理退款、Webhook 与异常重试。
- 前端根据后端返回的最终状态展示支付结果。
下面是一个概念性流程。实际使用的 API 字段必须以 Square 当前文档和账户已开通的产品能力为准。
async function submitPayment(sourceId, orderId) {
const response = await fetch("/api/payments", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
sourceId,
orderId
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.message || "Payment failed");
}
return result;
}
服务端应根据可信的订单数据计算金额,不能直接接受浏览器提交的分账结果:
app.post("/api/payments", async (req, res) => {
const { sourceId, orderId } = req.body;
const order = await loadOrder(orderId);
if (!order) {
return res.status(404).json({
message: "Order not found"
});
}
const paymentRequest = {
sourceId,
amount: order.totalAmount,
currency: order.currency,
sellerAccountId: order.sellerAccountId,
platformFee: order.platformFee,
idempotencyKey: order.paymentAttemptId
};
// 根据当前 Square API 和账户能力,将上述业务数据映射为实际请求。
const payment = await createSquareMarketplacePayment(paymentRequest);
res.json({
paymentId: payment.id,
status: payment.status
});
});
createSquareMarketplacePayment 是业务层的占位函数,并非 Square 官方 API 名称。示例没有指定 Square 当前使用的具体字段,因为多方交易接口、准入条件和字段命名可能因产品及账户类型而异。
使用自定义 Payment Handler 的可选方案
如果业务需要通过 Web Payments 界面调起支付,可以注册自定义支付方式:
const request = new PaymentRequest(
[
{
supportedMethods: "https://pay.example.com/square-marketplace",
data: {
orderId: "order-123"
}
}
],
{
total: {
label: "订单总额",
amount: {
currency: "USD",
value: "100.00"
}
}
}
);
const response = await request.show();
try {
const result = await fetch("/api/payments/confirm", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
orderId: "order-123",
paymentData: response.details
})
});
if (!result.ok) {
throw new Error("Payment confirmation failed");
}
await response.complete("success");
} catch (error) {
await response.complete("fail");
throw error;
}
这种支付方式需要自行托管支付方式清单,并实现浏览器支持的 Payment Handler。它能把 Square 支付接入统一的浏览器交互,但仍有以下限制:
- 浏览器必须支持 Payment Handler。
- 用户可能需要安装或注册该支付处理程序。
- 自定义
supportedMethodsURL 不会自动获得 Square 支持。 - Square 支付凭证仍应通过其允许的客户端方案生成。
- 最终交易和分账必须由可信的服务端完成。
这种方案更适合已经拥有自定义支付生态,同时需要统一 Web Payments 入口的项目。对于普通的 Square 商户集成,它通常不是首选。
注意事项
分账金额、平台费用和收款方身份不能由前端决定。后端应根据订单、卖家协议及账户配置重新计算,并校验所有数据。
不要把 Square 访问令牌或其他服务端密钥写入浏览器代码。客户端只能处理允许公开的应用标识和一次性支付凭证。
创建支付请求时还要设置幂等键,防止网络重试造成重复扣款。支付、退款和争议状态应通过 Square 提供的事件通知机制核对,不能只依据前端返回的 “成功” 结果。
Web Payments API 不能替代支付服务商的合规审查和账户授权。如果 Square 的多方交易能力需要单独申请、使用指定账户模型或仅支持特定地区,就必须先满足这些条件。功能请求、自定义 Payment Handler 和前端封装都无法绕过服务端能力限制。
备注:内容仅供参考。
