alipay.trade.precreate 沙箱扫码提示订单不存在
结论
遇到“订单不存在”,问题通常不在二维码图片本身,而是扫码端与下单环境不一致,或者二维码对应的沙箱订单已经失效、关闭,或被重复覆盖。
可以先检查以下几项:
alipay.trade.precreate请求是否发到了支付宝沙箱网关。- 扫码时是否使用沙箱版支付宝,并登录了沙箱买家账号。
- 当前展示的二维码是否来自本次接口响应中的
qr_code。 out_trade_no是否全局唯一,有没有被重复使用。- 订单是否已经超时、关闭或支付完成。
- HTTP 请求虽然成功,业务响应是否也确实成功。
如果同一套代码“有时可以、有时不可以”,应优先排查环境混用、旧二维码缓存和订单号重复。
先确认沙箱环境完全一致
沙箱创建的订单只能在对应的沙箱环境中查询和支付。请求网关、应用配置和买家账号中,只要有一项混入正式环境,就可能出现查不到订单的情况。
沙箱请求地址通常应配置为:
https://openapi-sandbox.dl.alipaydev.com/gateway.do
还要确认:
- 使用的是沙箱应用的
app_id。 - 应用私钥和支付宝公钥与该沙箱应用匹配。
- 扫码设备安装的是沙箱版支付宝,不是正式版支付宝。
- 沙箱版支付宝登录的是开放平台提供的沙箱买家账号。
- 没有使用普通支付宝账号或正式客户端验证沙箱二维码。
如果程序有多套配置,应记录实际生效的网关和 app_id,不能只查看配置文件。容器环境变量、启动参数或配置中心都可能覆盖本地配置。
检查接口是否真正创建成功
HTTP 请求成功不等于预创建订单成功。只有业务响应中的 code 为 10000,并且返回了有效的 qr_code,程序才应该生成二维码。
日志至少应记录以下字段:
codemsgsub_codesub_msgout_trade_noqr_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
不要将应用私钥、完整签名原文或其他敏感凭证写入日志。
推荐的排查顺序
- 每次测试都生成全新的
out_trade_no。 - 确认预创建响应为
code=10000,并保存原始qr_code。 - 使用沙箱版支付宝和沙箱买家账号扫码。
- 出错后立即通过
alipay.trade.query查询同一个订单。 - 对比预创建、二维码展示和订单查询三个环节中的
out_trade_no。 - 检查页面、接口、Redis 和 CDN 是否复用了旧二维码。
- 如果采用集群部署,对比各实例实际生效的网关、
app_id和密钥配置。 - 如果配置和调用链都一致,但问题仍能稳定复现,再携带请求时间、商户订单号、接口错误码及相关请求标识,向支付宝开放平台技术支持反馈。
不要只根据客户端弹窗判断接口是否故障。把预创建响应、二维码来源和订单查询结果对应起来,通常可以判断问题出在环境混用、订单状态,还是页面展示了错误的二维码。
备注:内容仅供参考。
