PAYATHON 2026

支付宝未扫码查询返回ACQ.TRADE_NOT_EXIST如何处理

支付小周

结论

正式环境中同样可能出现 ACQ.TRADE_NOT_EXIST。

alipay.trade.precreate 成功返回 qr_code,只表示二维码已经生成,不代表支付宝侧已有可供 alipay.trade.query 查询的交易记录。用户还未扫码时,查询接口可能返回 ACQ.TRADE_NOT_EXIST。扫码后,交易状态通常会进入 WAIT_BUYER_PAY、TRADE_SUCCESS 等状态,此时才能查询到相应记录。

所以,不要把未扫码阶段出现的 ACQ.TRADE_NOT_EXIST 一律当作系统异常反复抛出。可以将它转换成“等待用户扫码”的业务状态,并根据轮询次数、退避间隔和订单有效期决定是否继续查询。

为什么生成二维码后仍然查不到交易

线下扫码支付通常分为两个阶段:

  1. 商户调用 alipay.trade.precreate,支付宝返回二维码内容。
  2. 用户使用支付宝扫码后,支付宝侧开始建立或推进实际交易。

在第一个阶段,商户自己的系统中已经有订单,但支付宝交易记录可能还没有进入查询接口可见的状态。这意味着:

  • alipay.trade.precreate 返回成功,不代表订单已经支付。
  • 用户尚未扫码时返回 ACQ.TRADE_NOT_EXIST,不一定是预下单失败。
  • 扫码后可能先查到 WAIT_BUYER_PAY,完成付款后再变为 TRADE_SUCCESS。
  • 支付结果主要通过支付宝异步通知确认,主动查询一般用于页面展示和补偿。

沙箱与正式环境的处理时序可能不同,不能认为每次预下单后的查询结果都会完全一致。业务代码需要同时兼容“查不到交易”和“等待付款”这两种未支付状态。

推荐处理方案

1. 先保存预下单结果

调用 alipay.trade.precreate 成功后,在本地保存:

  • 商户订单号 out_trade_no
  • 二维码内容 qr_code
  • 本地订单状态,例如 WAIT_SCAN
  • 创建时间和失效时间
  • 支付金额

查询接口暂时返回 ACQ.TRADE_NOT_EXIST 时,不要立即把本地订单标记为失败。

2. 将 ACQ.TRADE_NOT_EXIST 转换为业务状态

可以按下表对查询结果进行分类:

查询结果建议处理
TRADE_SUCCESS标记支付成功,同时做好异步通知的幂等处理
TRADE_FINISHED标记交易完成
WAIT_BUYER_PAY保持等待支付状态
TRADE_CLOSED标记订单关闭
ACQ.TRADE_NOT_EXIST订单仍在有效期内时,视为等待扫码
网络超时、网关异常记录为查询状态未知,稍后重试
参数错误、签名错误、权限错误立即告警并检查配置,不要持续重试

只有满足以下条件,才适合把 ACQ.TRADE_NOT_EXIST 当作“等待扫码”处理:

  • alipay.trade.precreate 已明确成功;
  • 查询使用的 out_trade_no 与预下单时完全一致;
  • 应用、支付宝环境和商户配置没有切换;
  • 本地订单仍处于二维码有效期内。

如果有任一条件不成立,应先排查参数或环境配置。

3. 使用有限轮询和退避策略

前端或服务端都可以轮询订单状态,但不要无限、高频地调用支付宝接口。例如:

  • 开始时每 2~3 秒查询一次;
  • 多次查询仍未扫码后,逐步延长到 5~10 秒;
  • 达到业务设定的二维码有效期后停止查询;
  • 页面关闭或用户取消支付后停止轮询;
  • 订单超时后,按业务规则关闭订单或重新生成二维码。

具体的轮询频率和有效期,应根据所接入产品的官方文档、接口限流要求和自身业务配置确定。

4. 以异步通知为主,主动查询为辅

支付成功后,应通过支付宝异步通知更新订单状态,并完成以下校验:

  • 验证签名;
  • 校验 app_id;
  • 校验 out_trade_no;
  • 校验支付金额和币种;
  • 校验收款方信息;
  • 保证订单状态更新幂等。

主动查询主要用于:

  • 在收银台页面显示支付进度;
  • 异步通知延迟时进行补偿;
  • 定时修复长时间停留在处理中状态的订单。

Java 处理示例

下面的示例主要演示响应分类逻辑。具体的 SDK 类名和异常类型,应以项目实际使用的支付宝 Java SDK 版本为准。

public enum PaymentQueryStatus {
    WAIT_SCAN,
    WAIT_PAY,
    SUCCESS,
    CLOSED,
    UNKNOWN,
    ERROR
}
public PaymentQueryStatus queryPayment(
        AlipayClient alipayClient,
        String outTradeNo) throws AlipayApiException {

    AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
    request.setBizContent(
        "{\"out_trade_no\":\"" + outTradeNo + "\"}"
    );

    AlipayTradeQueryResponse response = alipayClient.execute(request);

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

        if ("TRADE_SUCCESS".equals(tradeStatus)
                || "TRADE_FINISHED".equals(tradeStatus)) {
            return PaymentQueryStatus.SUCCESS;
        }

        if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
            return PaymentQueryStatus.WAIT_PAY;
        }

        if ("TRADE_CLOSED".equals(tradeStatus)) {
            return PaymentQueryStatus.CLOSED;
        }

        return PaymentQueryStatus.UNKNOWN;
    }

    String subCode = response.getSubCode();

    if ("ACQ.TRADE_NOT_EXIST".equals(subCode)) {
        // 前提:预下单已经成功,订单号和运行环境均确认无误。
        // 此时不抛异常,交给上层按照订单有效期决定是否继续轮询。
        return PaymentQueryStatus.WAIT_SCAN;
    }

    // 其他业务错误不能统一按“未扫码”处理。
    throw new IllegalStateException(
        "Alipay trade query failed: code=" + response.getCode()
            + ", msg=" + response.getMsg()
            + ", subCode=" + response.getSubCode()
            + ", subMsg=" + response.getSubMsg()
    );
}

业务层还需要结合本地订单状态和有效期进行判断:

public PaymentQueryStatus getOrderStatus(LocalOrder order)
        throws AlipayApiException {

    if (order.isPaid()) {
        return PaymentQueryStatus.SUCCESS;
    }

    if (order.isClosed() || order.isExpired()) {
        return PaymentQueryStatus.CLOSED;
    }

    PaymentQueryStatus status =
        queryPayment(alipayClient, order.getOutTradeNo());

    if (status == PaymentQueryStatus.SUCCESS) {
        // 更新时必须使用条件更新或事务锁,避免查询与异步通知重复入账。
        markOrderPaidIdempotently(order.getOutTradeNo());
    }

    return status;
}

避免日志中反复出现异常

如果当前代码对所有 response.isSuccess() == false 的情况都直接抛出异常,可以单独处理 ACQ.TRADE_NOT_EXIST,并调整日志级别:

if ("ACQ.TRADE_NOT_EXIST".equals(response.getSubCode())) {
    log.debug(
        "Alipay trade is not visible yet, outTradeNo={}",
        outTradeNo
    );
    return PaymentQueryStatus.WAIT_SCAN;
}

等待扫码属于正常业务状态,可以记录为 DEBUG,也可以只记录指标,不必每次都打印日志。其他错误仍应保留 WARN 或 ERROR,以免掩盖真正的签名、权限或参数问题。

注意事项

不要对所有 ACQ.TRADE_NOT_EXIST 都无限重试。如果订单长时间查询不到,需要检查:

  • 查询时使用的是否为预下单时的同一个 out_trade_no;
  • 是否误用了尚未返回的 trade_no;
  • 预下单和查询是否使用同一个 app_id;
  • 是否混用了沙箱网关和正式网关;
  • 商户私钥、支付宝公钥及应用配置是否与当前环境对应;
  • 预下单是否真正成功,而不只是 HTTP 请求成功;
  • 二维码是否已经超过业务设定的有效期。

本地订单可以划分为 WAIT_SCAN、WAIT_PAY、PAID、CLOSED 和 QUERY_UNKNOWN。这样处理后,ACQ.TRADE_NOT_EXIST 只是支付流程中一个可预期的状态,不会再被当作需要反复抛出的程序异常。

备注:内容仅供参考。