PAYATHON 2026

支付宝退款接口多次返回 aop.ACQ.SYSTEM_ERROR

支付小周

结论

code=20000、sub_code=aop.ACQ.SYSTEM_ERROR 表示支付宝在处理退款请求时发生了系统级异常,但仅凭这两个字段无法判断具体的业务原因。

这个问题在不同时间重试仍然失败,同一商品重新支付后也无法退款,而其他订单可以正常处理。因此,更可能与该商品对应的退款请求数据、订单属性、资金状态或风控校验有关,而不是普通的短时网络波动。

不要持续无条件重试。先查询退款结果,排除退款已经受理、但响应异常的情况,再逐项对比正常订单和异常订单的请求参数。如果仍然无法定位,应携带 trace_id、订单号和请求时间,提交支付宝技术支持排查内部链路。

如何理解返回结果

响应中的主要字段如下:

code: 20000
msg: Service Currently Unavailable
sub_code: aop.ACQ.SYSTEM_ERROR
sub_msg: 系统异常
refund_fee: 0.00
send_back_fee: 0.00

这类响应没有给出明确的退款成功结果。不过,refund_fee=0.00 也不能单独证明退款已经失败。调用方收到异常响应时,服务端是否已经开始处理,仍需通过查询接口确认。

日志中的两条 trace_id 需要保留:

0600828f174117902694672348797
060d1ea6174117877065416292646

支付宝技术支持可以根据这些信息定位对应的服务端调用链。

可能原因

1. 退款请求中的商品数据异常

如果退款请求传入了 goods_detail,而异常总是出现在某个商品上,应先检查该商品生成的字段:

  • goods_id
  • alipay_goods_id
  • goods_name
  • quantity
  • price
  • goods_category
  • categories_tree
  • show_url

检查时要特别留意空字符串、内容过长、特殊控制字符、金额精度、数量与单价不匹配,以及序列化后的字段类型错误。

普通退款通常不要求传入 goods_detail。如果业务允许,可以创建新的测试订单,不传 goods_detail,进行一次小额验证。不要直接用真实的失败订单反复试验,否则可能产生重复退款。

2. 订单资金或交易状态特殊

异常订单可能带有普通订单没有的属性,例如:

  • 使用了红包、优惠券、商家券或平台补贴;
  • 存在分账、结算、资金冻结或特殊资金渠道;
  • 已经发生过部分退款;
  • 订单处于关闭、争议或其他处理中状态;
  • 退款金额超过当前可退金额;
  • 支付产品、收款账号或资金路由与正常订单不同。

即使商品相同,重新支付生成的新订单也可能再次命中相同的优惠、资金路由或业务规则。因此,重新下单后仍然失败,并不能单独证明问题出在商品字段上。

3. out_request_no 使用不当

每笔退款请求都应使用稳定且唯一的 out_request_no。同一次退款发生重试时,应继续使用原来的退款请求号。如果每次重试都生成新号码,支付宝可能会将这些请求识别为多笔退款。

还需要检查以下情况:

  • 不同订单误用了相同的 out_request_no;
  • 同一个 out_request_no 对应的退款金额发生变化;
  • 本地请求超时后,又生成新的退款单再次调用;
  • 多个任务同时处理同一笔退款。

4. 支付宝内部异常或风控拦截

aop.ACQ.SYSTEM_ERROR 是通用系统异常。调用方无法根据这条响应确认支付宝内部具体在哪个环节失败。

如果请求参数、交易状态和幂等控制都没有问题,而且故障可以在特定订单或商品上稳定复现,就需要由支付宝根据 trace_id 查询内部日志。

建议的排查步骤

第一步:停止无条件重复退款

为同一笔业务退款建立唯一记录,并始终使用同一个 out_request_no。收到超时、20000 或其他结果不确定的响应后,先查询退款结果,再判断是否需要重试。

第二步:查询退款结果

调用 alipay.trade.fastpay.refund.query,传入原支付订单号和退款时使用的 out_request_no。

{
  "out_trade_no": "20250305194600",
  "out_request_no": "REFUND_20250305194600_001"
}

如果查询确认退款成功,应更新本地状态,不能再次发起退款。只有明确查询不到这笔退款请求时,才能进入重试或人工处理流程。

第三步:查询原交易状态

调用 alipay.trade.query,检查以下内容:

  • 交易是否存在;
  • 当前交易状态;
  • 实收金额;
  • 已退款金额;
  • 当前剩余可退金额;
  • 支付渠道及优惠信息。

退款金额应以当前可退金额为准,不能只根据本地订单金额计算。

第四步:对比完整请求参数

逐字段对比一笔正常订单和一笔异常订单的退款请求,特别检查:

app_id
seller_id
out_trade_no / trade_no
refund_amount
out_request_no
refund_reason
goods_detail
refund_royalty_parameters
query_options

对比对象应是发送给支付宝前的最终请求 JSON,而不只是业务数据库里的原始数据。

第五步:检查商户侧日志和并发控制

建议保存以下信息:

  • 接口名称;
  • app_id;
  • out_trade_no 和支付宝 trade_no;
  • out_request_no;
  • 请求时间及毫秒时间戳;
  • 完整业务参数,敏感信息需要脱敏;
  • 原始响应;
  • trace_id;
  • SDK 异常堆栈;
  • 查询接口返回的结果。

同时确认定时任务、消息消费者和人工操作没有并发发起同一笔退款。

第六步:提交支付宝技术支持

如果查询结果仍不明确,或者同类订单能够稳定复现,应提交工单并附上以下信息:

接口:alipay.trade.refund
商户 app_id:脱敏后提供,工单中可填写完整值
商户订单号:20250305194600、20250305200713
退款请求号:对应的 out_request_no
请求时间:精确到秒
trace_id:
0600828f174117902694672348797
060d1ea6174117877065416292646
返回码:20000
子错误码:aop.ACQ.SYSTEM_ERROR

工单中还应注明其他订单可以正常退款,以及问题与某类商品或订单属性存在稳定关联,帮助技术支持缩小排查范围。

Java 处理示例

下面的代码用于说明处理思路。具体类名和可用方法可能因支付宝 Java SDK 版本而异,应以项目实际使用的 SDK 为准。

String outTradeNo = "20250305194600";
String outRequestNo = "REFUND_20250305194600_001";

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setOutTradeNo(outTradeNo);
model.setOutRequestNo(outRequestNo);
model.setRefundAmount("100.00");
model.setRefundReason("用户申请退款");

request.setBizModel(model);

AlipayTradeRefundResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
    // 记录支付宝返回信息,并将本地退款单更新为成功。
    markRefundSuccess(outRequestNo, response.getRefundFee());
} else if ("20000".equals(response.getCode())
        || "aop.ACQ.SYSTEM_ERROR".equals(response.getSubCode())) {
    // 此时结果不确定,不应立即生成新的 out_request_no 再次退款。
    markRefundPending(outRequestNo);

    // 后续使用原 out_request_no 调用退款查询接口。
    queryRefundResult(outTradeNo, outRequestNo);
} else {
    // 明确的业务失败应记录 code、sub_code、sub_msg,并按具体原因处理。
    markRefundFailed(
        outRequestNo,
        response.getCode(),
        response.getSubCode(),
        response.getSubMsg()
    );
}

退款查询示例:

AlipayTradeFastpayRefundQueryRequest queryRequest =
        new AlipayTradeFastpayRefundQueryRequest();

AlipayTradeFastpayRefundQueryModel queryModel =
        new AlipayTradeFastpayRefundQueryModel();

queryModel.setOutTradeNo(outTradeNo);
queryModel.setOutRequestNo(outRequestNo);
queryRequest.setBizModel(queryModel);

AlipayTradeFastpayRefundQueryResponse queryResponse =
        alipayClient.execute(queryRequest);

if (queryResponse.isSuccess()) {
    // 应结合 SDK 实际返回字段判断退款结果和退款金额。
    markRefundSuccess(outRequestNo, queryResponse.getRefundAmount());
} else {
    // 查询失败不等于退款失败,保留待确认状态并记录原始响应。
    markRefundPending(outRequestNo);
}

重试策略

只有查询确认尚未退款,而且当前错误属于可重试类型时,才能进行有限次数的重试。可以采用指数退避,并保持原 out_request_no 不变,例如:

第 1 次:延迟 5 秒
第 2 次:延迟 30 秒
第 3 次:延迟 2 分钟
仍失败:转人工处理或提交支付宝工单

具体间隔和重试次数应根据业务时效确定,不能将这些示例数值视为支付宝的官方要求。

注意事项

  • 不要只根据 msg 或 sub_msg 判断最终退款状态。
  • 不要因为响应中的 refund_fee=0.00 就创建新的退款请求。
  • 同一次退款重试必须保持 out_request_no、退款金额和退款目标一致。
  • 确认支付宝订单号 trade_no 与商户订单号 out_trade_no 的对应关系,避免查错订单。
  • 签名成功只表示响应可以通过验证,不表示退款业务已经成功。
  • 不要公开传播日志中的签名、密钥、用户身份信息和完整支付数据。
  • 如果只有包含 goods_detail 的请求失败,应重点检查商品字段。如果退款请求没有传入商品明细,则应排查交易资金结构、优惠、分账、订单状态和支付宝内部链路。

备注:内容仅供参考。