alipay.trade.close 提示交易不存在,如何关闭订单?
结论
alipay.trade.wap.pay 返回支付表单或支付链接时,支付宝侧不一定已经创建交易。用户实际打开支付宝收银台并提交支付请求后,支付宝才可能生成对应交易。
如果用户尚未进入收银台就调用 alipay.trade.close,支付宝无法查询到 out_trade_no 对应的交易,返回“交易不存在”属于正常情况。此时关闭商户系统中的订单即可,无须反复调用支付宝关单接口。
交易已经支付成功时,应调用退款接口,而不是关单接口。alipay.trade.close 主要用于关闭待支付交易。
为什么会出现“交易不存在”
手机网站支付通常分为两个阶段:
- 商户系统创建本地订单,生成
alipay.trade.wap.pay请求表单或跳转地址。 - 用户提交表单并进入支付宝收银台,支付宝受理请求并创建交易。
如果流程只完成了第一步,支付宝侧可能还没有这笔交易。此时调用:
alipay.trade.close
即使传入的 out_trade_no 与本地订单号完全一致,也可能收到“交易不存在”的响应。
“商户订单已创建”和“支付宝交易已创建”是两个不同的状态。商户系统需要维护自己的订单生命周期,不能根据支付宝交易是否存在来决定能否关闭本地订单。
正确的处理流程
1. 先关闭商户本地订单
订单超过支付期限或用户主动取消时,应先将本地订单更新为“已关闭”或“已取消”,同时禁止该订单继续触发业务交付。
更新订单时需要附带状态条件,避免支付通知与关单任务并发执行,错误覆盖订单状态。例如,只允许把“待支付”订单改为“已关闭”:
UPDATE payment_order
SET status = 'CLOSED',
closed_at = CURRENT_TIMESTAMP
WHERE out_trade_no = ?
AND status = 'WAIT_PAY';
随后根据受影响行数判断更新是否成功。如果受影响行数为 0,需要重新读取订单,确认其是否已经支付、关闭或退款。
2. 查询支付宝交易状态
如果用户可能进入过支付宝收银台,可以先调用:
alipay.trade.query
再根据查询结果分别处理:
- 返回交易不存在:支付宝侧没有可关闭的交易,只需关闭本地订单。
WAIT_BUYER_PAY:调用alipay.trade.close。TRADE_CLOSED:无须再次关单。TRADE_SUCCESS或TRADE_FINISHED:不能再按未支付订单处理。如需退回资金,应根据业务规则调用退款接口。- 查询失败或网络超时:不能直接认定交易不存在,应记录任务并重试,或交由人工核对。
3. 只关闭待支付交易
以下是使用 Java 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()) {
// 记录 code、sub_code、msg 和 sub_msg,后续重试或人工处理
throw new IllegalStateException(
"支付宝关单失败:" + closeResponse.getSubCode()
+ " - " + closeResponse.getSubMsg()
);
}
} else if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 交易已经付款,不应调用关单接口。
// 如业务需要退回资金,应进入退款流程。
} else if ("TRADE_CLOSED".equals(tradeStatus)) {
// 已关闭,无须再次处理
}
} else if (isTradeNotExist(queryResponse)) {
// 支付宝侧尚未创建交易,本地保持关闭状态即可
} else {
// 系统异常、签名问题、权限问题或网络问题不能按“交易不存在”处理
// 记录完整错误信息并安排重试
}
判断是否属于“交易不存在”时,应检查响应中的 code、sub_code 等结构化字段,不要只匹配中文错误文案:
private boolean isTradeNotExist(AlipayTradeQueryResponse response) {
String subCode = response.getSubCode();
// 具体错误码应以实际接口响应和当前支付宝接口文档为准。
return "ACQ.TRADE_NOT_EXIST".equals(subCode);
}
设置支付有效期
调用 alipay.trade.wap.pay 时,可以根据接口支持情况设置 timeout_express,限制用户完成支付的时间。例如:
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);
request.setBizContent("""
{
"out_trade_no": "ORDER_202609090001",
"total_amount": "99.00",
"subject": "订单支付",
"product_code": "QUICK_WAP_WAY",
"timeout_express": "15m"
}
""");
timeout_express 限制的是支付有效时间。商户仍需通过本地定时任务管理订单超时,不能认为有效期结束后,支付宝状态会立即同步到商户系统。
必须处理支付与关单并发
关单任务执行期间,用户可能正好完成支付。即使本地订单已经标记为关闭,也不能忽略支付宝异步通知。
收到支付通知后,需要:
- 验证签名。
- 校验
app_id、商户身份、out_trade_no、金额和币种等字段。 - 使用原子更新或状态机处理订单。
- 如果本地订单已关闭,但支付宝确认付款成功,应进入异常支付处理流程,例如自动退款或人工审核。
- 按支付宝接口要求正确响应通知,避免重复通知引发额外处理。
注意事项
- 生成支付表单不代表支付宝交易已经存在。
- “交易不存在”也不意味着应无限重试接口。如果用户从未进入收银台,重复关单没有意义。
out_trade_no必须与发起支付时完全一致,且交易应属于同一应用和商户主体。- 不要根据同步跳转结果判断最终支付状态。应以验签后的异步通知为主,必要时主动查询。
- 支付成功的交易通常应调用
alipay.trade.refund,而不是alipay.trade.close。 - 查询超时、系统错误和验签错误需要分别处理,不能一律视为“交易不存在”。
- 具体错误码、可关闭状态和参数能力可能随接口规范变化,应以当前接入环境的实际响应和支付宝开放平台文档为准。
备注:内容仅供参考。
