支付宝异步回调持续返回fail会自动退款吗?
明确结论
不会自动退款。
TRADE_SUCCESS 表示支付宝已经完成支付。商家在异步通知中持续返回 fail,只说明商家没有成功确认通知,不会改变交易的成功状态,也不会触发原路退款。
支付宝通常会按异步通知重试策略继续发送通知。超过重试周期后,即使商家仍未返回 success,支付宝也只是停止重试,订单依然处于支付成功状态。退款需要商家主动调用退款接口,或在支付宝商家平台发起。司法、风控、争议处理等特殊情况不在此结论范围内。
为什么不会退款
支付状态和通知状态是两回事:
TRADE_SUCCESS表示支付宝侧的交易结果。success或fail表示商家是否成功接收并处理了本次通知。
异步通知用于同步支付结果,并不是支付事务本身。支付宝收到 fail 后,只能判断商家可能未能正确处理通知,因此会安排重试,但不会撤销已经成功的支付。
即使商家系统在 25 小时内始终返回 fail,也不会自动退款。此时真正需要处理的是订单状态不一致:用户已经付款,但商家系统中的订单可能仍显示“待支付”,也可能因此没有发货。
商家应该如何处理
1. 修复回调接口
回调接口需要完成以下检查:
- 使用支付宝公钥验证通知签名。
- 校验
app_id是否属于当前应用。 - 校验
seller_id是否为预期收款账号。 - 校验
out_trade_no是否存在。 - 校验
total_amount是否与本地订单金额一致。 - 确认
trade_status是业务允许处理的状态。 - 通过事务更新订单,并保证重复通知不会导致重复发货。
- 处理成功后,只返回纯文本
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 小时”作为唯一的补单保障,还应同时使用主动查询和定时补偿。
备注:内容仅供参考。
