支付宝退款提示 ACQ.TRADE_HAS_CLOSE,但退款未成功
结论
ACQ.TRADE_HAS_CLOSE 表示支付宝侧的交易已关闭,不代表本次退款成功。
返回结果如下:
{
"code": "40004",
"msg": "Business Failed",
"sub_code": "ACQ.TRADE_HAS_CLOSE",
"sub_msg": "交易已经关闭",
"refund_fee": "0.00",
"send_back_fee": "0.00"
}
这次退款请求失败,实际退款金额为 0.00。需要先查询原交易的状态和资金情况,确认交易是未支付超时、被主动关闭、已经全额退款,还是退款时使用了错误的交易号或环境。
常见原因
原交易未支付便已关闭
订单可能因支付超时或商户调用关单接口而进入 TRADE_CLOSED 状态。支付宝此时没有收到可退资金,无法再对这笔交易发起退款。
例如:
- 商户创建订单;
- 用户没有完成付款;
- 订单超时或被主动关闭;
- 商户系统误将订单标记为已支付;
- 后续调用退款接口时收到
ACQ.TRADE_HAS_CLOSE。
遇到这种情况,应修正商户侧的订单状态,不要继续重试退款。
交易已经全额退款
支付宝交易完成全额退款后,也可能进入关闭状态。再次提交退款时,支付宝会拒绝请求。
当前响应中的 refund_fee: "0.00" 只表示这次请求没有退回资金,不能用它判断此前是否发生过退款。交易是否已经退款,应以交易查询、退款查询和账务流水为准。
使用了错误的交易标识
退款请求中的 trade_no 或 out_trade_no 可能指向了另一笔已经关闭的交易。常见情况有:
- 重复使用订单号;
- 混用测试环境和生产环境;
- 数据库保存了错误的支付宝交易号;
- 接入多个应用时使用了不匹配的
app_id; - 查询时使用商户订单号,退款时却传入另一笔交易的支付宝交易号。
如果同时传递 trade_no 和 out_trade_no,两者必须指向同一笔交易。一般只需选择其中一个准确的标识,以免产生歧义。
商户状态与支付宝状态不同步
商户可能仅根据前端跳转结果更新订单,没有正确处理异步通知或发起主动查询。这样会导致本地显示 支付成功,但支付宝侧的交易实际上并未成功。
不能只根据同步返回页判断支付结果。应结合支付宝异步通知,并调用 alipay.trade.query 主动核验。
排查步骤
1. 查询原交易
调用 alipay.trade.query 查询支付宝侧的真实交易状态。建议优先使用支付成功时保存的 trade_no:
{
"trade_no": "支付宝交易号"
}
也可以使用商户订单号:
{
"out_trade_no": "商户订单号"
}
重点检查以下字段:
trade_statustotal_amountreceipt_amountbuyer_pay_amountrefund_amounttrade_noout_trade_no
不同产品或接口版本返回的字段可能不同,应以实际响应为准。
各状态通常按以下方式处理:
WAIT_BUYER_PAY:尚未付款,不能退款;TRADE_SUCCESS:已经支付,可在产品允许的退款期限和金额范围内申请退款;TRADE_FINISHED:交易已经完成,能否退款取决于对应支付宝产品的规则;TRADE_CLOSED:交易已经关闭,不能再发起新的退款。
2. 查询退款记录
如果此前提交过退款,应使用原退款请求号调用 alipay.trade.fastpay.refund.query:
{
"trade_no": "支付宝交易号",
"out_request_no": "原退款请求号"
}
out_request_no 用于标识一次退款请求。同一笔交易需要进行多次部分退款时,每次都应使用不同的退款请求号。重试同一次退款时,必须继续使用原退款请求号。
网络超时或响应不明确时,不要直接生成新的 out_request_no,否则可能重复申请退款。
3. 核对账务结果
需要分别核对接口状态、商户数据库和实际资金流水:
- 商户订单是否确实收到款项;
- 支付宝账单中是否有这笔交易;
- 是否已经产生退款流水;
- 用户的付款渠道是否收到退款;
- 商户可用余额或结算记录是否发生变化。
如果交易查询显示已经退款,但用户表示尚未到账,还要考虑银行卡、花呗等支付渠道的入账时间。此时不应重新调用退款接口,而应根据退款流水继续核查渠道处理进度。
4. 修正本地订单状态
如果支付宝查询结果确认交易因未支付而关闭,应将商户订单更新为已关闭或支付失败,不要把它记为退款失败后继续重试。
如果查询确认此前已经全额退款,应补齐商户侧的退款记录,避免重复退款。
Java 调用示例
以下代码只展示排查思路。SDK 初始化、密钥和网关配置应沿用项目的现有配置:
AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
queryRequest.setBizContent("""
{
"out_trade_no": "ORDER_202609090001"
}
""");
AlipayTradeQueryResponse queryResponse = alipayClient.execute(queryRequest);
if (queryResponse.isSuccess()) {
String tradeStatus = queryResponse.getTradeStatus();
if ("TRADE_SUCCESS".equals(tradeStatus)) {
// 核对可退金额后再调用退款接口
} else if ("TRADE_CLOSED".equals(tradeStatus)) {
// 查询退款记录和账务流水,不要直接重复退款
}
} else {
// 记录 code、sub_code、sub_msg 和请求标识,继续排查
}
发起退款时,应保存唯一且稳定的退款请求号:
AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
refundRequest.setBizContent("""
{
"trade_no": "202609092200xxxxxxxxxxxx",
"refund_amount": "10.00",
"out_request_no": "REFUND_ORDER_202609090001"
}
""");
AlipayTradeRefundResponse refundResponse = alipayClient.execute(refundRequest);
if (refundResponse.isSuccess()) {
// 退款接口受理成功,保存返回结果
} else {
// 不要仅凭异常文案修改为退款成功
// 对结果不明确的请求,应使用原 out_request_no 查询退款状态
}
注意事项
code: "40004"表示业务处理失败,不能按退款成功处理。ACQ.TRADE_HAS_CLOSE不等于退款已经完成,它只说明目标交易当前处于关闭状态。- 不要反复重试已关闭交易的退款请求,重试无法改变交易状态。
- 支付成功和退款成功等状态,应以服务端通知及主动查询结果为准。
- 保存
trade_no、out_trade_no、out_request_no、请求时间和完整响应,方便后续对账。 - 如果支付宝交易查询、退款查询与账单流水仍有矛盾,应整理相关交易号、商户订单号、退款请求号和请求时间,通过支付宝商户支持渠道核查。不要公开应用私钥、签名原文或用户敏感信息。
备注:内容仅供参考。
