PAYATHON 2026

alipay.trade.precreate 沙箱扫码提示订单不存在

支付老李

结论

遇到“订单不存在”,问题通常不在二维码图片本身,而是扫码端与下单环境不一致,或者二维码对应的沙箱订单已经失效、关闭,或被重复覆盖。

可以先检查以下几项:

  1. alipay.trade.precreate 请求是否发到了支付宝沙箱网关。
  2. 扫码时是否使用沙箱版支付宝,并登录了沙箱买家账号。
  3. 当前展示的二维码是否来自本次接口响应中的 qr_code。
  4. out_trade_no 是否全局唯一,有没有被重复使用。
  5. 订单是否已经超时、关闭或支付完成。
  6. HTTP 请求虽然成功,业务响应是否也确实成功。

如果同一套代码“有时可以、有时不可以”,应优先排查环境混用、旧二维码缓存和订单号重复。

先确认沙箱环境完全一致

沙箱创建的订单只能在对应的沙箱环境中查询和支付。请求网关、应用配置和买家账号中,只要有一项混入正式环境,就可能出现查不到订单的情况。

沙箱请求地址通常应配置为:

https://openapi-sandbox.dl.alipaydev.com/gateway.do

还要确认:

  • 使用的是沙箱应用的 app_id。
  • 应用私钥和支付宝公钥与该沙箱应用匹配。
  • 扫码设备安装的是沙箱版支付宝,不是正式版支付宝。
  • 沙箱版支付宝登录的是开放平台提供的沙箱买家账号。
  • 没有使用普通支付宝账号或正式客户端验证沙箱二维码。

如果程序有多套配置,应记录实际生效的网关和 app_id,不能只查看配置文件。容器环境变量、启动参数或配置中心都可能覆盖本地配置。

检查接口是否真正创建成功

HTTP 请求成功不等于预创建订单成功。只有业务响应中的 code 为 10000,并且返回了有效的 qr_code,程序才应该生成二维码。

日志至少应记录以下字段:

  • code
  • msg
  • sub_code
  • sub_msg
  • out_trade_no
  • qr_code
  • 请求使用的 app_id
  • 请求网关
  • 请求和响应时间

Java 示例:

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi-sandbox.dl.alipaydev.com/gateway.do",
    appId,
    merchantPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

String outTradeNo = generateUniqueOutTradeNo();

AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
model.setOutTradeNo(outTradeNo);
model.setTotalAmount("0.01");
model.setSubject("沙箱扫码支付测试");
model.setTimeoutExpress("10m");

request.setBizModel(model);

AlipayTradePrecreateResponse response = alipayClient.execute(request);

if (!response.isSuccess() || !"10000".equals(response.getCode())) {
    throw new IllegalStateException(
        "预创建失败,code=" + response.getCode()
            + ", msg=" + response.getMsg()
            + ", subCode=" + response.getSubCode()
            + ", subMsg=" + response.getSubMsg()
    );
}

String qrCode = response.getQrCode();
if (qrCode == null || qrCode.isBlank()) {
    throw new IllegalStateException("预创建成功但未返回 qr_code");
}

// 应直接将 qrCode 生成二维码,不要自行拼接或截取
System.out.println("out_trade_no=" + outTradeNo);
System.out.println("qr_code=" + qrCode);

示例中的类名和方法采用常见的 Java SDK 用法,具体实现仍要以项目使用的 SDK 版本为准。

确保 out_trade_no 不重复

out_trade_no 是商户订单号,在商户系统内必须保持唯一。开发阶段如果把订单号写死,重复测试时可能会匹配到此前已经支付、关闭或状态异常的订单。

不建议这样写:

model.setOutTradeNo("TEST_ORDER_001");

测试时可以用时间戳加随机值生成订单号:

private static String generateUniqueOutTradeNo() {
    return "SBX"
        + java.time.LocalDateTime.now()
            .format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))
        + java.util.UUID.randomUUID()
            .toString()
            .replace("-", "")
            .substring(0, 8);
}

在业务系统中,最好由订单服务统一生成订单号,并在数据库中添加唯一约束,防止并发请求产生重复订单号。

排除旧二维码和缓存问题

页面、Redis、CDN 或前端状态如果缓存了上一次的 qr_code,用户看到的二维码就可能不属于刚创建的订单。

需要检查:

  • 每次预创建后,是否用新响应覆盖了旧二维码。
  • 二维码图片接口是否被浏览器或 CDN 缓存。
  • 前端轮询刷新时是否出现响应乱序。
  • 多次点击“生成二维码”后,页面展示的订单号是否与后端日志一致。
  • 程序是否从数据库中读取了旧订单的 qr_code。

可以在二维码图片地址中附加订单号,避免浏览器复用旧资源:

<img src="/payment/qrcode?out_trade_no=SBX20260909123000123">

如果二维码由后端生成,可以根据实际需要通过响应头禁用缓存:

Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache

不要修改支付宝返回的 qr_code,也不要先对它进行 URL 解码,再重新拼接。应把原始字符串直接交给二维码生成组件。

查询订单状态进行交叉验证

出现“订单不存在”后,应立即使用相同的沙箱网关、app_id 和密钥调用 alipay.trade.query,查询对应的 out_trade_no。

AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();

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

AlipayTradeQueryResponse queryResponse =
    alipayClient.execute(queryRequest);

System.out.println("code=" + queryResponse.getCode());
System.out.println("msg=" + queryResponse.getMsg());
System.out.println("sub_code=" + queryResponse.getSubCode());
System.out.println("sub_msg=" + queryResponse.getSubMsg());
System.out.println("trade_status=" + queryResponse.getTradeStatus());

根据查询结果,可以进一步缩小排查范围:

  • 查询同样显示订单不存在:检查预创建是否成功、环境是否一致,以及订单号是否正确。
  • 查询可以找到订单,但扫码仍提示不存在:检查扫码客户端、登录账号和二维码是否为旧码。
  • 查询显示订单已经关闭:生成新的商户订单号,然后重新调用预创建接口。
  • 查询显示已经支付:不要再展示原二维码,应进入支付成功处理流程。
  • 查询偶尔成功、偶尔失败:记录每次实际使用的网关、app_id、out_trade_no 和 qr_code,检查多个实例之间的配置是否一致。

注意订单有效期

如果设置了 timeout_express,二维码必须在有效期内使用。例如:

model.setTimeoutExpress("10m");

订单超时后,不要继续展示旧二维码。前端倒计时结束时,应提示用户刷新并创建新订单。

即使没有显式设置该字段,也不应长期保存沙箱二维码并反复测试。沙箱数据可能被重置,其稳定性和数据生命周期也不能等同于正式环境。

多实例部署时检查配置漂移

“偶尔出现”还可能与部分服务实例配置错误有关。例如,两个实例中一个使用沙箱配置,另一个误用了正式环境或另一套沙箱应用配置。请求经过负载均衡后,就会表现得时好时坏。

可以在预创建日志中加入不包含私钥的配置指纹:

instance=payment-service-02
gateway=https://openapi-sandbox.dl.alipaydev.com/gateway.do
app_id=2021************
out_trade_no=SBX20260909123000123
response_code=10000

不要将应用私钥、完整签名原文或其他敏感凭证写入日志。

推荐的排查顺序

  1. 每次测试都生成全新的 out_trade_no。
  2. 确认预创建响应为 code=10000,并保存原始 qr_code。
  3. 使用沙箱版支付宝和沙箱买家账号扫码。
  4. 出错后立即通过 alipay.trade.query 查询同一个订单。
  5. 对比预创建、二维码展示和订单查询三个环节中的 out_trade_no。
  6. 检查页面、接口、Redis 和 CDN 是否复用了旧二维码。
  7. 如果采用集群部署,对比各实例实际生效的网关、app_id 和密钥配置。
  8. 如果配置和调用链都一致,但问题仍能稳定复现,再携带请求时间、商户订单号、接口错误码及相关请求标识,向支付宝开放平台技术支持反馈。

不要只根据客户端弹窗判断接口是否故障。把预创建响应、二维码来源和订单查询结果对应起来,通常可以判断问题出在环境混用、订单状态,还是页面展示了错误的二维码。

备注:内容仅供参考。