PAYATHON 2026

alipay.trade.close 返回 ACQ.TRADE_NOT_EXIST,如何阻止订单支付?

支付老李

结论

ACQ.TRADE_NOT_EXIST 表示支付宝当前找不到与该 out_trade_no 对应的交易,所以 alipay.trade.close 没有可关闭的对象。

用户已经打开支付宝收银台,不代表支付宝已经生成了可查询、可关闭的交易。在部分支付流程中,支付链接、收银台会话与正式交易记录并不是同时创建的。因此,立即调用 alipay.trade.close 不一定能阻止用户付款。

建议按以下方式处理:

  1. 先在商户系统中将订单设为“已取消”或“禁止支付”。
  2. 查询支付宝交易。只有交易存在且状态为 WAIT_BUYER_PAY 时,才调用 alipay.trade.close。
  3. 如果返回 ACQ.TRADE_NOT_EXIST,在短时间内延迟重查,避免遗漏稍后才创建的交易。
  4. 如果支付已经完成,则不能再关闭交易,只能按业务规则调用退款接口。
  5. 创建支付请求时设置合理的支付有效期,缩短旧支付入口可继续使用的时间。

为什么查询和关闭都提示交易不存在

out_trade_no 是商户生成的订单号。拥有这个编号,不代表支付宝已经创建了对应的交易记录。

支付过程中可能涉及几个不同的对象:

  • 商户系统中的业务订单;
  • 跳转支付宝时使用的支付请求或收银台会话;
  • 支付宝正式建立的交易记录。

即使用户已经进入收银台,甚至停留在输入密码的页面,支付宝是否已建立交易记录,仍取决于具体支付产品和当前支付阶段。如果交易尚未落库,以下两个接口都会返回交易不存在:

  • alipay.trade.query
  • alipay.trade.close

还需要排查这些常见问题:

  • 查询和发起支付使用了不同的 app_id;
  • 正式环境与沙箱环境混用;
  • out_trade_no 传错,或者重新生成支付请求时更换了订单号;
  • 支付请求并未成功提交到支付宝;
  • 服务商模式下使用了不同的商户授权上下文;
  • 支付刚发起便立即查询,交易数据还不能被查询。

因此,ACQ.TRADE_NOT_EXIST 不能直接理解为“用户绝对无法继续支付”。它只表示在本次查询使用的应用、商户和环境下,暂时找不到这笔交易。

推荐的处理流程

1. 先关闭商户自己的订单

业务上决定不再允许支付时,应先原子地修改本地订单状态,例如:

PAYING -> CANCELED

后续创建支付请求、处理支付结果和执行业务履约时,都必须检查该状态。不能只根据支付宝接口的调用结果判断本地订单是否有效。

如果用户再次请求支付,本地接口应直接拒绝:

if (order.getStatus() == OrderStatus.CANCELED) {
    throw new IllegalStateException("订单已取消,不能继续支付");
}

这样可以防止用户从商户页面重新获取支付链接,但不能保证已经打开的支付宝收银台立即失效。

2. 查询支付宝交易状态

查询时,应使用与发起支付时完全相同的应用、环境和商户授权信息:

AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("""
{
  "out_trade_no": "ORDER_202609090001"
}
""");

AlipayTradeQueryResponse response = alipayClient.execute(request);

根据查询结果分别处理:

查询结果处理方式
WAIT_BUYER_PAY调用 alipay.trade.close
TRADE_SUCCESS已支付,不能关闭;根据业务决定是否退款
TRADE_FINISHED交易已完成,通常不能关闭
TRADE_CLOSED无需重复关闭
ACQ.TRADE_NOT_EXIST本地保持取消状态,并进行有限次数的延迟重查

3. 交易存在后再关闭

查询到待付款交易后,关闭操作才有明确的对象:

AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
request.setBizContent("""
{
  "out_trade_no": "ORDER_202609090001"
}
""");

AlipayTradeCloseResponse response = alipayClient.execute(request);

if (!response.isSuccess()) {
    String subCode = response.getSubCode();
    String subMsg = response.getSubMsg();
    // 记录结果,并根据 subCode 判断是否需要重新查询交易状态
}

如果已经取得支付宝交易号,也可以传入 trade_no。无论使用哪个编号,都要确认它属于当前应用和商户上下文。

关闭成功后,建议再次查询,确认最终状态已经变为 TRADE_CLOSED。关闭逻辑还应支持重复执行,避免任务重试引发异常。

4. 对交易不存在进行延迟重查

如果用户可能仍停留在已经打开的收银台中,支付宝交易可能稍后才会创建。可以为此设置一个范围明确的补偿任务,例如:

public void cancelPayment(String outTradeNo) {
    // 第一步:原子地取消本地订单
    orderService.cancelIfUnpaid(outTradeNo);

    // 第二步:异步检查支付宝交易
    paymentCloseTask.schedule(outTradeNo);
}

public void checkAndClose(String outTradeNo) {
    TradeQueryResult result = alipayGateway.query(outTradeNo);

    switch (result.status()) {
        case WAIT_BUYER_PAY:
            alipayGateway.close(outTradeNo);
            break;

        case TRADE_SUCCESS:
            refundService.createRefundIfRequired(outTradeNo);
            break;

        case TRADE_FINISHED:
        case TRADE_CLOSED:
            break;

        case NOT_EXIST:
            // 在限定时间和次数内延迟重试,不能无限轮询
            paymentCloseTask.retryLater(outTradeNo);
            break;
    }
}

重查间隔、次数和持续时间应根据实际支付产品、订单有效期及业务风险确定,不能假设所有场景都适用同一个固定值。

已经支付成功时如何处理

查询与关闭之间存在竞态。例如:

  1. 系统查询到 WAIT_BUYER_PAY;
  2. 用户完成密码验证;
  3. 系统随后调用关闭接口;
  4. 由于交易已经支付成功,关闭失败。

即使先查询再关闭,也无法保证一定能赶在用户付款前完成。收到支付成功通知后,系统必须重新检查本地订单状态:

if (localOrder.isCanceled() && alipayTrade.isPaid()) {
    // 不进行履约,并按业务规则发起退款或进入人工处理
    refundService.createRefundIfRequired(localOrder, alipayTrade);
}

退款通常使用 alipay.trade.refund。是否自动退款、退多少以及是否需要人工审核,应按具体业务规则处理。

处理异步通知时还应做到:

  • 验证支付宝通知签名;
  • 校验 app_id、商户身份、订单号和金额;
  • 通过数据库事务或状态机避免重复履约;
  • 保证关闭、退款和通知处理的幂等性;
  • 即使本地订单已经取消,也不能直接忽略支付成功通知。

从支付请求阶段限制有效期

如果使用的支付接口支持支付超时参数,可以在创建支付请求时设置合理的有效期,例如该接口支持的 timeout_express 或 time_expire。

可用参数、格式和限制取决于具体支付产品及当前接口文档,不能把一个支付接口的配置直接套用到另一个接口。设置超时时间后,仍要保留本地取消、交易关闭和支付后退款等补偿逻辑。超时配置不能代替完整的订单状态管理。

注意事项

  • alipay.trade.close 只能关闭已经存在且允许关闭的支付宝交易,不能关闭单纯的商户订单号或收银台页面。
  • 取消本地订单不会自动关闭支付宝交易,关闭支付宝交易也不会自动修改本地业务状态。
  • 不要无限重试 ACQ.TRADE_NOT_EXIST。应设置重试期限,并记录最终处理结果。
  • 未确认交易状态前不要直接退款。退款接口要求交易已经支付成功。
  • 不能只根据同步返回判断支付是否成功。最终状态应结合主动查询和通过验签的异步通知来确认。
  • 如果应用、环境、订单号和商户授权上下文确认完全一致,但经过较长时间仍查询不到交易,需要结合支付接口的原始请求、响应和支付宝网关日志继续排查。

备注:内容仅供参考。