PAYATHON 2026

支付宝付款正常但退款出现 aop.ACQ.SYSTEM_ERROR

支付小周

结论

aop.ACQ.SYSTEM_ERROR 通常表示支付宝退款链路出现了临时系统异常,或者退款服务暂时不可用。付款正常不代表退款也一定正常,因为支付和退款使用不同的接口,还会经过不同的交易状态、资金渠道、权限和风控校验。

如果错误只是偶发出现,应先按”结果未知”处理:查询退款结果,再使用原退款请求号重试。不要立即生成新的退款请求号反复提交,以免造成重复退款。

如果连续数天无法退款,而且所有订单都有相同问题,就需要检查商户权限、交易状态、资金渠道、请求参数和证书配置。同时保留支付宝返回的 code、sub_code、sub_msg 及请求时间,联系支付宝技术支持排查。

常见原因

1. 支付宝退款服务临时异常

错误信息中的:

Service Currently Unavailable
aop.ACQ.SYSTEM_ERROR

通常表示请求已经到达支付宝网关,但退款服务或相关依赖没有正常完成处理。问题可能来自短时抖动,也可能只影响部分商户、部分交易或特定资金渠道。

接口报错不一定意味着退款失败。退款可能已经受理,只是在返回响应时发生了异常。因此,不要直接把订单恢复为”未退款”状态,也不要用新的请求号再次发起退款。

2. 网关封装错误时丢失了详细信息

业务系统中的 ERROR_GATEWAY 很可能是支付组件或网关层自行定义的错误,并非支付宝接口返回的原始错误码。

应记录支付宝返回的完整内容,至少包括:

code
msg
sub_code
sub_msg
trade_no
out_trade_no
refund_fee
gmt_refund_pay

如果日志里只有一行 aop.ACQ.SYSTEM_ERROR,现有信息不足以判断具体原因。需要检查支付 SDK 或网关是否覆盖、截断了原始响应。

3. 交易状态不允许退款

以下情况也可能导致退款请求失败:

  • 原交易不存在,或者订单号填写错误;
  • 交易尚未成功;
  • 交易已经关闭;
  • 已退款金额与本次退款金额之和超过实付金额;
  • 退款期限、交易产品或资金渠道存在限制;
  • 同一退款请求号此前已经使用,但本次提交的参数与原请求不一致。

这类问题一般会返回更具体的 sub_code。如果中间网关没有完整处理响应,业务系统也可能只看到系统错误。

4. 商户配置或权限发生变化

支付成功但退款失败,也可能是退款侧配置出现了问题,例如:

  • 应用与商户账号的绑定关系发生变化;
  • 应用退款权限、产品签约状态或商户状态异常;
  • 使用了错误的 app_id、私钥、证书或支付宝公钥;
  • 支付请求和退款请求使用了不同的配置或运行环境;
  • 更新证书后,部分服务器仍在使用旧配置;
  • 商户资金状态异常,或退款资金渠道受限。

签名和证书错误通常会返回更明确的错误码。因此,只有在所有退款都持续失败时,才需要重点检查这些配置。

建议的排查步骤

第一步:确认影响范围

需要分别确认:

  • 是单笔交易失败,还是所有交易都失败;
  • 是偶发失败,还是每次调用都失败;
  • 全额退款和部分退款是否都有问题;
  • 不同支付时间、支付渠道和金额的订单是否表现一致;
  • 多台服务器是否都出现相同问题。

如果只有少量请求偶发失败,更可能是支付宝链路的临时异常。如果所有请求都稳定失败,则应重点检查账号、接口权限、请求参数和配置。

第二步:保存完整请求和响应

日志中应包含:

API: alipay.trade.refund
app_id
trade_no 或 out_trade_no
out_request_no
refund_amount
请求时间
支付宝响应的 code、msg、sub_code、sub_msg
网关请求 ID 或 trace ID

不要在日志中记录商户私钥、应用私钥、完整身份证号等敏感信息。

还要确认系统实际调用的是:

alipay.trade.refund

同时检查支付和退款是否使用同一个正式环境、同一个 app_id 以及同一套签名配置。

第三步:先查询退款结果

遇到 SYSTEM_ERROR、请求超时或连接中断时,应先调用:

alipay.trade.fastpay.refund.query

查询时使用原退款请求中的 out_request_no,确认退款是否已经成功。

根据查询结果处理本地状态:

  • 查询确认退款成功:更新本地退款状态,不再重试;
  • 查询确认退款失败或记录不存在:使用原 out_request_no 重试;
  • 查询结果仍不明确:保持”退款处理中”状态,稍后再次查询;
  • 不要因为一次系统错误就生成新的 out_request_no。

第四步:核对退款参数

重点检查:

trade_no / out_trade_no
refund_amount
out_request_no
refund_reason

需要注意:

  • trade_no 和 out_trade_no 至少提供一个;
  • refund_amount 不得超过可退款金额;
  • 金额应使用十进制字符串,避免直接使用浮点数计算;
  • 同一次退款重试时,out_request_no 和退款金额必须保持一致;
  • 多次部分退款时,每次退款都必须使用不同的 out_request_no。

金额建议使用 BigDecimal:

BigDecimal refundAmount = new BigDecimal("10.00");
String amount = refundAmount.setScale(2, RoundingMode.UNNECESSARY).toPlainString();

不要使用:

double refundAmount = 0.1 + 0.2;

浮点数可能产生精度误差,导致提交的金额与预期不一致。

Java 调用示例

下面的示例只展示核心处理方式。具体类名和构造参数应以项目当前使用的支付宝 Java SDK 为准。

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setOutTradeNo("ORDER_202609090001");
model.setRefundAmount("10.00");
model.setOutRequestNo("REFUND_202609090001");
model.setRefundReason("用户申请退款");

request.setBizModel(model);

AlipayTradeRefundResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
    // 退款接口调用成功。
    // 仍建议根据业务需要保存 trade_no、refund_fee 等返回字段。
    markRefundSuccess(response);
} else {
    log.warn(
        "Alipay refund failed: code={}, msg={}, subCode={}, subMsg={}, body={}",
        response.getCode(),
        response.getMsg(),
        response.getSubCode(),
        response.getSubMsg(),
        response.getBody()
    );

    if ("aop.ACQ.SYSTEM_ERROR".equals(response.getSubCode())) {
        // 结果可能不明确,不要直接创建新的退款请求。
        markRefundProcessing();
        queryRefundResult("ORDER_202609090001", "REFUND_202609090001");
    } else {
        markRefundFailed(response.getSubCode(), response.getSubMsg());
    }
}

退款查询示例:

AlipayTradeFastpayRefundQueryRequest request =
    new AlipayTradeFastpayRefundQueryRequest();

AlipayTradeFastpayRefundQueryModel model =
    new AlipayTradeFastpayRefundQueryModel();

model.setOutTradeNo("ORDER_202609090001");
model.setOutRequestNo("REFUND_202609090001");

request.setBizModel(model);

AlipayTradeFastpayRefundQueryResponse response =
    alipayClient.execute(request);

if (response.isSuccess()) {
    // 根据查询响应中的退款信息更新本地状态。
    markRefundSuccess(response);
} else {
    log.warn(
        "Alipay refund query failed: code={}, msg={}, subCode={}, subMsg={}, body={}",
        response.getCode(),
        response.getMsg(),
        response.getSubCode(),
        response.getSubMsg(),
        response.getBody()
    );
}

重试策略

退款是资金操作,重试时必须保证幂等。

可以设置有限次数的退避重试,例如:

首次失败后等待 2 秒
第二次等待 5 秒
第三次等待 15 秒

每次重试前都要先查询退款结果,并始终使用相同的 out_request_no。不要无限重试,也不要让用户通过连续点击退款按钮创建多个退款单。

本地退款状态建议至少分为:

INIT
PROCESSING
SUCCESS
FAILED
MANUAL_REVIEW

遇到 aop.ACQ.SYSTEM_ERROR 时,应先将退款状态设为 PROCESSING,不要立即标记为 FAILED。

仍无法解决时

如果问题持续存在,应整理以下信息并提交给支付宝技术支持:

  • 商户账号及 app_id;
  • 调用接口名称 alipay.trade.refund;
  • 问题发生的准确时间;
  • 支付宝交易号 trade_no;
  • 商户订单号 out_trade_no;
  • 退款请求号 out_request_no;
  • 完整的 code、msg、sub_code、sub_msg;
  • 脱敏后的请求参数和原始响应;
  • 是否所有退款请求都失败;
  • 最近是否更换过证书、密钥、SDK、网关或商户配置。

仅凭 aop.ACQ.SYSTEM_ERROR 无法确定唯一原因。排查时,应先保留原始响应、查询退款结果并保证重试幂等,再根据影响范围判断是临时服务异常,还是商户侧的配置问题。

备注:内容仅供参考。