PAYATHON 2026

支付宝退款提示 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 状态。支付宝此时没有收到可退资金,无法再对这笔交易发起退款。

例如:

  1. 商户创建订单;
  2. 用户没有完成付款;
  3. 订单超时或被主动关闭;
  4. 商户系统误将订单标记为已支付;
  5. 后续调用退款接口时收到 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_status
  • total_amount
  • receipt_amount
  • buyer_pay_amount
  • refund_amount
  • trade_no
  • out_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、请求时间和完整响应,方便后续对账。
  • 如果支付宝交易查询、退款查询与账单流水仍有矛盾,应整理相关交易号、商户订单号、退款请求号和请求时间,通过支付宝商户支持渠道核查。不要公开应用私钥、签名原文或用户敏感信息。

备注:内容仅供参考。