PAYATHON 2026

手机网站支付支付宝服务器异步通知延迟的原因是什么?

支付小周

结论

支付宝异步通知并非实时回调,可能延迟,也可能重复发送。示例订单从付款到记录通知触发相隔约 86 分钟。仅根据交易号和时间还不能确定原因,常见情况有:

  • 商户通知地址暂时无法访问、响应超时,或者没有返回纯文本 success,支付宝随后发起重试。
  • 商户网关、负载均衡、WAF、防火墙或应用线程池短时异常。
  • 支付宝侧的通知任务排队或出现短时波动。
  • 商户记录的是业务处理完成时间,并非 HTTP 请求实际到达时间。

业务系统不应把异步通知作为支付结果的唯一来源。如果订单长时间停留在“待支付”状态,应主动调用 alipay.trade.query 查询交易状态。无论处理异步通知还是主动查询,都要保证幂等性。

如何判断延迟发生在哪一侧

先核对三个时间:

  1. 支付宝交易的实际付款时间。
  2. 商户服务器首次收到通知请求的时间。
  3. 商户订单更新为已支付的时间。

该订单的时间如下:

  • 付款时间:2020-09-03 21:54
  • 记录的触发通知时间:2020-09-03 23:20
  • 时间差:约 86 分钟

如果商户 Web 服务器的访问日志显示,通知请求直到 23:20 才第一次到达,那么延迟发生在通知发出前或传输途中,还需结合支付宝侧的通知记录继续排查。

如果日志显示通知早已到达,但订单直到 23:20 才更新,问题通常出在商户应用内部,例如验签失败、数据库锁等待、消息积压、事务回滚或异常重试。

同时要确认支付宝是否发送过多次通知。如果早期请求已经到达,但商户返回的不是 success、HTTP 状态异常或响应超时,那么 23:20 收到的可能只是重试,不一定是首次通知。

建议的排查步骤

1. 检查入口访问日志

使用商户订单号和支付宝交易号搜索 Nginx、网关及应用日志:

rg 'UZFB20200903215430707613676|2020090322001438751425798479' access.log application.log

重点检查:

  • 第一次请求到达的时间。
  • HTTP 状态码及请求耗时。
  • 是否出现 499、502、503、504 等异常状态码。
  • 相同 trade_no 是否收到过重复通知。
  • 验签、金额校验和数据库事务是否成功。
  • 接口实际返回的内容是否为纯文本 success。

2. 检查通知接口的响应

通知处理成功后,接口应直接返回:

success

不要返回 JSON、HTML、额外空格或页面模板。下面这种响应不符合要求:

{"code":200,"message":"success"}

处理失败时,可以返回 failure 或其他非成功响应,让支付宝按照通知机制重试。接口也不能长时间阻塞,否则即使本地业务最终处理成功,支付宝仍可能把本次通知判定为失败。

3. 主动查询交易状态

订单超过预期时间仍处于“待支付”状态时,可以调用 alipay.trade.query 查询。请求通常传入 out_trade_no 或 trade_no,具体字段以项目当前使用的支付宝 SDK 和接口文档为准。

AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent(
    "{\"out_trade_no\":\"UZFB20200903215430707613676\"}"
);

AlipayTradeQueryResponse response = alipayClient.execute(request);

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

    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        // 校验订单金额、商户身份等信息后,幂等地更新订单状态
        markOrderPaidIdempotently(
            response.getOutTradeNo(),
            response.getTradeNo(),
            response.getTotalAmount()
        );
    }
}

查询接口调用成功不等于订单已经支付。系统必须继续检查 trade_status,并核对订单号、金额和收款方身份,不能直接把订单改为已支付。

4. 正确处理异步通知

下面是简化后的 Java 处理结构。SDK 类名和支付宝公钥的获取方式需要按照项目的实际配置调整:

@PostMapping(value = "/alipay/notify", produces = "text/plain;charset=UTF-8")
public String alipayNotify(HttpServletRequest request) {
    Map<String, String> params = new HashMap<>();

    request.getParameterMap().forEach((key, values) -> {
        params.put(key, String.join(",", values));
    });

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

        if (!valid) {
            return "failure";
        }

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

        // 从数据库读取商户订单,并校验:
        // 1. out_trade_no 是否存在
        // 2. total_amount 是否与订单金额一致
        // 3. app_id、seller_id 是否属于当前商户
        // 4. trade_no 是否存在冲突

        if ("TRADE_SUCCESS".equals(tradeStatus)
                || "TRADE_FINISHED".equals(tradeStatus)) {
            updateOrderIdempotently(
                outTradeNo,
                tradeNo,
                totalAmount,
                appId,
                sellerId
            );
        }

        return "success";
    } catch (Exception exception) {
        // 记录异常,但不要记录私钥等敏感信息
        return "failure";
    }
}

updateOrderIdempotently 应使用唯一约束、条件更新或状态机来防止重复处理。例如:

UPDATE payment_order
SET status = 'PAID',
    alipay_trade_no = :trade_no,
    paid_at = :paid_at
WHERE out_trade_no = :out_trade_no
  AND status = 'WAIT_PAY';

如果更新行数为 0,需要判断订单是已经处理过,还是根本不存在或数据不一致,不能一律按成功处理。

接口设计注意事项

  • 不要依赖通知的到达顺序,系统应能安全处理重复通知和延迟通知。
  • 使用异步通知中的金额前,必须与本地订单金额进行核对。
  • 验签应使用支付宝公钥,不能使用应用私钥或应用公钥。
  • 通知参数应按照支付宝 SDK 的要求原样参与验签,不要自行拼接,以免验签失败。
  • 日志中应记录通知原始参数、首次接收时间、验签结果、处理结果和响应耗时,同时过滤敏感数据。
  • 通知接口只处理必要的校验和可靠落库。发货、积分、短信等耗时操作应交给内部消息队列。
  • 前端同步跳转只能改善用户体验,不能作为可信的支付成功依据。
  • 主动查询需要限制频率,并设置明确的订单补偿周期,避免无限轮询。
  • 不能根据用户截图或客户端回传结果直接确认支付成功。

如果确认商户侧在 23:20 之前没有收到任何请求,而且通知地址当时始终可以访问,应向支付宝技术支持提供 trade_no、out_trade_no、付款时间、首次收到通知的时间及商户网关日志,请平台查询该笔交易的通知投递记录。在缺少投递记录的情况下,不能仅凭 86 分钟的时间差认定支付宝系统发生异常。

备注:内容仅供参考。