电脑网站支付和网页扫码支付成功后查询提示交易不存在
结论
40004 / ACQ.TRADE_NOT_EXIST 的含义是:支付宝没有在当前查询请求对应的商户、应用和运行环境中找到指定交易。
页面显示“支付成功”,不能证明查询条件一定正确。常见原因包括:
- 支付和查询使用的
app_id、商户账号或密钥配置不一致; - 支付发生在正式环境,查询请求却发往沙箱环境,或者相反;
- 查询使用的
out_trade_no与创建支付时传入的值不完全一致; - 页面只是根据
return_url显示支付成功,并未通过异步通知或查询结果确认交易状态; - 查询时间太早,交易数据还未完成同步;
- 实际支付成功的是另一笔订单,业务系统却关联了错误的订单号。
排查时,建议优先使用支付宝在支付成功后返回的 trade_no 查询,同时核对支付、回调和查询三个环节使用的应用配置。
返回码代表什么
交易查询接口可能返回:
{
"code": "40004",
"msg": "Business Failed",
"subCode": "ACQ.TRADE_NOT_EXIST",
"subMsg": "交易不存在"
}
各字段的含义如下:
code = 40004表示请求已经到达支付宝,但业务处理失败;subCode = ACQ.TRADE_NOT_EXIST表示在当前查询范围内没有找到对应交易;- 金额字段为
0.00,不代表发生过一笔零元交易。这通常只是未查询到有效交易数据时返回的字段值。
这类错误通常不是签名错误。签名或密钥无法通过校验时,一般会返回签名、权限或参数类错误。但签名通过也不代表应用配置正确。如果查询使用了另一个应用的有效密钥,仍可能收到“交易不存在”的结果。
按顺序排查
1. 核对支付和查询使用的订单号
先确认查询请求中的 out_trade_no 与创建支付请求时传入的值逐字符一致,尤其要检查:
- 大小写是否一致;
- 前后是否有空格;
- 内容是否被截断;
- 是否误用了本地数据库主键;
- 重试支付时是否生成了新的订单号;
- 是否把退款单号、支付流水号或其他业务编号当成了商户订单号。
例如,支付时传入:
P202504191538401
查询时也必须传入完全相同的值。
建议在生成支付请求前记录原始 biz_content。后续查询时,应从同一条订单记录中读取 out_trade_no,不要根据时间或其他字段重新拼接。
2. 优先使用支付宝交易号查询
如果同步跳转参数或异步通知中已经包含支付宝交易号 trade_no,优先用它查询。trade_no 是支付宝生成的交易标识,定位具体交易通常比本地订单号更直接。
查询参数示例:
{
"trade_no": "支付宝返回的交易号"
}
如果使用 trade_no 可以查到交易,而使用 out_trade_no 查不到,应检查本地订单号的保存、映射和读取过程。
不要把 out_trade_no 的值传入 trade_no 字段。两者的含义不同:
{
"out_trade_no": "商户订单号",
"trade_no": "支付宝交易号"
}
通常填写其中一个即可,具体要求以当前使用的 SDK 和接口文档为准。
3. 核对 app_id 和商户主体
支付请求、异步通知验签和交易查询应使用同一套应用配置。至少需要核对:
app_id
应用私钥
支付宝公钥或支付宝公钥证书
app_cert_sn
alipay_root_cert_sn
签约商户账号
常见的配置问题有:
- 混用了测试配置和生产配置;
- 多个应用共用同一份配置文件;
- 支付服务和查询服务部署在不同机器上,环境变量不一致;
- 更新证书或密钥后,部分服务仍在读取旧配置;
- 服务商模式下,支付和查询使用的授权商户信息不一致;
- 支付由应用 A 发起,查询却由应用 B 发起。
在不泄露私钥的前提下,可以为每次支付和查询记录以下信息:
app_id
网关地址
out_trade_no
trade_no
请求时间
请求 ID 或链路 ID
部署环境
商户主体标识
4. 检查正式环境和沙箱环境
确认支付请求和查询请求发往同一个环境。
常见情况包括:
- 支付请求发往正式网关,查询请求却发往沙箱网关;
- 前端打开的是正式收银台,后端查询服务仍在加载测试配置;
- 本地、测试和生产环境使用了相同订单号,排查时看错了数据环境。
沙箱和正式环境的数据彼此隔离。即使 out_trade_no 完全相同,也无法跨环境查询。
5. 不要只相信同步跳转页面
电脑网站支付完成后,浏览器通常会跳转到商户配置的 return_url。这个页面主要用于向用户展示结果,不能作为最终入账依据,因为:
- 用户可能直接构造或重复访问页面地址;
- 浏览器跳转可能丢失或中断;
- 页面代码可能只要进入成功路由就显示“支付成功”;
- 同步参数可能属于另一笔订单;
- 页面显示成功时,服务端可能还没有完成订单状态更新。
服务端应按以下步骤确认支付结果:
- 验证支付宝异步通知的签名;
- 核对通知中的
app_id、out_trade_no、total_amount和商户信息; - 检查
trade_status; - 必要时调用交易查询接口复核;
- 所有校验通过后,再更新本地订单状态。
异步通知的典型判断逻辑如下:
if (signVerified
&& expectedAppId.equals(notifyAppId)
&& expectedOutTradeNo.equals(outTradeNo)
&& expectedAmount.compareTo(totalAmount) == 0
&& ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus))) {
// 使用幂等事务更新订单状态
}
这段代码只用于说明校验思路。验签方法和字段获取方式应以当前使用的支付宝 SDK 为准。
6. 检查是否查询得过早
支付完成后立即查询,少数情况下会遇到短暂的数据同步延迟。此时可以进行有限次数的延迟重试,例如:
第 1 次:立即查询
第 2 次:2 秒后
第 3 次:5 秒后
第 4 次:10 秒后
示例伪代码:
int[] delays = {0, 2, 5, 10};
for (int delay : delays) {
if (delay > 0) {
Thread.sleep(delay * 1000L);
}
TradeQueryResult result = queryTrade(outTradeNo);
if (result.isTradeFound()) {
return result;
}
if (!"ACQ.TRADE_NOT_EXIST".equals(result.getSubCode())) {
throw new RuntimeException("查询失败:" + result.getSubCode());
}
}
throw new RuntimeException("多次查询仍不存在,需要核对应用、环境和订单号");
不要无限重试。如果数十秒后仍持续返回 ACQ.TRADE_NOT_EXIST,应检查订单号、应用身份和运行环境,而不是继续轮询。
交易查询示例
以下是使用 Java SDK 的结构示例。不同 SDK 版本的类名可能略有差异,应以项目的实际依赖为准:
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo("P202504191538401");
// 如果已经取得支付宝交易号,也可以改为:
// model.setTradeNo("支付宝交易号");
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println("trade_no: " + response.getTradeNo());
System.out.println("out_trade_no: " + response.getOutTradeNo());
System.out.println("trade_status: " + response.getTradeStatus());
} else {
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());
}
初始化客户端时,需要确认配置来自发起支付的同一个应用:
AlipayClient alipayClient = new DefaultAlipayClient(
gatewayUrl,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
如果项目采用证书模式,应使用对应的证书客户端配置,不能混用普通公钥模式和证书模式的参数。
建议增加的日志
支付、通知和查询日志应能通过 out_trade_no 串联,便于快速定位问题。可以记录:
{
"stage": "trade_query",
"environment": "production",
"app_id": "实际使用的应用 ID",
"out_trade_no": "P202504191538401",
"trade_no": "",
"gateway": "实际请求的网关",
"code": "40004",
"sub_code": "ACQ.TRADE_NOT_EXIST",
"request_time": "实际请求时间",
"trace_id": "业务链路标识"
}
日志中不要保存应用私钥、完整签名密钥、用户付款密码等敏感信息。
还需要检查数据库中是否存在异常记录:
SELECT
out_trade_no,
trade_no,
payment_status,
app_id,
payment_channel,
created_at,
paid_at
FROM payment_order
WHERE out_trade_no = 'P202504191538401';
表名和字段名应按实际系统调整。检查时重点关注订单号是否被覆盖、是否关联到错误的应用,以及是否存在重复订单记录。
如何判断问题位于哪一环
可以根据查询结果缩小排查范围:
trade_no能查到,out_trade_no查不到:本地订单号的保存或映射可能有误。- 两种编号都查不到,但支付宝账单中存在交易:优先检查
app_id、商户主体、授权关系和网关环境。 - 支付宝账单中也不存在交易:页面上的“支付成功”很可能不是可靠的服务端支付结果。
- 稍后重试可以查到:可能存在短暂的同步延迟,应增加有次数上限的重试机制。
- 只有某台服务器查不到:对比各节点的配置文件、环境变量、证书和发布时间。
- 只有服务商代商户场景查不到:检查支付和查询是否使用了同一个授权商户身份。
注意事项
- 不要根据前端成功页面直接发货或充值。
- 处理异步通知时必须验签,并校验订单号、金额、应用和商户身份。
- 更新订单时应保证幂等,避免重复通知造成重复发货。
out_trade_no应在对应业务范围内保持唯一。支付重试时,不要随意复用已经用于其他交易的编号。- 不要通过修改本地订单状态掩盖查询失败。应先确认支付宝侧是否存在真实交易。
- 如果已经确认订单号、
trade_no、应用、商户主体和环境全部一致,但接口仍稳定返回ACQ.TRADE_NOT_EXIST,应整理完整请求时间、app_id、脱敏后的订单号、支付宝trade_no、网关环境和接口响应,再通过支付宝开放平台的技术支持渠道核查。
备注:内容仅供参考。
