面对面支付异常 10003,订单状态仍为 WAIT_BUYER_PAY
结论
异常码 10003 不代表支付失败。它表示支付宝已经受理订单,但支付结果仍在处理中。此时,接口返回的不是最终结果,需要继续调用 alipay.trade.query 查询交易状态。
如果用户已经确认付款,而同一笔订单长时间返回 WAIT_BUYER_PAY,问题通常不只是状态同步延迟。需要核对查询所用的订单号、应用、商户身份和运行环境,确认它们与支付请求完全一致。订单最终状态应以支付宝查询接口或验签后的异步通知为准,不能只凭用户端显示的”支付成功”更新本地订单。
为什么会返回 10003
面对面支付需要收银端、支付宝支付系统和用户客户端共同完成。付款过程中可能遇到以下情况:
- 用户已经扫码或提交付款,但整个支付流程尚未完成。
- 支付结果已经产生,但同步接口没有及时取得最终状态。
- 网络超时或系统繁忙,商户端暂时无法确认交易结果。
- 付款仍在等待用户输入密码、完成风控验证或执行其他操作。
所以,10003 表示当前”结果未知”。收到这个状态后,既不能立即把订单标记为支付成功,也不能直接将其视为失败并重新创建同一业务订单。
WAIT_BUYER_PAY 表示什么
alipay.trade.query 返回:
trade_status=WAIT_BUYER_PAY
说明当前查询到的支付宝交易仍在等待买家付款。
短时间内出现这一状态通常正常,可以继续轮询。如果用户已经实际扣款,但状态长时间没有变化,应依次检查以下问题。
1. 查询的交易与付款交易不一致
先确认支付请求与查询请求使用了同一个 out_trade_no,并注意以下几点:
- 不要混用业务订单号和支付流水号。
- 重试时不要生成新的
out_trade_no。 - 检查订单号是否被截断、拼接或添加了前后缀。
- 如果已经取得支付宝交易号
trade_no,可以优先用它查询。
同一笔业务支付应始终使用唯一的商户订单号。如果重新生成订单号后查询旧订单,就可能出现用户已经付款,查询结果却仍为待付款的情况。
2. 支付与查询使用了不同的应用或商户身份
检查两次请求的配置是否一致,包括:
app_id- 商户账号或
seller_id - 应用私钥和支付宝公钥
- 支付宝网关地址
- 签约产品及授权关系
由一个应用发起的交易,不能默认使用另一个应用的配置也能查询到正确结果。
3. 混用了沙箱环境和生产环境
沙箱与生产环境的数据彼此隔离。支付请求和查询请求必须发送到同一环境,SDK 配置也要保持一致。
4. 用户看到”支付成功”,但成功的不是当前订单
需要核对用户账单中的支付宝交易号、金额、收款方和付款时间。收银台页面或商户前端显示的成功提示,不能代替服务端查询到的交易状态。
如果账单中的 trade_no 与商户保存的交易号不同,应根据实际交易号追查对应订单。
5. 异步通知没有得到正确处理
支付宝可能已经发出异步通知,但商户系统因验签失败、接口异常或业务处理失败而没有更新本地订单。不过在这种情况下,直接调用 alipay.trade.query,通常仍能查到支付宝侧的真实状态。
可以据此判断:
- 查询接口也返回
WAIT_BUYER_PAY:优先检查订单、应用、商户身份和运行环境是否匹配。 - 查询接口返回
TRADE_SUCCESS,但本地订单仍未支付:检查异步通知和本地订单更新逻辑。
推荐处理流程
第一步:保留支付请求上下文
至少记录以下信息:
out_trade_no
app_id
seller_id(如有)
total_amount
支付请求时间
支付宝返回的 code、sub_code、msg、sub_msg
trade_no(如果返回)
日志中不要保存用户付款码、应用私钥等敏感信息。
第二步:收到 10003 后进入查询流程
使用原支付请求中的 out_trade_no,或已经取得的 trade_no 调用:
alipay.trade.query
可以按以下原则处理查询状态:
TRADE_SUCCESS:支付成功。校验订单号、金额和商户身份后更新订单。TRADE_FINISHED:交易已经完成,按成功交易处理,但不能再通过常规退款以外的方式变更。WAIT_BUYER_PAY:尚未确认支付成功,继续进行有限次数的轮询。TRADE_CLOSED:交易已经关闭,不能按支付成功处理。- 查询失败或结果不确定:继续按未知状态处理,不要直接认定支付失败。
轮询次数和间隔应根据收银台超时时间及业务要求设置,避免无限循环或高频查询。
第三步:轮询结束后让订单状态收敛
如果在允许的支付时间内仍然无法确认成功,应停止收银台等待,并按照面对面支付接口的接入规范调用撤销或关单能力。
面对面支付通常可以使用以下接口处理异常状态:
alipay.trade.cancel
需要注意,alipay.trade.cancel 对已经成功的交易可能产生撤销或退款效果。因此,调用前必须再次查询,并根据接口的实际返回结果处理。不能因为”发起撤销成功”,就认为原交易从未支付。
究竟使用 alipay.trade.cancel 还是其他关单方式,应以当前签约产品及所用支付宝 SDK/API 文档为准。
第四步:同时接收异步通知
查询与异步通知需要同时保留:
- 轮询用于让收银台及时确认结果。
- 异步通知用于补偿网络超时和状态延迟。
- 两条处理链路都要支持重复调用。
更新订单时,建议使用条件更新,只允许订单从未支付状态变为已支付状态,避免重复记账或重复发货。
Java 处理示例
以下示例用于说明处理思路。具体类名和配置方式需要与项目使用的支付宝 Java SDK 版本一致:
public TradeState queryTrade(
AlipayClient alipayClient,
String outTradeNo,
String expectedAmount) throws AlipayApiException {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(outTradeNo);
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (!response.isSuccess()) {
return TradeState.UNKNOWN;
}
String tradeStatus = response.getTradeStatus();
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
if (!expectedAmount.equals(response.getTotalAmount())) {
throw new IllegalStateException("支付宝交易金额与商户订单金额不一致");
}
// 实际项目中还应校验 app_id、seller_id 等交易归属信息。
// 数据库更新必须保证幂等。
return TradeState.SUCCESS;
}
if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
return TradeState.PENDING;
}
if ("TRADE_CLOSED".equals(tradeStatus)) {
return TradeState.CLOSED;
}
return TradeState.UNKNOWN;
}
状态枚举示例:
public enum TradeState {
SUCCESS,
PENDING,
CLOSED,
UNKNOWN
}
对 10003 的调用逻辑可以写成:
TradeState state = queryTrade(
alipayClient,
order.getOutTradeNo(),
order.getTotalAmount()
);
switch (state) {
case SUCCESS:
markOrderPaidIdempotently(order);
break;
case PENDING:
case UNKNOWN:
scheduleNextQuery(order);
break;
case CLOSED:
markOrderClosed(order);
break;
}
处理异步通知时,也要先验签并校验关键字段:
if (signatureValid
&& outTradeNoMatches
&& amountMatches
&& sellerMatches
&& appIdMatches
&& ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus))) {
markOrderPaidIdempotently(order);
}
注意事项
- 不要把
10003当成支付失败,也不要立即要求用户重复付款。 - 不仅要检查接口通信是否成功,还要检查
trade_status。 - 不要仅凭客户端页面、截图或用户口述更新订单。
- 支付成功处理必须保证幂等。查询和异步通知可能同时到达,也可能重复到达。
- 确认支付成功前,应校验
out_trade_no、total_amount、app_id、seller_id等关键字段。 - 查询超时后不要无限重试,应通过撤销或关单机制让未知状态最终收敛。
- 如果支付宝账单确认已经扣款,但使用正确的
trade_no查询后仍长期异常,应保存完整的请求时间、订单号、交易号和接口响应,再通过支付宝商家支持渠道进一步核查。
备注:内容仅供参考。
