PAYATHON 2026

支付宝沙箱退款返回系统异常,今天是否正在维护?

支付老李

结论

仅凭这次返回结果,无法确认支付宝沙箱今天是否正在维护。

code="20000"、sub_code="aop.ACQ.SYSTEM_ERROR" 和 msg="Service Currently Unavailable" 表明支付宝侧服务暂时不可用或处理异常。原因可能是沙箱服务波动、网关内部异常或临时故障,但这不等于官方发布了维护通知。响应中也没有维护时间或预计恢复时间等信息。

目前退款结果并不明确,因此不要直接判定退款失败,也不要马上反复提交退款请求。应先查询交易和退款状态,再决定是否重试。

返回结果说明

关键字段如下:

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

这些字段的含义是:

  • 10000 通常表示接口业务处理成功。
  • 20000 表示服务暂不可用,本次调用没有返回明确的成功结果。
  • aop.ACQ.SYSTEM_ERROR 是系统级异常,不属于“退款金额错误”或“交易不存在”这类明确的业务错误。
  • refund_fee="0.00" 不能单独作为退款失败的最终依据,仍应以退款查询结果和交易状态为准。

还要注意,请求中的 outTradeNo 是 030710482217413,而响应中的 out_trade_no 是 VP250227075320250307002。两者不一致,应优先排查以下问题:

  • 实际发送给支付宝的 out_trade_no 是否经过业务层转换;
  • 日志中的请求和响应是否来自同一次调用;
  • SDK 请求对象是否被复用;
  • 是否通过 trade_no 定位交易,但日志仍打印了另一个本地订单号;
  • 中间服务是否重新组装或覆盖了退款参数。

建议处理步骤

1. 查询原交易状态

先调用 alipay.trade.query,并使用支付宝交易号:

{
  "trade_no": "2025030722001472610505209996"
}

确认交易是否存在,以及当前是否仍可退款。同时核对查询结果中的 out_trade_no,判断它对应的是哪个本地订单。

2. 查询退款结果

如果退款请求设置了 out_request_no,调用 alipay.trade.fastpay.refund.query:

{
  "trade_no": "2025030722001472610505209996",
  "out_request_no": "唯一退款请求号"
}

out_request_no 应由商户系统生成,并在同一次退款的查询和重试过程中保持不变。如果每次重试都随机生成新的值,支付宝可能会将其识别为新的退款请求。

如果原调用没有保存 out_request_no,需要结合交易查询结果、商户退款记录和支付宝沙箱账单判断实际状态。

3. 核对沙箱环境配置

检查请求是否混用了正式环境和沙箱环境的数据:

  • 网关地址应指向支付宝沙箱网关;
  • app_id 应属于对应的沙箱应用;
  • 应使用沙箱应用密钥和支付宝沙箱公钥;
  • trade_no 必须由同一沙箱环境中的交易生成;
  • 沙箱买家账号、卖家账号和应用应属于同一套沙箱配置。

正式环境生成的 trade_no 不能直接用于沙箱退款,沙箱环境生成的 trade_no 也不能用于正式环境退款。

4. 确认未退款后再有限重试

查询并确认退款没有成功后,可以对系统异常进行延迟重试,例如等待数秒后再次调用,并采用指数退避策略。不要在短时间内无限循环调用。

重试时应保持以下信息不变:

  • 原交易标识;
  • 退款金额;
  • out_request_no;
  • 退款业务含义。

Java 示例

下面是一种“先发起退款,结果不明确时再查询”的处理方式。支付宝 Java SDK 不同版本的类名可能略有差异,请以项目当前使用的 SDK 为准。

String tradeNo = "2025030722001472610505209996";
String refundRequestNo = "REFUND_030710482217413_001";

AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.setBizContent("""
{
  "trade_no": "%s",
  "refund_amount": "10.00",
  "refund_reason": "正常退款",
  "out_request_no": "%s"
}
""".formatted(tradeNo, refundRequestNo));

AlipayTradeRefundResponse response = alipayClient.execute(request);

if ("10000".equals(response.getCode())) {
    // 退款成功,保存支付宝返回结果
} else if ("20000".equals(response.getCode())
        || "aop.ACQ.SYSTEM_ERROR".equals(response.getSubCode())) {
    // 结果不明确,不要立即认定退款失败
    // 稍后调用退款查询接口确认
} else {
    // 明确的业务失败,根据 sub_code 和 sub_msg 处理
}

退款查询示例:

AlipayTradeFastpayRefundQueryRequest queryRequest =
        new AlipayTradeFastpayRefundQueryRequest();

queryRequest.setBizContent("""
{
  "trade_no": "%s",
  "out_request_no": "%s"
}
""".formatted(tradeNo, refundRequestNo));

AlipayTradeFastpayRefundQueryResponse queryResponse =
        alipayClient.execute(queryRequest);

if ("10000".equals(queryResponse.getCode())) {
    // 根据查询结果更新本地退款状态
} else {
    // 记录 code、sub_code、msg、sub_msg,等待后续查询或人工核查
}

如何确认是否正在维护

要确认沙箱是否正在维护,应以支付宝开放平台公告、沙箱控制台提示或支付宝技术支持的答复为准。当前响应只能说明调用时发生了系统级异常,无法证明当时存在计划维护。

提交问题时,可以提供以下信息,但不要公开应用私钥、完整签名原文或用户敏感数据:

  • 沙箱 app_id;
  • API 名称 alipay.trade.refund;
  • 请求发生的准确时间和时区;
  • trade_no、实际发送的 out_trade_no;
  • out_request_no;
  • 返回的 code、sub_code、msg 和 sub_msg;
  • 支付宝返回的请求标识或网关链路标识(如果 SDK 日志中存在)。

注意事项

  • 遇到系统异常时,应遵循“先查询、后重试”的原则,避免重复退款。
  • 金额建议使用带两位小数的字符串,例如 "10.00"。
  • HTTP 请求成功或验签成功不代表退款成功,最终仍需检查业务响应码。
  • 当前请求与响应中的 out_trade_no 不一致,这个问题比“是否维护”更应该优先排查。
  • 示例中的交易日期看起来属于历史数据。如果是在较晚时间重新测试,还需要确认沙箱交易是否仍然存在、是否已经退款,以及沙箱数据是否被重置。

备注:内容仅供参考。