PAYATHON 2026

小程序支付成功但后端未收到异步通知

支付老李

结论

支付成功不等于后端一定能收到异步通知。支付宝是否发送通知,主要取决于创建交易时有没有正确传入 notify_url,以及支付宝服务器能否访问该地址。

仅凭现有信息,无法确认交易号 2020110722001422215734505242 在平台侧的通知记录。可以先检查以下三项:

  1. 是否通过 request.setNotifyUrl(...) 设置了异步通知地址。
  2. notify_url 是否能从公网访问,是否被防火墙、登录鉴权、反向代理或其他安全策略拦截。
  3. 通知接口处理完请求后,是否以纯文本返回 success。

如果创建交易时没有携带有效的 notify_url,即使支付成功,支付宝也不会向业务服务器发送这笔交易的异步通知。

正确设置异步通知地址

使用 alipay-sdk-java 创建小程序支付交易时,需要在请求对象上设置 notify_url。不要把它放进业务参数,也不能用 return_url 代替。

AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();

AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo(outTradeNo);
model.setTotalAmount("0.01");
model.setSubject("测试商品");
model.setBuyerId(buyerId);

request.setBizModel(model);

// 必须设置在 request 上
request.setNotifyUrl("https://example.com/alipay/notify");

AlipayTradeCreateResponse response = alipayClient.execute(request);

if (response.isSuccess()) {
    String tradeNo = response.getTradeNo();
    // 将 tradeNo 返回给小程序,由前端调用支付能力
} else {
    // 记录 code、subCode、msg、subMsg,便于定位问题
}

还要检查最终发送到支付宝网关的请求,确认其中确实包含 notify_url。只在应用后台配置网关地址,通常不能代替单笔支付请求中的 notify_url。

检查通知地址

通知地址应满足这些条件:

  • 使用完整的公网 URL,例如 https://example.com/alipay/notify。
  • 不能使用 localhost、127.0.0.1、局域网 IP,也不能是只能通过 VPN 访问的地址。
  • 不应跳转到登录页,也不要返回 301 或 302。
  • 接口应允许支付宝直接发送 POST 请求。
  • 不要要求 Cookie、Token、自定义请求头或登录状态。
  • 检查 Nginx、WAF、CDN、防火墙和安全组有没有拦截请求。
  • 确认域名解析、TLS 证书和服务端口均正常。
  • 排查应用日志时,也要查看网关访问日志,以免请求已经到达 Nginx,却没有进入 Java 应用。

可以从公网环境模拟访问通知地址。不过,模拟请求只能验证网络是否连通,不能证明支付宝发送过通知。

验签并返回 success

支付宝会以表单参数的形式提交异步通知。后端需要先取得全部参数,再使用支付宝公钥验签。验签通过后,还要核对订单信息。

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

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

    for (Map.Entry<String, String[]> entry
            : request.getParameterMap().entrySet()) {
        String[] values = entry.getValue();
        params.put(
            entry.getKey(),
            values == null ? "" : String.join(",", values)
        );
    }

    boolean signVerified;

    try {
        signVerified = AlipaySignature.rsaCheckV1(
            params,
            alipayPublicKey,
            "UTF-8",
            "RSA2"
        );
    } catch (AlipayApiException e) {
        signVerified = false;
    }

    if (!signVerified) {
        response.setContentType("text/plain;charset=UTF-8");
        response.getWriter().print("failure");
        return;
    }

    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");

    // 必须核对 app_id、out_trade_no、total_amount、seller_id 等关键字段
    // 订单更新必须支持幂等,避免重复通知导致重复记账
    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {

        // 根据 outTradeNo 查询本地订单
        // 核对金额和收款方
        // 在事务中更新订单状态
    }

    response.setContentType("text/plain;charset=UTF-8");
    response.getWriter().print("success");
}

接口必须返回纯文本 success。如果返回的是 JSON、HTML、空字符串、异常页面或其他内容,平台可能会把本次处理视为失败,并按平台策略重试。

通过主动查询补偿

不能把异步通知作为确认支付结果的唯一依据。前端显示支付成功后,后端可以调用 alipay.trade.query 主动查询交易状态。定时任务也应扫描长时间停留在待支付状态的订单。

AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();

AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(outTradeNo);
// 也可以根据实际情况使用 model.setTradeNo(tradeNo);

request.setBizModel(model);

AlipayTradeQueryResponse response = alipayClient.execute(request);

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

    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        // 核对金额、商户订单号、应用和收款方后,幂等更新订单
    }
} else {
    // 记录 code、subCode、msg、subMsg
}

主动查询只是补偿机制,仍然需要校验金额、商户订单号、应用身份和收款方。

建议的排查顺序

  1. 查看创建交易时的完整请求日志,确认其中包含正确的 notify_url。
  2. 查询这笔交易的实际状态,确认状态是 TRADE_SUCCESS 或 TRADE_FINISHED,不要只根据前端提示判断。
  3. 查看通知域名对应的 Nginx、负载均衡和应用访问日志。
  4. 检查通知接口是否被鉴权、CSRF、防火墙或 WAF 拦截。
  5. 确认接口没有发生重定向,并且能够快速返回纯文本 success。
  6. 核对支付宝公钥、签名类型和字符集是否与实际配置一致。
  7. 如果公网入口完全没有这笔交易的访问记录,可通过支付宝商家平台或技术支持渠道,结合交易号、应用 app_id、商户订单号和交易创建时间查询平台侧通知记录。

注意事项

  • notify_url 是异步通知地址,return_url 是页面跳转地址,不能混用。
  • 不要只根据小程序前端回调把订单标记为已支付。
  • 通知可能重复到达,订单处理必须支持幂等。
  • 验签前不要自行拼接参数,也不要把通知转换成 JSON。
  • 如果业务处理耗时较长,应先可靠保存通知,再异步处理业务逻辑。
  • SDK 版本通常不会造成某一笔交易完全不发送通知。应先检查请求参数、网络入口和接口响应。如果准备升级 SDK,还需要结合当前 Java 版本和支付宝开放平台的兼容要求单独验证。

备注:内容仅供参考。