PAYATHON 2026

APP支付退款:商家订单号提示交易不存在

支付阿杰

结论

“商家订单号”通常是商户生成的 out_trade_no,“订单号”通常是支付平台生成的 trade_no。两者都能用于退款,但使用 out_trade_no 时,传入值必须与支付请求中的值完全一致。退款请求还必须使用原交易对应的沙箱应用、商户账号和环境配置。

如果使用 trade_no 可以退款,说明这笔交易确实存在。改用 out_trade_no 后提示“交易不存在”,通常是因为商户订单号传错、应用或环境不一致,或者传入了本地业务单号、退款单号等其他编号。

常见原因

1. 传入的不是支付时的 out_trade_no

商户系统中往往同时有多个编号,例如:

  • 业务订单号
  • 支付订单号
  • 商户订单号 out_trade_no
  • 支付平台交易号 trade_no
  • 退款请求号 out_request_no

退款接口需要的是支付请求中实际提交的 out_trade_no。不能用 out_request_no、数据库主键或页面展示的其他业务编号代替。

还要确认字符串没有发生变化,例如:

  • 前导零被删除;
  • 复制时混入空格或换行;
  • 大小写被改变;
  • 订单号过长,被数据库截断;
  • JavaScript 把长数字转换为 Number,造成精度丢失。

订单号应始终按字符串保存和传输。

2. 支付和退款使用了不同的沙箱应用

如果支付时使用应用 A 的 app_id,退款时却加载了应用 B 的配置,仅靠 out_trade_no 可能无法找到原交易。常见情况有:

  • 切换过沙箱应用;
  • 本地保存了多套配置文件;
  • 支付服务和退款服务读取了不同的环境变量;
  • 使用了不同的沙箱商户账号;
  • 沙箱数据重置后,仍在使用旧订单号。

可以对照支付日志和退款日志,检查其中的 app_id、网关地址、商户账号和密钥配置是否一致。

3. 请求发到了错误的环境

沙箱订单只能在沙箱环境中查询和退款。如果支付请求使用沙箱网关,退款请求却发往正式环境网关,接口会提示交易不存在。

以支付宝为例,沙箱请求通常使用以下网关:

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

实际地址应以当前接入平台的最新配置说明为准。正式环境和沙箱环境的配置不能混用。

4. out_trade_no 在业务系统中被重复使用

商户订单号应保持唯一。重复使用同一个 out_trade_no,可能让本地记录无法与平台交易正确对应。在多应用、多商户或反复测试的场景中,这类问题更容易出现。

订单号可以包含环境标识和随机成分,例如:

SBX202609141530001234

排查步骤

  1. 找到创建 APP 支付订单时的原始请求日志。
  2. 从日志中复制实际提交的 out_trade_no,不要根据页面展示值或数据库记录推测。
  3. 找到支付成功响应中返回的 trade_no。
  4. 确认这两个编号在本地支付记录中对应同一笔交易。
  5. 对比支付请求和退款请求使用的 app_id、网关地址及商户账号。
  6. 检查 out_trade_no 是否被截断、转换成数字或混入空白字符。
  7. 调用交易查询接口,验证 out_trade_no 能否找到对应交易。
  8. 如果使用 out_trade_no 查询失败,但使用 trade_no 查询成功,应优先修正订单号映射和环境配置。退款时可以暂时使用 trade_no。

日志中不要记录应用私钥、支付密钥或完整的签名材料。

代码示例

下面以支付宝 Java SDK 的典型调用方式为例。不同 SDK 版本中的类名和方法可能有所差异,请以项目实际使用的 SDK 为准。

使用商户订单号退款:

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setOutTradeNo("SBX202609141530001234");
model.setRefundAmount("10.00");
model.setOutRequestNo("REFUND202609141530001234");

request.setBizModel(model);

AlipayTradeRefundResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
    System.out.println("退款请求成功");
} else {
    System.out.println(response.getCode());
    System.out.println(response.getSubCode());
    System.out.println(response.getSubMsg());
}

如果已经取得支付平台交易号,也可以使用 trade_no:

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setTradeNo("2026091422000000000000000000");
model.setRefundAmount("10.00");
model.setOutRequestNo("REFUND202609141530001234");

request.setBizModel(model);

AlipayTradeRefundResponse response = alipayClient.execute(request);

一次请求最好只使用一种交易定位方式,避免两个字段的内容不一致:

// 二选一
model.setOutTradeNo(originalOutTradeNo);
// model.setTradeNo(originalTradeNo);

注意事项

  • out_trade_no 是原支付订单号,out_request_no 是退款请求号,两者用途不同。
  • out_request_no 应保持唯一。重复提交相同的退款请求号,通常会被视为同一笔退款请求。
  • 退款金额不能超过该交易当前的可退金额。
  • 不要只看“交易不存在”这段提示,还应记录并核对接口返回的 code、sub_code 和 sub_msg。
  • 沙箱数据不能代表正式环境数据。切换环境后,需要重新创建支付订单。
  • 如果原始支付日志中的 out_trade_no 仍然无法查询,但同一笔交易的 trade_no 可以正常退款,应携带脱敏后的请求参数、响应码、app_id 和交易时间,请支付平台技术支持协助核查。

备注:内容仅供参考。