PAYATHON 2026

alipay.trade.query 能否查询已创建但未支付的 H5 订单?

支付老李

结论

alipay.trade.query 不只用于查询支付成功的订单。只要支付宝侧已经建立交易,就可能查到 WAIT_BUYER_PAY、TRADE_SUCCESS、TRADE_CLOSED、TRADE_FINISHED 等状态。

如果未支付订单返回 ACQ.TRADE_NOT_EXIST,通常是因为商户系统虽然生成了 H5 支付链接或表单,但请求还没有真正进入支付宝收银台,支付宝侧也就没有创建可查询的交易。

这种情况下,没有其他接口能够查询这条只存在于商户系统中的订单。商户需要自行保存和管理订单状态。

为什么生成支付链接后仍然“交易不存在”

alipay.trade.wap.pay 和 alipay.trade.page.pay 都是页面支付接口。通过 SDK 的 pageExec 生成 URL、HTML 表单或跳转参数,不等于支付宝已经创建了交易。

一般会经过以下流程:

  1. 商户系统生成 out_trade_no,并在自己的数据库中创建订单。
  2. SDK 生成支付宝收银台地址或表单。
  3. 浏览器跳转到支付宝,支付宝接收并校验支付请求。
  4. 支付宝侧建立交易。
  5. 用户完成付款,或者让交易停留在待支付状态。

如果流程停在第 2 步,比如用户没有打开支付页面、跳转失败、浏览器拦截,或者生成链接后没有提交,支付宝就收不到创建交易的请求。此时用 out_trade_no 调用 alipay.trade.query,会返回:

code: 40004
sub_code: ACQ.TRADE_NOT_EXIST
sub_msg: 交易不存在

这是正常的查询结果,但不能直接理解为“订单未支付”。它的准确含义是:在本次查询所使用的支付宝环境和商户身份下,没有找到对应的支付宝交易。

如何判断订单状态

商户系统应同时维护“商户订单状态”和“支付宝交易状态”,不要把查询接口当作唯一的状态来源。

商户侧可以设置以下状态:

CREATED          商户订单已创建,尚未确认支付宝侧是否建单
WAIT_BUYER_PAY   支付宝交易已创建,等待付款
PAID             支付成功
CLOSED           交易关闭
EXPIRED          商户侧认定支付超时

处理查询结果时,可以按下面的逻辑执行:

  • TRADE_SUCCESS:支付成功,将商户订单更新为 PAID。
  • TRADE_FINISHED:交易已完成,也应按已支付处理,但通常不能退款。
  • WAIT_BUYER_PAY:支付宝侧已经存在这笔交易,目前正等待用户付款。
  • TRADE_CLOSED:交易已经关闭。
  • ACQ.TRADE_NOT_EXIST:支付宝侧暂时查不到交易。保留商户侧的 CREATED 状态,不要标记为支付失败。
  • 网络异常、请求超时或系统错误:稍后重试,不要因此修改订单的最终支付状态。

Node.js 示例

创建 H5 支付时,应先把商户订单写入数据库,再生成跳转地址:

const outTradeNo = createUniqueOrderNo();

await orderRepository.create({
  outTradeNo,
  amount: '0.01',
  status: 'CREATED',
  createdAt: new Date()
});

const payUrl = await alipaySdk.pageExec('alipay.trade.wap.pay', {
  method: 'GET',
  bizContent: {
    out_trade_no: outTradeNo,
    total_amount: '0.01',
    subject: '测试订单',
    product_code: 'QUICK_WAP_WAY'
  },
  returnUrl: 'https://example.com/payment/return',
  notifyUrl: 'https://example.com/payment/notify'
});

查询时只传有值的交易编号。没有支付宝交易号时,不建议同时传入空字符串形式的 trade_no:

const result = await alipaySdk.exec('alipay.trade.query', {
  bizContent: {
    out_trade_no: outTradeNo
  }
});

const code = result.code;
const subCode = result.subCode ?? result.sub_code;
const tradeStatus = result.tradeStatus ?? result.trade_status;

if (code === '10000') {
  switch (tradeStatus) {
    case 'WAIT_BUYER_PAY':
      await orderRepository.updateStatus(outTradeNo, 'WAIT_BUYER_PAY');
      break;

    case 'TRADE_SUCCESS':
    case 'TRADE_FINISHED':
      await orderRepository.markPaid(outTradeNo);
      break;

    case 'TRADE_CLOSED':
      await orderRepository.updateStatus(outTradeNo, 'CLOSED');
      break;
  }
} else if (subCode === 'ACQ.TRADE_NOT_EXIST') {
  // 支付宝侧尚未发现该交易。
  // 保留商户订单,不要直接标记为支付失败。
  await scheduleQueryRetry(outTradeNo);
} else {
  throw new Error(`支付宝查询失败:${code} / ${subCode || 'UNKNOWN'}`);
}

SDK 返回的响应字段可能是 snake_case,也可能是 camelCase,具体应以当前 SDK 的实际返回结构为准。

是否有其他接口可以查询未支付订单

这要分两种情况。

支付宝侧已经建立交易

如果用户已经进入支付宝收银台,交易可能处于 WAIT_BUYER_PAY 状态。这类订单仍然使用 alipay.trade.query 查询,不需要更换接口。

只有商户订单或支付链接

如果当前只是:

  • 在商户数据库中创建了订单;
  • 调用 pageExec 生成了支付 URL 或表单;
  • 用户还没有访问或提交支付请求;

那么支付宝侧就没有对应的交易,自然也没有支付宝接口可以返回它的状态。此时应查询商户自己的订单表,并把状态显示为“待发起支付”或“尚未付款”。

alipay.trade.precreate 等接口会显式创建支付宝交易,但它们对应的是不同支付产品和业务场景,不能仅为了查询 H5 未支付订单就直接替换。能否使用这些接口,取决于应用签约的支付产品和实际业务流程。

排查 ACQ.TRADE_NOT_EXIST

如果确认用户已经进入收银台,但长时间仍然查不到交易,可以检查以下事项:

  1. 创建支付和查询交易是否使用同一个支付宝环境,比如是否都在正式环境或都在沙箱环境。
  2. 两次请求是否使用相同的 app_id、商户身份和收款账号。
  3. out_trade_no 是否完全一致,有没有空格、截断或重复生成的问题。
  4. 支付页面是否真正完成了跳转和提交,而不是只在服务端生成了 URL。
  5. 是否在跳转后立刻发起查询。刚建单时可能存在短暂延迟,可以延迟后重试有限次数。
  6. 是否误传了 trade_no: ''。没有支付宝交易号时,只传 out_trade_no。
  7. H5 页面支付应按照 SDK 的提示使用 pageExec,不要继续使用已经弃用的 FormData 调用方式。

实际建议

支付结果应主要以支付宝异步通知为准,并使用 alipay.trade.query 做主动补偿。收到异步通知后,还要完成验签,并核对以下字段:

  • app_id
  • seller_id
  • out_trade_no
  • total_amount
  • trade_status

如果某个商户订单一直返回 ACQ.TRADE_NOT_EXIST,可以在设定的支付有效期结束后,将商户状态更新为 EXPIRED。这是商户系统自己的超时判断,不是支付宝返回的交易状态。

日志中也不要完整记录包含 sign、交易号和用户标识的网关 URL。问题中展示的签名无法用于反推出私钥,但完整请求仍可能被重放,也可能泄露业务信息,因此生产日志需要脱敏。

备注:内容仅供参考。