支付宝查询使用out_trade_no提示交易不存在的原因
结论
out_trade_no 和 trade_no 是两种不同的订单号,不能用同一个值替换查询。
out_trade_no:由商户系统生成,必须与创建或支付交易时提交给支付宝的商户订单号完全一致。trade_no:由支付宝为交易生成,是支付宝交易号。
如果某个值填入 trade_no 可以查询成功,填入 out_trade_no 却提示交易不存在,通常表示它是支付宝交易号,不是创建交易时提交的商户订单号。
两个字段的区别
| 字段 | 生成方 | 含义 | 查询条件 |
|---|---|---|---|
out_trade_no | 商户系统 | 商户订单号 | 必须匹配原交易中的商户订单号,并处于正确的应用、商户和环境上下文中 |
trade_no | 支付宝 | 支付宝交易号 | 必须是支付宝返回的真实交易号,并且当前调用方具有查询该交易的权限 |
创建交易时,商户一般会提交自己的 out_trade_no:
{
"out_trade_no": "ORDER_20220328_001",
"total_amount": "10.00",
"subject": "测试订单"
}
支付宝受理交易后,会在同步响应或异步通知中返回 trade_no:
{
"out_trade_no": "ORDER_20220328_001",
"trade_no": "2022032822001400000000000000"
}
查询时要使用与字段对应的值。按商户订单号查询:
{
"out_trade_no": "ORDER_20220328_001"
}
按支付宝交易号查询:
{
"trade_no": "2022032822001400000000000000"
}
即使两者都是数字字符串,也不能把支付宝返回的 trade_no 当作 out_trade_no 传入。
为什么使用 out_trade_no 会提示交易不存在
常见原因如下。
1. 传入的其实是 trade_no
这与当前现象最为吻合。同一个值作为 trade_no 可以查询成功时,应先确认它的来源:
- 如果来自支付宝支付响应的
trade_no字段,就应使用trade_no查询。 - 如果来自商户数据库中的订单编号,就应使用
out_trade_no查询。 - 不要通过号码的长度或格式判断字段类型,应以响应字段名和数据来源为准。
2. 商户订单号与支付时提交的不一致
out_trade_no 必须与创建交易时提交的字符串完全一致。以下情况都可能导致查询失败:
- 查询时使用了业务订单号,而支付时提交的是支付流水号;
- 订单号的前缀或后缀不同;
- 补单或重试时重新生成了订单号;
- 数据库存储时截断了订单号;
- 字符串中有空格或大小写差异;
- 传入了退款单号、预授权单号等其他业务编号。
应直接查看支付请求日志或支付宝异步通知中的 out_trade_no,不要根据业务数据推测。
3. 查询所用的应用或商户上下文不一致
使用 out_trade_no 查询时,除了订单号必须匹配,交易上下文也必须正确。需要核对:
- 查询使用的
app_id是否与创建交易时一致; - 签约商户或收款账号是否一致;
- 服务商代商户调用时,
app_auth_token是否属于创建该交易的授权商户; - 是否在不同商户账号下使用了相同的商户订单号。
out_trade_no 是商户侧编号,不是支付宝平台的全局交易标识,无法只凭这个号码跨商户或跨应用查询交易。
4. 沙箱与生产环境混用
示例代码使用的是沙箱网关:
https://openapi.alipaydev.com/gateway.do
如果交易在生产环境创建,却通过沙箱网关查询,或者交易在沙箱环境创建,却通过生产网关查询,都会提示交易不存在。生产环境通常使用:
https://openapi.alipay.com/gateway.do
app_id、应用私钥、支付宝公钥和交易环境也必须配套,不能混用沙箱配置和生产配置。
5. 交易尚未在支付宝侧成功创建
商户系统可能已经生成了 out_trade_no,但创建交易的请求失败、没有成功发送,或者没有被支付宝受理。此时本地数据库虽然有订单,支付宝侧却可能没有对应的交易。
遇到这种情况,应检查原始的交易创建响应,而不只是确认本地订单是否存在。
推荐的查询写法
建议明确区分两种查询方法,不要使用含义模糊的 num 参数。
按商户订单号查询:
public AlipayTradeQueryResponse queryByOutTradeNo(
AlipayClient alipayClient,
String outTradeNo) throws AlipayApiException {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNo);
request.setBizContent(bizContent.toString());
return alipayClient.execute(request);
}
按支付宝交易号查询:
public AlipayTradeQueryResponse queryByTradeNo(
AlipayClient alipayClient,
String tradeNo) throws AlipayApiException {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("trade_no", tradeNo);
request.setBizContent(bizContent.toString());
return alipayClient.execute(request);
}
调用后还要检查错误码和子错误码:
AlipayTradeQueryResponse response =
queryByOutTradeNo(alipayClient, "ORDER_20220328_001");
if (response.isSuccess()) {
System.out.println("查询成功");
System.out.println("out_trade_no: " + response.getOutTradeNo());
System.out.println("trade_no: " + response.getTradeNo());
System.out.println("trade_status: " + response.getTradeStatus());
} else {
System.out.println("查询失败");
System.out.println("code: " + response.getCode());
System.out.println("msg: " + response.getMsg());
System.out.println("sub_code: " + response.getSubCode());
System.out.println("sub_msg: " + response.getSubMsg());
}
如果由服务商代商户查询,还需要使用创建交易时对应的应用授权令牌。具体传递方式取决于所用的支付宝 Java SDK 版本:
AlipayTradeQueryResponse response =
alipayClient.execute(request, null, appAuthToken);
排查步骤
- 在支付创建请求中找到原始的
out_trade_no。 - 在支付响应或异步通知中找到支付宝返回的
trade_no。 - 使用各自对应的字段查询,不要重复使用来源不明的同一个号码。
- 核对查询和支付使用的
app_id、商户身份以及app_auth_token。 - 确认查询与支付使用的是同一环境,不要混用沙箱网关和生产网关。
- 输出
code、sub_code、msg、sub_msg和完整响应体,确认具体的失败原因。 - 如果本地有订单但支付宝查询不到,检查原始交易创建请求是否真正成功。
注意事项
同时传入 trade_no 和 out_trade_no,可能掩盖商户订单号错误,也会增加排查难度。实际开发时应选择一种含义明确的查询依据。
示例代码中还包含了疑似完整的应用私钥。不要把私钥写入源码、日志或公开内容。如果该密钥曾被实际使用或已经泄露,应立即在支付宝开放平台更换应用密钥,并通过环境变量、密钥管理服务或受控配置文件加载新密钥。
备注:内容仅供参考。
