PAYATHON 2026

支付宝小程序未支付订单的交易号能否重复使用及有效期

支付小周

结论

可以,前提是上一次创建的支付宝订单仍可支付。

先分清两个编号:

  • out_trade_no:商户系统生成的商户订单号。
  • trade_no:支付宝交易号,通常在调用 alipay.trade.create 创建订单后返回。

用户取消支付页面,不代表交易已经关闭。后端查询并确认订单状态仍为 WAIT_BUYER_PAY 后,可以把原来的 trade_no 返回给小程序前端,再通过 my.tradePay 拉起支付。

不要因为前端返回“取消支付”就创建新订单,也不要跳过状态查询直接复用交易号。

为什么可以重新拉起

取消收银台通常只表示用户中断了这次支付操作,支付宝侧的交易可能仍然存在。只要交易尚未支付成功、关闭或超时,原支付宝交易号就可以继续用于支付。

同一笔业务订单应尽量使用同一个 out_trade_no,以免:

  • 重复创建支付宝交易;
  • 用户重复付款;
  • 支付回调难以对应业务订单;
  • 对账时出现一笔业务订单对应多笔支付宝交易的情况。

out_trade_no 并不是小程序拉起支付时使用的支付宝交易号。调用 my.tradePay 时,一般要传入创建订单接口返回的 trade_no:

my.tradePay({
  tradeNO: tradeNo,
  success: (res) => {
    console.log('支付操作完成', res);
  },
  fail: (err) => {
    console.error('支付调用失败', err);
  }
});

推荐处理流程

用户再次点击“支付”时,后端先根据 out_trade_no 查询支付宝交易状态,例如调用 alipay.trade.query,然后根据查询结果处理。

交易仍待支付

状态为 WAIT_BUYER_PAY 时,可以返回此前保存的 trade_no:

{
  "out_trade_no": "ORDER_202609080001",
  "trade_no": "2026090822000000000000000000",
  "action": "PAY_EXISTING"
}

前端再使用这个 trade_no 拉起支付。

交易已经成功

状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,不要再次拉起支付。后端应根据支付宝查询结果或异步通知更新本地订单状态,并向前端返回“已支付”。

交易已经关闭

状态为 TRADE_CLOSED 时,原 trade_no 已无法继续支付。此时应按照业务规则生成新的商户订单号,并创建新的支付宝交易。

交易关闭后,不建议继续使用原 out_trade_no 创建新交易。更稳妥的处理方式是生成新的支付流水号,同时将其关联到原业务订单。

查询不到交易

如果支付宝返回交易不存在,需要检查:

  • 第一次创建订单是否成功;
  • out_trade_no 是否属于当前支付宝应用和商户;
  • 应用环境是否一致,例如正式环境和沙箱环境是否混用;
  • 本地是否正确保存了创建接口返回的 trade_no。

确认交易确实不存在后,再决定是否重新创建。

后端示例

下面的示例只展示核心判断逻辑。具体 SDK 方法名应以项目实际使用的支付宝 SDK 为准:

public PayResult retryPay(String outTradeNo) {
    LocalOrder order = orderRepository.findByOutTradeNo(outTradeNo);
    if (order == null) {
        throw new IllegalArgumentException("商户订单不存在");
    }

    AlipayTradeQueryResponse response =
        alipayClient.execute(buildQueryRequest(outTradeNo));

    if (!response.isSuccess()) {
        throw new IllegalStateException(
            "支付宝订单查询失败:" + response.getSubMsg()
        );
    }

    String tradeStatus = response.getTradeStatus();

    switch (tradeStatus) {
        case "WAIT_BUYER_PAY":
            String tradeNo = response.getTradeNo();
            orderRepository.saveTradeNo(outTradeNo, tradeNo);
            return PayResult.payExisting(outTradeNo, tradeNo);

        case "TRADE_SUCCESS":
        case "TRADE_FINISHED":
            orderService.markPaid(outTradeNo, response.getTradeNo());
            return PayResult.alreadyPaid(outTradeNo);

        case "TRADE_CLOSED":
            return createNewPaymentFor(order);

        default:
            throw new IllegalStateException(
                "暂不支持的交易状态:" + tradeStatus
            );
    }
}

订单创建成功后,应立即保存支付宝返回的 trade_no,不要完全依赖后续查询获取:

AlipayTradeCreateResponse response =
    alipayClient.execute(createRequest);

if (!response.isSuccess()) {
    throw new IllegalStateException(
        "创建支付宝订单失败:" + response.getSubMsg()
    );
}

orderRepository.savePaymentInfo(
    outTradeNo,
    response.getTradeNo()
);

有效期如何确定

不要脱离订单配置,单独为 trade_no 设置一个固定有效期。它能否再次用于支付,取决于支付宝交易是否仍处于 WAIT_BUYER_PAY 状态。

如果创建交易时设置了 timeout_express,订单通常会按照该配置在超时后进入不可支付状态。例如:

{
  "out_trade_no": "ORDER_202609080001",
  "total_amount": "99.00",
  "subject": "商品订单",
  "timeout_express": "30m"
}

这里的 30m 表示订单支付超时时间为 30 分钟。支持的格式、取值范围、默认期限和实际关单时间,应以当前接入接口的官方文档及支付宝查询结果为准。即使按照本地时间计算尚未超时,最终仍应以 alipay.trade.query 返回的交易状态为准。

注意事项

  • 不要把“用户取消支付”直接视为订单关闭。
  • 不要仅根据前端回调认定支付成功。支付结果应以后端查询和支付宝异步通知为准。
  • 同一个 out_trade_no 应对应同一笔业务交易,金额、商品信息等内容不应随意更改。
  • 重新支付前必须校验订单所属用户,避免他人仅凭 out_trade_no 获取或支付不属于自己的订单。
  • “查询订单”和“创建订单”可能并发执行。后端应使用数据库唯一索引、状态锁或幂等机制,防止重复创建。
  • 如果需要重新计时、修改金额或更换商品,应先确认旧交易已经关闭,再使用新的 out_trade_no 创建交易。
  • 前端字段通常写作 tradeNO,支付宝接口返回字段通常写作 trade_no。注意两者的大小写和命名方式,不要混用。

备注:内容仅供参考。