PAYATHON 2026

支付宝小程序支付提示“付款失败,订单不存在或者状态异常”

支付小周

结论

这个提示通常不代表“小程序无法调起收银台”,而是支付宝根据 tradeNO 查询订单时,没有找到对应订单,发现订单不属于当前应用或商户,或者订单已经无法支付。

建议先检查以下几项:

  1. my.tradePay 接收的应是 alipay.trade.create 返回的支付宝交易号 trade_no,不能传商户订单号 out_trade_no。
  2. 创建订单和发起支付必须使用同一套环境、应用及商户身份,包括网关环境、app_id、签约商户和应用授权关系。
  3. 订单不能已经支付、关闭、退款或超时失效。
  4. out_trade_no 必须唯一。重复创建时,不能修改金额、买家或其他重要参数。
  5. 后端只有在确认 alipay.trade.create 业务调用成功后,才能把 trade_no 返回给小程序。

最常见的参数错误

把 out_trade_no 当成 tradeNO

这三个字段的用途不同:

  • out_trade_no:商户系统生成的订单号。
  • trade_no:支付宝创建交易后返回的支付宝交易号。
  • tradeNO:小程序 my.tradePay 的入参,其值应来自支付宝返回的 trade_no。

正确流程如下:

商户后端生成 out_trade_no
        ↓
调用 alipay.trade.create
        ↓
取得响应中的 trade_no
        ↓
将 trade_no 返回小程序
        ↓
my.tradePay({ tradeNO: trade_no })

小程序端示例:

my.tradePay({
  tradeNO: serverResponse.trade_no,
  success: (res) => {
    console.log('支付结果', res);
  },
  fail: (err) => {
    console.error('支付调用失败', err);
  }
});

下面这种写法通常是错误的:

my.tradePay({
  tradeNO: serverResponse.out_trade_no
});

还要注意字段名的大小写。小程序接口使用 tradeNO,不是 trade_no 或 tradeNo。

创建交易实际上没有成功

后端调用 alipay.trade.create 后,不能只检查 HTTP 状态码,也不能在调用失败时继续向前端返回空值或商户订单号。还应检查支付宝业务响应中的 code、msg、sub_code 和 sub_msg。

伪代码示例:

AlipayTradeCreateResponse response = alipayClient.execute(request);

if (!response.isSuccess()) {
    log.error(
        "alipay.trade.create failed, code={}, msg={}, subCode={}, subMsg={}",
        response.getCode(),
        response.getMsg(),
        response.getSubCode(),
        response.getSubMsg()
    );
    throw new IllegalStateException("创建支付宝交易失败");
}

String tradeNo = response.getTradeNo();
if (tradeNo == null || tradeNo.isBlank()) {
    throw new IllegalStateException("支付宝未返回 trade_no");
}

return tradeNo;

不同语言和 SDK 版本使用的类名、方法可能不同,但判断标准一致:业务响应必须明确成功,并且包含有效的 trade_no,之后才能调用 my.tradePay。

应用、商户或环境不一致

即使 trade_no 没有传错,如果创建订单和发起支付时使用的身份或环境不同,支付宝也可能把它识别为订单不存在或状态异常。

逐项核对以下内容:

  • 后端调用接口使用的 app_id 是否与当前小程序对应的应用一致。
  • 创建交易的商户 PID 是否属于当前签约主体。
  • 如果由第三方应用代商户调用,app_auth_token 是否正确、有效,且属于当前商户。
  • 是否把沙箱环境创建的订单拿到正式环境使用,或者反过来。
  • 是否误用了另一套配置文件、密钥、网关地址或部署环境。
  • 在多商户系统中,是否把 A 商户创建的 trade_no 交给了 B 商户的小程序。
  • 前端是否因缓存、并发请求或变量复用,取到了上一笔订单的 trade_no。

建议分别在创建订单和前端支付的位置记录这些关联信息:

out_trade_no
trade_no
app_id
商户标识
用户标识
total_amount
创建时间
当前运行环境
支付宝响应 code/sub_code

日志中不要写入应用私钥、用户访问令牌等敏感信息。

out_trade_no 重复或订单参数发生变化

out_trade_no 应在商户维度内保持唯一,并且始终与同一笔业务订单绑定。常见问题有:

  • 用户重试时再次调用创建接口,但仍使用原来的 out_trade_no。
  • 同一个 out_trade_no 对应的 total_amount 与首次创建时不同。
  • 测试代码长期使用固定订单号。
  • 多台服务器使用时间戳生成订单号,导致并发碰撞。
  • 原订单已经关闭,却仍使用原订单号发起支付。
  • 同一订单更换了买家账号或商户主体。

可以按下面的方式生成不重复的商户订单号:

String outTradeNo =
    "MP" + System.currentTimeMillis()
    + UUID.randomUUID().toString().replace("-", "").substring(0, 8);

如果业务需要“重新支付”,应先查询原订单状态:

  • 原订单仍可支付:继续使用原交易信息。
  • 原订单已经成功:不要再次创建或扣款,直接更新本地状态。
  • 原订单已经关闭:按照业务规则生成新的 out_trade_no,再创建新交易。
  • 订单状态暂时不明确:先主动查询,不要反复创建订单。

订单已经处于不可支付状态

订单处于以下状态时,通常不能再次发起正常付款:

  • 已支付成功。
  • 已关闭或已撤销。
  • 已退款,或业务已经进入退款流程。
  • 已超过交易允许的支付时间。
  • 商户本地已经取消订单,但前端仍保存着旧的 trade_no。
  • 同一笔订单已在其他页面或设备上完成支付。

最终状态不能只看小程序回调。用户关闭收银台、网络中断或回调没有到达时,小程序端得到的结果可能与支付宝服务端记录的状态不同。

应通过 alipay.trade.query 查询订单:

{
  "out_trade_no": "MP202609090001"
}

也可以使用支付宝交易号:

{
  "trade_no": "202609092200xxxxxxxxxxxx"
}

查询后按照实际交易状态处理。常见状态名称包括 WAIT_BUYER_PAY、TRADE_SUCCESS、TRADE_FINISHED 和 TRADE_CLOSED。各状态的具体含义以及当前接口的支持情况,应以实际响应和所用开放平台文档为准。

买家标识不正确

小程序支付通常需要把当前支付宝用户与创建的交易正确关联。后端不应采用前端随意传入或从历史缓存中取得的用户标识。

需要确认:

  • 用户标识来自当前小程序用户的授权流程。
  • 授权码没有被重复使用或错误缓存。
  • 创建订单时使用的买家标识与当前发起支付的用户一致。
  • 用户切换账号后,没有继续使用上一个账号的订单或用户标识。
  • 没有混用不同体系的用户标识字段。

不同接入模式对字段的要求可能不同。例如,直连商户与第三方应用代调用使用的参数并不完全相同。应根据实际签约产品和当前 alipay.trade.create 文档,确定应使用 buyer_id 还是其他对应字段,不能只根据字段名称判断。

金额和订单内容异常

创建交易时,还要检查这些业务参数:

  • total_amount 是否使用合法的金额格式。
  • 金额是否大于零,并符合产品限制。
  • 金额单位是否按照接口要求填写,避免直接把“分”当成“元”。
  • subject 是否为空或超过长度限制。
  • product_code 是否与实际签约的支付产品一致。
  • 可选的超时时间参数是否使订单过早失效。
  • 请求 JSON 是否存在字段嵌套、序列化或字符编码问题。

金额建议使用十进制定点类型处理,避免直接使用二进制浮点数计算:

BigDecimal totalAmount = new BigDecimal("9.90");

不要使用:

double totalAmount = 9.90;

推荐排查顺序

1. 检查小程序实际收到的值

打印脱敏后的请求信息,确认 tradeNO:

  • 值不为空。
  • 前后没有空格。
  • 没有在 JSON 序列化时变成其他字段。
  • 与本次 alipay.trade.create 返回的 trade_no 完全一致。
  • 不是 out_trade_no。

2. 检查创建接口的完整响应

主要查看:

code
msg
sub_code
sub_msg
trade_no
out_trade_no

如果创建接口调用失败,应直接处理创建失败的原因,不能继续调用 my.tradePay。

3. 主动查询订单

使用本次保存的 trade_no 或 out_trade_no 调用 alipay.trade.query:

  • 查询不到:检查交易是否确实创建成功,以及环境、应用和商户是否一致。
  • 能查询到且状态为待支付:检查买家身份、应用归属和前端传参。
  • 能查询到但已经关闭:创建新订单,不要继续支付旧订单。
  • 能查询到且已经成功:按支付成功处理,避免重复支付。

4. 对照前后端日志

以 out_trade_no 作为业务链路标识,把这些日志串联起来:

生成商户订单
调用 alipay.trade.create
收到 trade_no
接口返回给小程序
小程序调用 my.tradePay
支付通知或主动查询

如果 trade_no 在其中任何一步发生变化,通常是缓存、并发覆盖或字段映射出了问题。

注意事项

支付结果应以后端异步通知或主动查询为准,不能只依赖 my.tradePay 的前端回调。收到异步通知后,还要验证签名,并核对 app_id、商户身份、out_trade_no、金额和交易状态。

同一笔通知可能重复到达,因此订单更新必须具备幂等性。例如,只有本地订单仍处于“待支付”状态时,才执行支付成功后的业务动作,以免重复发货或重复记账。

如果完成以上检查后仍然找不到原因,应保留支付宝返回的 code、sub_code、sub_msg、接口请求时间、out_trade_no 和脱敏后的 trade_no。仅凭“订单不存在或者状态异常”这句展示文案,无法准确判断问题所在。最终应以服务端接口响应和订单查询结果为准。

备注:内容仅供参考。