PAYATHON 2026

alipay.trade.close 返回 ACQ.TRADE_NOT_EXIST 如何解决?

支付老李

结论

ACQ.TRADE_NOT_EXIST 表示支付宝在当前应用和运行环境中找不到 out_trade_no 对应的交易。

商户系统创建了订单,并不代表支付宝侧已经创建交易。排查时,应先确认支付请求已成功提交,再核对应用、运行环境和订单号是否一致。如果支付刚发起就立即调用关闭接口,交易可能暂时还无法查询,可以稍后查询并进行有限重试。

常见原因

1. 订单只创建在商户系统中

如果订单只是写入了本地数据库,尚未成功调用以下任一支付接口,支付宝侧通常没有可关闭的交易:

  • alipay.trade.create
  • alipay.trade.precreate
  • alipay.trade.page.pay
  • alipay.trade.wap.pay
  • alipay.trade.app.pay

这种情况下直接调用 alipay.trade.close,即使 out_trade_no 完全正确,也会返回“交易不存在”。

对于返回表单、跳转地址或客户端支付参数的接口,还要确认这些内容已被实际提交或使用。仅生成表单或签名字符串,并不会在支付宝侧创建交易。

2. 创建交易后立即关闭

支付请求刚提交时,交易数据可能还没有进入可查询、可关闭的状态。如果程序随后立即调用关闭接口,就可能暂时查不到交易。

不建议只靠固定等待时间解决。更稳妥的处理方式是先调用 alipay.trade.query:

  • 查到交易后,根据交易状态判断是否需要关闭。
  • 如果仍返回 ACQ.TRADE_NOT_EXIST,可以延迟后进行有限次数的重试。
  • 多次查询仍不存在时,应检查支付请求是否真正提交成功。

3. 创建和关闭交易使用了不同环境

支付宝沙箱与生产环境的数据彼此隔离。例如:

  • 创建交易时使用沙箱网关,关闭时却使用生产网关。
  • 创建交易时使用生产环境,查询或关闭时改用了沙箱环境。

创建、查询和关闭交易必须使用同一套网关、应用配置和密钥。

4. 使用了不同的 app_id

out_trade_no 是商户订单号,但对应交易仍归属于创建它的支付宝应用。如果创建和关闭交易时使用的 app_id 不同,即使订单号相同,当前应用也可能找不到这笔交易。

服务商代商户调用时,还需要核对授权关系、代调用参数和交易的实际归属。

5. 订单号在传递过程中发生变化

重点检查以下情况:

  • 订单号是否带有前后空格、换行符或不可见字符。
  • 数字订单号是否被转换成浮点数,造成精度丢失。
  • 是否误用了数据库中的其他编号。
  • 创建和关闭时是否采用了不同的格式化或截断方式。
  • 是否错误复用了测试订单号。

建议在生成、存储、签名和传输 out_trade_no 的整个过程中,始终将它作为字符串处理。

推荐排查步骤

第一步:核对创建交易的请求记录

找到创建该订单时实际发送给支付宝的请求,确认请求中包含:

out_trade_no=202201291338512515593332

还要确认支付宝接口返回成功,或者支付表单、跳转请求、客户端支付参数已经被实际使用。只在本地生成请求参数,不能证明支付宝侧已经创建交易。

第二步:调用交易查询接口

使用相同的 app_id、网关和授权上下文调用 alipay.trade.query:

{
  "out_trade_no": "202201291338512515593332"
}

根据查询结果分别处理:

  • WAIT_BUYER_PAY:交易存在且尚未支付,可以调用关闭接口。
  • TRADE_SUCCESS:交易已经支付成功,不应再关闭,应按业务需要处理退款。
  • TRADE_CLOSED:交易已经关闭,无需重复调用关闭接口。
  • ACQ.TRADE_NOT_EXIST:支付宝侧仍未找到交易,应继续检查创建请求、运行环境和应用配置。

第三步:确认运行环境和应用身份

对比创建、查询和关闭三个请求中的以下信息:

gateway
app_id
method
out_trade_no
app_auth_token(如使用)

这些信息必须处于同一交易上下文。日志中不要记录应用私钥、完整签名材料或其他敏感凭证。

第四步:对短暂延迟进行有限重试

如果可以确认支付请求刚刚提交成功,可以在业务允许的范围内延迟查询。重试必须设置次数上限,不能无限循环调用。

String outTradeNo = "202201291338512515593332";

for (int attempt = 1; attempt <= 3; attempt++) {
    AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
    queryRequest.setBizContent(
        "{\"out_trade_no\":\"" + outTradeNo + "\"}"
    );

    AlipayTradeQueryResponse queryResponse = alipayClient.execute(queryRequest);

    if (queryResponse.isSuccess()) {
        String tradeStatus = queryResponse.getTradeStatus();

        if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
            AlipayTradeCloseRequest closeRequest = new AlipayTradeCloseRequest();
            closeRequest.setBizContent(
                "{\"out_trade_no\":\"" + outTradeNo + "\"}"
            );

            AlipayTradeCloseResponse closeResponse =
                alipayClient.execute(closeRequest);

            if (!closeResponse.isSuccess()) {
                throw new IllegalStateException(
                    closeResponse.getSubCode() + ": " +
                    closeResponse.getSubMsg()
                );
            }
        }

        break;
    }

    if (!"ACQ.TRADE_NOT_EXIST".equals(queryResponse.getSubCode())) {
        throw new IllegalStateException(
            queryResponse.getSubCode() + ": " +
            queryResponse.getSubMsg()
        );
    }

    Thread.sleep(1000L * attempt);
}

示例中的重试次数和时间间隔只是一种实现方式。生产环境应结合并发量、超时策略和支付宝接口调用限制进行调整。Web 请求线程不适合长时间执行 Thread.sleep,可以改用延迟队列或定时任务完成重试。

正确的业务处理方式

关闭超时订单时,可以按以下流程处理:

商户订单到达关闭时间
        ↓
调用 alipay.trade.query
        ↓
WAIT_BUYER_PAY → 调用 alipay.trade.close
TRADE_SUCCESS  → 标记已支付,不关闭
TRADE_CLOSED   → 同步本地关闭状态
交易不存在     → 核对支付请求,按策略有限重试

如果订单从未真正发起支付宝支付,通常只需要关闭商户本地订单,无需调用 alipay.trade.close。

注意事项

  • 不要根据本地订单状态直接认定支付宝交易已经存在。
  • 创建、查询和关闭接口必须使用相同的 out_trade_no、运行环境和应用身份。
  • out_trade_no 应始终按字符串保存和传输。
  • 关闭交易前应先查询交易状态,避免把已支付订单误判为待关闭订单。
  • 关闭接口返回成功后,仍需结合接口响应、异步通知和主动查询更新本地状态。
  • 如果确认交易已经创建、各项配置完全一致,但多次查询仍返回 ACQ.TRADE_NOT_EXIST,应整理请求时间、app_id、接口响应和支付宝请求流水号等信息,通过支付宝开放平台技术支持进一步核查。

备注:内容仅供参考。