alipay.trade.pay 同一付款码重复支付结果不一致
结论
仅凭这种现象,不能判断支付宝系统出现了异常。
付款码支付中的 auth_code 是一种短时有效的付款凭证,只供单次支付尝试使用。同一个付款码不应拿来创建多笔交易,也不能当作普通的幂等参数反复提交。它与小程序登录场景中的 authCode 并非同一种凭证,但在业务处理上都不能重复消费。
10003 表示交易正在处理中,比如系统正等待用户输入支付密码。收到该状态后,应通过商户订单号查询交易结果,而不是再次调用 alipay.trade.pay,更不能使用同一个 auth_code 创建另一笔订单。
为什么两次调用的结果可能不同
alipay.trade.pay 返回的是调用发生时的交易状态,不一定代表最终结果。
例如:
- 第一次请求触发支付流程,用户需要输入密码,接口返回
10003。 - 用户随后完成验证,交易状态变为成功。
- 商户再次使用相同的付款码发起请求。
两个请求的到达时间、订单号、付款码状态和风控处理阶段可能不同,因此一个请求可能显示成功,另一个仍显示处理中或已经失败。特别是在并发或短时间内重复提交时,只比较两次同步响应,无法确认最终支付结果。
此时需要重点检查两次请求使用的 out_trade_no:
- 如果
out_trade_no相同,应按同一笔商户订单处理,并通过查询接口确认最终状态。 - 如果
out_trade_no不同,就相当于尝试使用同一个付款码创建两笔交易,不应采用这种方式。
正确的处理流程
1. 每次扫码只提交一次支付请求
收银设备读取付款码后,应立即锁定支付按钮或当前订单状态,防止用户重复点击或系统并发提交。
扫码成功
↓
锁定当前订单
↓
调用 alipay.trade.pay 一次
↓
根据返回状态处理
不要保存付款码用于稍后重试。接口超时后,也不要直接使用同一个付款码重新创建交易。
2. 收到 10003 后查询原订单
10003 并不表示支付已经失败,而是表示交易仍在处理中。商户应保留原来的 out_trade_no,通过 alipay.trade.query 轮询交易状态。
常见状态可以这样处理:
TRADE_SUCCESS:支付成功。WAIT_BUYER_PAY:继续等待并查询。- 明确失败或交易不存在:根据实际返回码处理。
- 超过商户设定的等待时间仍未成功:关闭或撤销交易,再提示用户重新出示付款码。
3. 使用商户订单号保证业务幂等
商户系统应使用 out_trade_no 作为支付业务的幂等依据。同一笔订单只能有一个有效的支付处理流程。
public PayResult pay(String outTradeNo, String authCode) {
Order order = orderRepository.findByOutTradeNo(outTradeNo);
if (order.isPaid()) {
return PayResult.success(order.getAlipayTradeNo());
}
if (order.isPaying()) {
return queryTrade(outTradeNo);
}
orderRepository.markPaying(outTradeNo);
AlipayTradePayResponse response = callAlipayTradePay(
outTradeNo,
authCode
);
if ("10000".equals(response.getCode())) {
orderRepository.markPaid(
outTradeNo,
response.getTradeNo()
);
return PayResult.success(response.getTradeNo());
}
if ("10003".equals(response.getCode())) {
return queryTrade(outTradeNo);
}
orderRepository.markPayFailed(
outTradeNo,
response.getSubCode(),
response.getSubMsg()
);
return PayResult.failed(response.getSubCode());
}
这段代码用于说明状态控制思路。具体的 SDK 类名和字段读取方式,应以项目当前使用的支付宝 SDK 为准。
查询逻辑可以这样组织:
public PayResult queryTrade(String outTradeNo) {
AlipayTradeQueryResponse response =
callAlipayTradeQuery(outTradeNo);
if (!response.isSuccess()) {
return PayResult.unknown(
response.getSubCode(),
response.getSubMsg()
);
}
String tradeStatus = response.getTradeStatus();
if ("TRADE_SUCCESS".equals(tradeStatus)) {
orderRepository.markPaid(
outTradeNo,
response.getTradeNo()
);
return PayResult.success(response.getTradeNo());
}
if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
return PayResult.pending();
}
return PayResult.failed(tradeStatus);
}
超时和异常时不要直接重付
如果调用 alipay.trade.pay 时发生网络超时、响应丢失或状态不明确,不能直接生成新的 out_trade_no,再用原付款码提交一次。应按以下顺序处理:
- 使用原
out_trade_no调用alipay.trade.query。 - 查询成功后,更新本地订单状态。
- 如果查询结果仍是等待状态,在限定时间内继续轮询。
- 如果最终状态仍无法确认,根据接入方案调用撤销或关闭能力。
- 确认原交易不会继续成功后,再让用户出示新的付款码。
注意事项
- 付款码通常会动态刷新,有效期较短,不适合作为可持久化、可重复调用的业务参数。
- 不要只根据
alipay.trade.pay的同步响应向用户出货,还应结合最终交易状态和异步通知进行确认。 - 异步通知同样需要做幂等处理,防止重复通知造成重复发货或重复记账。
- 如果两次请求使用了不同的
out_trade_no,应分别查询两笔订单,确认是否实际扣款,不能只比较接口返回码。 - 如果能够稳定复现“一码两笔交易均成功”,应保存两次请求的
out_trade_no、支付宝交易号trade_no、请求时间和完整响应码,再通过支付宝技术支持渠道核查。不要记录或上传完整付款码等敏感支付凭证。
备注:内容仅供参考。
