alipay.trade.page.pay 如何实现多商户订单合并支付?
结论
alipay.trade.page.pay 的一次请求只能提交一笔支付宝交易,不能同时提交 A、B、C 多个支付宝商户的订单,也不能把不同商户号下的交易直接合并付款。
一次接口调用只有一个 out_trade_no、一个 total_amount 和一个收款主体。天猫式的”多店铺订单一次付款”并不是简单地传入多个订单号,而是由平台统一创建支付单,并在支付完成后按平台的结算规则拆分资金。
类似的业务通常有两种处理方式:
- 平台已获得合规的统一收款及分账能力,可以创建一笔平台支付单,支付成功后再向各卖家分账或结算。
- 平台没有相关能力,则由 A、B、C 分别创建交易,用户逐笔支付,不能通过
alipay.trade.page.pay强行合并。
为什么不能直接合并
alipay.trade.page.pay 按照一笔商户订单对应一笔支付宝交易的模式工作。例如:
{
"out_trade_no": "PAY202609090001",
"total_amount": "300.00",
"subject": "平台订单支付",
"product_code": "FAST_INSTANT_TRADE_PAY"
}
其中:
out_trade_no是当前调用方生成的唯一商户订单号。total_amount是本次交易的总金额。- 收款主体由调用接口时使用的支付宝应用及其签约商户身份决定。
- 接口没有用于提交多个收款商户或多笔独立交易订单的通用参数。
即使把三个业务订单号拼接进 out_trade_no 或 passback_params,支付宝侧处理的仍是一笔交易,资金也只会进入这笔交易对应的收款主体。拼接订单号可以帮助平台记录业务关联,但不能实现多商户收款或资金拆分。
推荐的订单模型
如果平台已经开通并获准使用相应的收款、分账或结算能力,可以将业务订单和支付订单分成两层:
平台支付单 PAY-001,金额 300 元
├── 卖家 A 子订单 ORDER-A,金额 100 元
├── 卖家 B 子订单 ORDER-B,金额 80 元
└── 卖家 C 子订单 ORDER-C,金额 120 元
支付时,只向 alipay.trade.page.pay 提交平台支付单:
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl("https://example.com/pay/return");
request.setNotifyUrl("https://example.com/pay/notify");
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo("PAY-001");
model.setTotalAmount("300.00");
model.setSubject("平台订单合并支付");
model.setProductCode("FAST_INSTANT_TRADE_PAY");
request.setBizModel(model);
String form = alipayClient.pageExecute(request).getBody();
这段代码创建的是一笔总额为 300.00 元的支付交易。平台需要在自己的数据库中保存 ORDER-A、ORDER-B、ORDER-C 与该支付单之间的关系,例如:
payment_order
-------------
payment_no = PAY-001
total_amount = 300.00
status = WAIT_BUYER_PAY
payment_order_item
------------------
PAY-001 | ORDER-A | SELLER-A | 100.00
PAY-001 | ORDER-B | SELLER-B | 80.00
PAY-001 | ORDER-C | SELLER-C | 120.00
支付后的处理流程
可以按以下流程实现:
- 用户选中多个卖家的订单。
- 服务端锁定这些订单,重新计算金额,并生成唯一的平台支付单。
- 调用
alipay.trade.page.pay,提交支付单号和合计金额。 - 接收支付宝异步通知并完成验签。
- 核对通知中的
app_id、out_trade_no、total_amount、seller_id等信息,确认它们与本地支付单一致。 - 根据
trade_status幂等更新平台支付单。 - 将关联的子订单更新为已支付。
- 如果平台已开通合规的分账产品,再按各子订单的金额执行分账或后续结算。
异步通知可以按类似逻辑处理:
public String handleNotify(Map<String, String> params) throws Exception {
boolean verified = AlipaySignature.rsaCheckV1(
params,
ALIPAY_PUBLIC_KEY,
"UTF-8",
"RSA2"
);
if (!verified) {
return "failure";
}
String paymentNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");
String tradeStatus = params.get("trade_status");
BigDecimal paidAmount = new BigDecimal(params.get("total_amount"));
PaymentOrder paymentOrder = paymentOrderService.findByPaymentNo(paymentNo);
if (paymentOrder == null) {
return "failure";
}
if (!paidAmount.equals(paymentOrder.getTotalAmount())) {
return "failure";
}
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
paymentOrderService.markPaidIdempotently(paymentNo, tradeNo);
}
return "success";
}
这个示例省略了事务、并发控制和完整的字段校验,不能直接当作生产环境中的完整通知处理代码。
分账能力需要单独开通
统一收款后再把资金分给多个卖家,不是 alipay.trade.page.pay 自带的功能。平台通常需要另行申请支付宝的分账、商家结算或平台类产品能力,并根据实际签约的产品调用相应接口。
平台能使用哪些产品和接口,以及分账时间、手续费、收款方绑定方式和资金限制,都取决于所属行业、商户资质和支付宝开放平台当前提供的能力。仅签约普通电脑网站支付,并不意味着平台可以先收取全部货款,再自行转给卖家。
如果尚未获得相应能力,可以选择以下方式:
- 每个卖家使用自己的商户身份分别创建支付交易。
- 由平台申请适用于多商户业务的支付及分账方案。
- 联系支付宝开放平台或对应服务商,结合实际业务模式确认需要签约的产品。
退款也要按子订单设计
合并支付后,用户可能只退某个卖家的部分商品。因此,平台必须记录每个子订单的可退金额,再针对原支付宝交易发起部分退款。
例如,原交易的支付总额是 300.00 元,A 卖家子订单需要退款 30.00 元。退款请求仍关联原交易,但退款金额填写 30.00 元,同时使用唯一的退款请求号。如果资金已经分账,平台还要同步处理分账资金是否需要回退以及如何回退,具体规则以实际签约的分账产品为准。
注意事项
- 不要在同一次
alipay.trade.page.pay请求中放入多个商户的 AppID、商户号或签名参数。 - 不要直接使用前端传入的订单金额计算合并金额。服务端必须根据数据库中的订单重新核算。
out_trade_no应表示平台支付单,不能在不同的支付请求中重复使用。- 支付通知可能重复发送,更新支付单和子订单时必须保证幂等。
- 页面同步跳转只用于展示结果。订单状态应以验签通过的异步通知或主动查询结果为准。
- 平台统一收款涉及资金结算和合规要求,应先确认已签约的支付宝产品是否支持这种业务模式。
- 如果业务要求在一个付款页面中分别向多个支付宝商户直接入账,普通的
alipay.trade.page.pay无法实现。平台需要申请专门的支付及分账方案,修改接口参数解决不了这个问题。
备注:内容仅供参考。
