PAYATHON 2026

支付宝异步回调持续返回fail会自动退款吗?

支付阿杰

明确结论

不会自动退款。

TRADE_SUCCESS 表示支付宝已经完成支付。商家在异步通知中持续返回 fail,只说明商家没有成功确认通知,不会改变交易的成功状态,也不会触发原路退款。

支付宝通常会按异步通知重试策略继续发送通知。超过重试周期后,即使商家仍未返回 success,支付宝也只是停止重试,订单依然处于支付成功状态。退款需要商家主动调用退款接口,或在支付宝商家平台发起。司法、风控、争议处理等特殊情况不在此结论范围内。

为什么不会退款

支付状态和通知状态是两回事:

  • TRADE_SUCCESS 表示支付宝侧的交易结果。
  • success 或 fail 表示商家是否成功接收并处理了本次通知。

异步通知用于同步支付结果,并不是支付事务本身。支付宝收到 fail 后,只能判断商家可能未能正确处理通知,因此会安排重试,但不会撤销已经成功的支付。

即使商家系统在 25 小时内始终返回 fail,也不会自动退款。此时真正需要处理的是订单状态不一致:用户已经付款,但商家系统中的订单可能仍显示“待支付”,也可能因此没有发货。

商家应该如何处理

1. 修复回调接口

回调接口需要完成以下检查:

  1. 使用支付宝公钥验证通知签名。
  2. 校验 app_id 是否属于当前应用。
  3. 校验 seller_id 是否为预期收款账号。
  4. 校验 out_trade_no 是否存在。
  5. 校验 total_amount 是否与本地订单金额一致。
  6. 确认 trade_status 是业务允许处理的状态。
  7. 通过事务更新订单,并保证重复通知不会导致重复发货。
  8. 处理成功后,只返回纯文本 success。

不要返回 JSON、HTML,也不要附加空格或说明文字。

2. 主动查询漏单

如果已经错过异步通知的重试周期,应通过订单查询接口主动核对支付宝交易状态。电脑网站支付通常可以调用 alipay.trade.query,并使用商户订单号 out_trade_no 或支付宝交易号 trade_no 查询。

确认支付成功后,应通过补偿流程更新本地订单,不要继续等待支付宝回调。

3. 建立定时补偿机制

建议定期扫描符合以下条件的订单:

  • 本地状态仍为“待支付”;
  • 已经创建一段时间;
  • 尚未关闭;
  • 支付结果仍不明确。

对这些订单调用查询接口,并在确认状态后补单。这样即使异步通知丢失、超时或处理失败,也能避免支付状态长期不一致。

Java 回调处理示例

下面的示例只展示核心流程。公钥、字符集和 SDK 调用方式应按项目的实际配置填写。

@PostMapping("/alipay/notify")
public void alipayNotify(
        HttpServletRequest request,
        HttpServletResponse response) throws IOException {

    Map<String, String> params = new HashMap<>();

    request.getParameterMap().forEach((key, values) -> {
        if (values != null && values.length > 0) {
            params.put(key, String.join(",", values));
        }
    });

    try {
        boolean signVerified = AlipaySignature.rsaCheckV1(
                params,
                alipayPublicKey,
                "UTF-8",
                "RSA2"
        );

        if (!signVerified) {
            response.getWriter().write("fail");
            return;
        }

        String appId = params.get("app_id");
        String sellerId = params.get("seller_id");
        String outTradeNo = params.get("out_trade_no");
        String tradeNo = params.get("trade_no");
        String tradeStatus = params.get("trade_status");
        BigDecimal totalAmount = new BigDecimal(params.get("total_amount"));

        Order order = orderService.findByOutTradeNo(outTradeNo);

        if (order == null
                || !expectedAppId.equals(appId)
                || !expectedSellerId.equals(sellerId)
                || order.getPayAmount().compareTo(totalAmount) != 0) {
            response.getWriter().write("fail");
            return;
        }

        if ("TRADE_SUCCESS".equals(tradeStatus)
                || "TRADE_FINISHED".equals(tradeStatus)) {
            // 方法内部应使用事务和状态判断保证幂等
            orderService.markPaidIfNecessary(outTradeNo, tradeNo);
        }

        response.getWriter().write("success");
    } catch (Exception e) {
        // 记录完整异常,便于后续补偿处理
        response.getWriter().write("fail");
    }
}

幂等更新可以采用类似的逻辑:

UPDATE orders
SET status = 'PAID',
    alipay_trade_no = ?
WHERE out_trade_no = ?
  AND status = 'WAIT_PAY';

如果受影响行数为 0,还需要判断订单是否已经处理过,不能直接重复发货。

主动查询示例

AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();

AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(outTradeNo);
request.setBizModel(model);

AlipayTradeQueryResponse queryResponse = alipayClient.execute(request);

if (queryResponse.isSuccess()) {
    String tradeStatus = queryResponse.getTradeStatus();

    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        orderService.markPaidIfNecessary(
                outTradeNo,
                queryResponse.getTradeNo()
        );
    }
}

主动查询成功只表示接口调用成功。业务代码仍需检查 trade_status、订单金额、收款账号等字段。

注意事项

  • 不要因为本地订单仍是“待支付”,就直接关闭订单或重新创建同一业务订单。
  • 不要在业务处理完成前返回 success,否则业务处理失败后,支付宝可能不再重试。
  • 不要在回调中执行耗时过长的操作。可以先可靠地记录支付结果,再通过队列处理发货等后续业务。
  • 异步通知可能重复到达。支付入账、积分发放、库存扣减和发货都必须保证幂等。
  • 前端同步跳转不能代替异步通知,也不能单独作为支付成功的依据。
  • 需要退款时,应由商家确认业务条件后主动发起,并记录退款单号、退款金额和处理状态。
  • 具体重试次数和时间间隔可能随支付宝产品规则调整。不要把“25 小时”作为唯一的补单保障,还应同时使用主动查询和定时补偿。

备注:内容仅供参考。