支付宝如何实现多商户多个订单合并支付?
结论
普通商户调用 alipay.trade.app.pay 时,一次只能创建一笔支付宝交易,out_trade_no 对应一个商户订单号。这个订单号可以关联平台内部的多个子订单,但支付宝侧仍只会生成一笔交易,不会自动拆成多个商家的账单。
淘宝式的“多商户、多订单、一次付款、支付宝侧生成多笔交易”,不能通过向 alipay.trade.app.pay 传入多个订单号实现。这类模式通常依赖支付宝向特定平台开放的合并支付或平台型支付能力,需要单独签约和审核。能否开通、使用哪个接口以及需要哪些参数,应以支付宝开放平台为当前应用提供的产品文档为准。
如果没有获得这类能力,通常有三种处理方式:
- 平台统一收款:将多个订单合并为一笔支付宝交易,支付成功后由平台在内部拆分订单和金额。
- 商户分别收款:每个商户单独创建支付宝交易,用户逐笔支付。
- 接入支付宝的平台型产品:在满足资质和业务合规要求的前提下,实现平台收款、分账或直接结算。
为什么普通支付接口只能传一个订单号
在 alipay.trade.app.pay 中,out_trade_no 是商户侧交易的唯一标识。一次接口调用对应:
- 一个
out_trade_no - 一个支付总金额
total_amount - 一个收款主体
- 一笔支付宝交易号
trade_no
普通 App 支付接口不支持下面这种参数结构:
{
"out_trade_no": [
"ORDER_1001",
"ORDER_1002",
"ORDER_1003"
]
}
即使三个订单属于同一个平台,也应该先在业务系统中创建一个“支付单”,再使用支付单号调用支付宝。多个业务订单与支付单之间的关系由平台自行维护。
例如:
支付单 PAY_202609090001
├── 商户 A / 订单 ORDER_A_001 / 50.00 元
├── 商户 A / 订单 ORDER_A_002 / 20.00 元
└── 商户 B / 订单 ORDER_B_001 / 30.00 元
支付总额:100.00 元
调用普通 App 支付后,支付宝侧看到的是 PAY_202609090001 对应的一笔 100 元交易,而不是三笔独立交易。
方案一:平台创建合并支付单
如果业务允许平台作为统一收款方,可以在系统中建立三层模型:
- 业务订单:记录用户购买的商品及其所属商户。
- 支付单:汇总本次需要支付的多个业务订单。
- 支付流水:记录支付宝
trade_no、支付状态和通知信息。
创建支付单时,需要在数据库事务中锁定相关订单,重新计算应付金额,同时确认这些订单没有被取消或支付。
下面是简化后的 Java 示例:
List<Order> orders = orderService.findUnpaidOrders(orderIds, userId);
BigDecimal totalAmount = orders.stream()
.map(Order::getPayAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
String paymentNo = paymentService.createPayment(
userId,
totalAmount,
orders
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl("https://example.com/api/alipay/notify");
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(paymentNo);
model.setSubject("订单合并支付");
model.setTotalAmount(totalAmount.setScale(2, RoundingMode.HALF_UP).toPlainString());
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
String orderString = response.getBody();
服务端将 orderString 返回给 App,再由支付宝客户端 SDK 发起支付。
这里的 paymentNo 是平台生成的支付单号,不是任何一个子订单号。
支付成功后的处理
App 返回的同步结果不能作为最终支付依据。服务端需要验签并处理支付宝异步通知,再根据支付单查询所有关联的子订单。
示意代码如下:
@PostMapping("/api/alipay/notify")
public String handleNotify(HttpServletRequest request) throws Exception {
Map<String, String> params = extractParams(request);
boolean verified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"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");
String totalAmount = params.get("total_amount");
if (!"TRADE_SUCCESS".equals(tradeStatus)
&& !"TRADE_FINISHED".equals(tradeStatus)) {
return "success";
}
paymentService.confirmPaid(
paymentNo,
tradeNo,
new BigDecimal(totalAmount)
);
return "success";
}
confirmPaid 至少需要完成以下校验:
app_id是否属于当前应用。seller_id或收款账号是否正确。out_trade_no是否存在,是否已被其他交易占用。- 通知金额是否与数据库中的支付单金额完全一致。
- 当前支付单状态是否允许变更。
- 同一通知重复到达时,系统能否幂等处理。
确认支付成功后,再通过同一事务或可靠消息流程,将关联的子订单更新为已支付。
多商户资金如何处理
“平台内部拆单”与“资金实际进入不同商家账户”是两件事。
调用普通 alipay.trade.app.pay 时,款项默认进入签约的收款主体。平台不能只凭数据库中的商户字段,就让支付宝自动将资金转入不同商户的账户。
多商户业务通常还要处理这些问题:
- 平台是否具备代收资金的业务资质。
- 各商户是否需要成为支付宝认可的二级商户或结算接收方。
- 是否需要接入分账、结算或平台直付类产品。
- 退款、手续费和优惠金额如何分摊。
- 是否需要向商户提供对账单和结算单。
alipay.trade.order.settle 容易因名称被误认为“合并支付接口”,但它用于交易后的结算或分账,不能代替 alipay.trade.app.pay 创建多笔支付交易,也不会将一笔普通支付自动转换成多笔支付宝账单。该接口能否使用,还取决于签约产品和账户权限。
如果必须在支付宝侧生成多笔账单
如果业务必须同时满足以下要求:
- 用户只操作一次支付。
- 子订单属于不同的收款商户。
- 每个商户在支付宝侧都有独立交易或账单。
此时应向支付宝申请相应的合并支付或平台型支付产品,而不是继续调整普通 App 支付接口的参数。这类能力可能受到行业、主体资质、商户入驻方式和风控要求的限制,不能假定所有应用都可以直接调用。
接入前,需要向支付宝确认:
- 当前业务是否允许平台聚合多个商户的订单。
- 用户完成一次付款后,支付宝侧生成一笔交易还是多笔交易。
- 子商户如何入驻、签约并确定收款账户。
- 接口是否支持 App 场景,而不只是网页或其他特定业务场景。
- 退款按主单还是子单发起。
- 账单、手续费、优惠和结算金额如何对应各个子订单。
如果支付宝没有向当前应用开放相应产品,就无法只依靠公开的 alipay.trade.app.pay 实现淘宝同等模式。
实现时容易忽略的问题
金额必须由服务端计算
不要使用 App 直接提交的总金额。服务端应根据订单价格、优惠、运费和订单状态重新计算,并校验:
支付单总额 = 所有有效子订单应付金额之和
支付单与业务订单要分开
不要随意选择某个子订单号作为 out_trade_no,否则在退款、对账和重复支付检查时,很难判断这笔款项具体覆盖了哪些订单。
退款需要按子订单核算
即使支付宝侧只有一笔交易,平台仍可基于原交易发起部分退款,但需要自行记录每个子订单的已退款金额,并确保累计退款金额不超过原交易金额。具体退款接口和可用能力以已签约产品为准。
防止部分订单状态变化
用户选择订单后,到调起支付宝之前,商品可能下架,库存可能变化,订单也可能超时。创建支付单时应锁定订单或设置有效期。支付成功后,还要处理库存确认失败等异常情况。
做好对账
当平台内部的一个支付单关联多个订单时,支付宝账单只能直接对应这个支付单。对账程序需要先通过 out_trade_no 找到支付单,再查询关联的子订单和商户结算记录。
普通项目最常见的做法,是将多个订单合并为一个平台支付单,再调用一次 alipay.trade.app.pay。如果必须由支付宝生成多笔属于不同商户的账单,就需要申请支付宝专门开放的平台型或合并支付能力,无法通过一次传入多个 out_trade_no 实现。
备注:内容仅供参考。
