PAYATHON 2026

网站支付对接中使用 out_trade_no 查询提示交易不存在

支付小周

结论

out_trade_no 是商户订单号,trade_no 是支付宝生成的交易号,二者不能混用。

pageExecute 只负责生成跳转到支付宝收银台的 HTML 表单,不会返回 trade_no。浏览器将表单提交到支付宝网关,并由支付宝受理、创建交易后,查询接口才可能查到这笔交易。如果只是调用了 pageExecute,或者用户还没有进入收银台,此时使用 out_trade_no 查询并收到“交易不存在”的结果,通常属于正常情况。

支付完成后,可以从异步通知、同步跳转参数或查询接口结果中取得 trade_no,不能从 pageExecute 返回的表单内容中提取。

原因

网站支付通常要经过以下流程:

  1. 商户系统生成唯一的 out_trade_no。
  2. 调用 alipay.trade.page.pay 的 pageExecute 生成 HTML 表单。
  3. 浏览器提交表单并跳转到支付宝收银台。
  4. 支付宝受理请求并创建交易。
  5. 支付完成后,支付宝通过 notify_url 发送异步通知。

下面这段代码只会生成等待浏览器提交的页面内容:

AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
String form = response.getBody();

此时的 form 通常是一段包含隐藏字段和自动提交脚本的 HTML。它不是支付结果,也不包含可供业务使用的支付宝 trade_no。

如果服务端生成表单后,没有将它响应给浏览器并实际提交到支付宝,支付宝侧就还没有对应的交易。此时使用 out_trade_no 查询,自然会返回“交易不存在”。

正确的处理方式

1. 商户自行生成并保存 out_trade_no

提交支付请求前,先生成唯一的商户订单号,并将它与本地订单绑定。

String outTradeNo = generateUniqueOrderNo();

orderService.saveOrder(outTradeNo, "WAIT_PAY");

在同一个商户应用下,out_trade_no 应当保持唯一。后续进行支付查询、异步通知校验和订单对账时,都要使用这个原始值。

2. 将 pageExecute 返回的表单输出给浏览器

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);

AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(outTradeNo);
model.setProductCode("FAST_INSTANT_TRADE_PAY");
model.setTotalAmount("0.01");
model.setSubject("测试订单");

request.setBizModel(model);

AlipayTradePagePayResponse response = alipayClient.pageExecute(request);

responseHttp.setContentType("text/html;charset=UTF-8");
responseHttp.getWriter().write(response.getBody());
responseHttp.getWriter().flush();

浏览器需要真正执行并提交这段 HTML,进入支付宝收银台。只在服务端打印或保存 response.getBody(),不会在支付宝侧创建交易。

3. 查询时继续使用原始 out_trade_no

AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();

AlipayTradeQueryModel queryModel = new AlipayTradeQueryModel();
queryModel.setOutTradeNo(outTradeNo);

queryRequest.setBizModel(queryModel);

AlipayTradeQueryResponse queryResponse =
        alipayClient.execute(queryRequest);

if (queryResponse.isSuccess()) {
    String tradeNo = queryResponse.getTradeNo();
    String tradeStatus = queryResponse.getTradeStatus();

    System.out.println("trade_no: " + tradeNo);
    System.out.println("trade_status: " + tradeStatus);
} else {
    System.out.println("sub_code: " + queryResponse.getSubCode());
    System.out.println("sub_msg: " + queryResponse.getSubMsg());
}

如果支付宝已经受理请求并创建交易,但买家还没有付款,查询结果通常会显示待支付状态。具体状态和值应以实际接口返回及当前支付宝接口文档为准。

如果仍然返回“交易不存在”,可以逐项检查:

  • 支付表单是否已经提交到支付宝网关。
  • 查询使用的 out_trade_no 是否与支付请求完全一致。
  • 支付请求和查询请求是否使用同一个 app_id。
  • 两次请求是否处于相同环境,即都使用沙盒环境或都使用正式环境。
  • 两次请求是否使用同一商户身份和正确的签名配置。
  • 是否在支付宝受理交易前就立即发起了查询。

4. 从异步通知中获取 trade_no

支付成功后,支付宝会向 notify_url 发送通知。验签通过后,可以读取其中的 trade_no 和 out_trade_no:

Map<String, String> params = new HashMap<>();

Map<String, String[]> requestParams = request.getParameterMap();
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
    params.put(entry.getKey(), String.join(",", entry.getValue()));
}

boolean signVerified = AlipaySignature.rsaCheckV1(
        params,
        alipayPublicKey,
        "UTF-8",
        "RSA2"
);

if (signVerified) {
    String outTradeNo = params.get("out_trade_no");
    String tradeNo = params.get("trade_no");
    String tradeStatus = params.get("trade_status");

    // 还需核对 app_id、seller_id、订单金额等业务字段
    // 更新订单时应保证幂等
}

实际业务中,应将异步通知作为确认支付结果的主要依据。同步 return_url 主要用于页面跳转和结果展示,不能单独作为订单支付成功的依据。

参数不要混用

按支付宝交易号查询时,应设置 trade_no:

AlipayTradeQueryModel queryModel = new AlipayTradeQueryModel();
queryModel.setTradeNo(alipayTradeNo);

按商户订单号查询时,应设置 out_trade_no:

AlipayTradeQueryModel queryModel = new AlipayTradeQueryModel();
queryModel.setOutTradeNo(merchantOrderNo);

不要把支付宝流水号填入 out_trade_no。即使某次测试看似返回了结果,也不能说明两个字段可以互换。遇到这种情况,应检查代码最终生成的请求参数,以及沙盒页面中各字段的真实含义。

注意事项

  • pageExecute 返回的是收银台跳转表单,不是交易创建结果,因此无法从中可靠获取 trade_no。
  • 用户打开并提交收银台页面后,再根据业务需要查询订单。
  • 查询不到交易不一定表示支付失败,也可能是交易请求还没有到达支付宝。
  • 本地订单应先保存为待支付状态,不能用查询接口代替本地订单管理。
  • 保存支付宝异步通知中的 trade_no,供退款、对账和后续交易查询使用。
  • 异步通知可能重复发送,订单更新逻辑必须保证幂等。
  • 沙盒环境与正式环境的数据完全隔离,不能跨环境查询。

备注:内容仅供参考。