支付宝关闭订单接口提示 ACQ.TRADE_NOT_EXIST 怎么处理?
明确结论
alipay.trade.close 只能关闭支付宝侧已经创建且仍未支付的交易。
这两个错误分别表示:
ACQ.TRADE_NOT_EXIST:支付宝侧没有找到对应交易。常见情况是支付请求只生成了收银台页面或支付链接,但用户尚未进入收银台,因此支付宝交易还没有真正创建。订单号、应用或环境不一致也会导致这个错误。ACQ.REASON_ILLEGAL_STATUS:交易已经支付成功,当前状态不允许关闭。对于已支付订单,应调用退款接口,不能再调用关闭接口。
正确的处理方式是先查询支付宝交易状态。交易存在且未支付时调用关闭接口;交易已经支付时,根据业务需要发起退款;交易不存在时,通常关闭本地订单即可,必要时可以稍后再次查询确认。
为什么支付接口成功后仍提示交易不存在
“支付接口调用成功”不等于”支付宝交易已经创建”。
以 alipay.trade.page.pay、alipay.trade.wap.pay 等页面支付产品为例,接口返回成功通常只表示系统已经生成跳转表单或支付地址。支付宝侧的实际交易可能要等用户打开收银台后才会创建。
如果用户一直没有跳转到支付宝收银台,此时直接调用:
alipay.trade.close
就可能返回:
ACQ.TRADE_NOT_EXIST
还需要排查以下情况:
out_trade_no与发起支付时使用的值不一致;- 查询或关闭订单时使用了不同的
app_id; - 一个请求发往沙箱环境,另一个请求发往正式环境;
- 使用
trade_no时,传入的值不是支付宝交易号; - 支付请求刚刚发出,交易数据暂时无法查询;
- 支付请求并未被支付宝成功受理,只是本地程序完成了表单生成。
本地数据库中有这笔订单,不代表支付宝侧也已经创建了对应交易。
为什么支付成功后不能关闭
支付成功后,交易通常已经进入 TRADE_SUCCESS 状态。关闭接口只能终止未支付交易,无法撤销已经完成的付款,因此会返回:
ACQ.REASON_ILLEGAL_STATUS
如果需要把已支付的款项退还给用户,应调用:
alipay.trade.refund
不能用关闭接口代替退款。
推荐的处理流程
1. 先关闭本地支付入口
订单超时或业务决定取消订单时,先将本地订单状态改为”关闭中”或”已关闭”,同时禁止系统再次生成支付参数。更新状态时应使用条件更新,避免支付回调并发执行并覆盖已支付状态。
例如:
UPDATE orders
SET status = 'CLOSING'
WHERE order_no = ?
AND status = 'WAIT_PAY';
只有受影响行数为 1 时,才继续执行后续关闭流程。
2. 查询支付宝交易
调用:
alipay.trade.query
建议使用发起支付时的同一个 out_trade_no,并确认 app_id、网关环境和商户身份一致。
根据查询结果分别处理:
| 查询结果 | 处理方式 |
|---|---|
WAIT_BUYER_PAY | 调用 alipay.trade.close |
TRADE_SUCCESS 或 TRADE_FINISHED | 不再关闭;如需退钱,调用退款接口 |
TRADE_CLOSED | 本地订单可直接标记为已关闭 |
ACQ.TRADE_NOT_EXIST | 支付宝侧没有可关闭的交易,关闭本地订单即可;如果时序尚不确定,可以延迟复查 |
| 网络异常或未知结果 | 不要直接认定关闭成功,应重新查询 |
不同支付宝产品的交易状态和可执行操作可能存在差异,应以当前产品的接口文档和实际查询结果为准。
3. 关闭仍处于待支付状态的交易
下面是支付宝 Java SDK 的典型调用方式。SDK 类名和响应字段应以项目实际使用的 SDK 版本为准。
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()) {
// 将本地订单更新为已关闭
} else {
// 记录 code、sub_code、sub_msg,随后重新查询最终状态
}
} else if ("TRADE_CLOSED".equals(tradeStatus)) {
// 支付宝交易已经关闭,同步更新本地状态
} else if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 已支付,不能调用关闭接口
// 如业务确实需要退回款项,应进入退款流程
}
} else if ("ACQ.TRADE_NOT_EXIST".equals(queryResponse.getSubCode())) {
// 支付宝侧尚无此交易。
// 可关闭本地订单;如果可能存在请求时序问题,应安排延迟复查。
} else {
// 网络、签名、权限或其他异常,保留原始响应并进行重试或人工处理
}
实际项目中不要直接拼接 JSON。建议使用 JSON 序列化组件,避免订单号中的特殊字符导致格式错误。
如何处理”交易不存在”与支付回调并发
不能直接把 ACQ.TRADE_NOT_EXIST 当作永久结论,因为可能出现以下时序:
- 系统发出支付请求;
- 关闭任务开始查询,此时暂时得到交易不存在;
- 用户随后打开此前生成的支付页面;
- 支付宝侧创建交易,用户完成付款。
关闭本地订单后,还应采取以下措施:
- 用户支付前,再次检查本地订单是否仍允许付款;
- 不再为已关闭订单返回新的支付链接或支付表单;
- 对
ACQ.TRADE_NOT_EXIST安排一次或多次延迟查询; - 收到支付异步通知时,通过条件更新判断订单能否转为已支付;
- 如果本地订单已经关闭,却收到了真实的支付成功通知,不要忽略,应转入异常支付或自动退款流程;
- 关闭接口超时或返回未知异常时,应通过查询确认最终状态,不要反复修改本地状态。
注意事项
关闭订单、支付回调和主动查询可能同时发生,因此订单状态必须以幂等方式更新。建议保存以下信息,便于排查问题:
- 商户订单号
out_trade_no; - 支付宝交易号
trade_no; app_id和使用的网关环境;- 接口返回的
code、sub_code、sub_msg; - 请求时间、查询时间和回调时间;
- 本地订单状态变更记录。
判断原则很直接:支付宝侧没有交易时,不需要调用关闭接口;存在未支付交易时,调用 alipay.trade.close;交易已经支付时,只能保留交易或按业务规则退款。
备注:内容仅供参考。
