PAYATHON 2026

手机 H5 支付后 notify_url 无法接收支付宝通知

支付阿杰

结论

通常不需要在支付宝应用网关中配置 notify_url。

手机网站支付(alipay.trade.wap.pay)的异步通知地址由支付请求中的 notify_url 指定。支付成功后,支付宝服务器会向该地址发送 HTTP POST 请求。

浏览器能打开这个地址,只能证明浏览器发出的 GET 请求可以访问,不能证明支付宝服务器发送的 POST 通知能够到达,也不能证明业务系统可以正确处理通知。

排查步骤

1. 确认支付请求中传入了 notify_url

以支付宝 Java SDK 为例:

AlipayClient alipayClient = new DefaultAlipayClient(
    gatewayUrl,
    appId,
    merchantPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

request.setReturnUrl("https://www.example.com/pay/return");
request.setNotifyUrl("https://www.example.com/api/alipay/notify");

request.setBizContent("""
{
  "out_trade_no": "ORDER_202609090001",
  "total_amount": "0.01",
  "subject": "订单支付",
  "product_code": "QUICK_WAP_WAY"
}
""");

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
String paymentForm = response.getBody();

return_url 和 notify_url 的作用不同:

  • return_url:支付结束后跳转用户浏览器,用于展示页面。
  • notify_url:由支付宝服务器主动调用,用于确认支付结果并更新订单。

业务系统不能只根据 return_url 判断支付是否成功。

2. 确认通知地址可以从公网访问

notify_url 需要满足以下条件:

  • 使用完整的公网 URL。
  • 域名可以通过公网 DNS 正常解析。
  • 端口可以从公网访问,建议优先使用标准的 443 端口。
  • HTTPS 证书有效,证书链完整,证书与域名匹配。
  • 不使用 localhost、内网 IP 或只能通过公司 VPN 访问的地址。
  • 接口不要求登录,不依赖 Cookie,也不会跳转到登录页。
  • 网关、防火墙、CDN 或 WAF 不会拦截支付宝发出的 POST 请求。

可以在业务服务器之外测试 POST 请求:

curl -i -X POST \
  'https://www.example.com/api/alipay/notify' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'out_trade_no=test&trade_status=TRADE_SUCCESS'

这项测试只能检查网络和路由是否正常,不能代替真实通知的验签测试。

3. 确认后端路由接受 POST 请求

支付宝异步通知通常使用 application/x-www-form-urlencoded 格式提交参数。如果接口只支持 GET,即使浏览器可以正常访问,支付宝发送的 POST 请求仍可能返回 404、405,或者被框架拒绝。

Spring Boot 示例:

@PostMapping(
    value = "/api/alipay/notify",
    consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
)
public String alipayNotify(HttpServletRequest request) {
    Map<String, String> params = new HashMap<>();

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

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

        if (!verified) {
            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. 校验 app_id、seller_id 是否属于当前商户
        // 2. 根据 out_trade_no 查询本地订单
        // 3. 校验订单金额和通知金额是否一致
        // 4. 判断 trade_status 是否为业务认可的支付状态
        // 5. 使用事务和幂等机制更新订单

        return "success";
    } catch (AlipayApiException exception) {
        return "failure";
    }
}

示例中的 alipayPublicKey 是支付宝公钥,不是应用公钥或应用私钥。具体如何获取公钥,取决于应用使用的是密钥模式还是证书模式。

4. 正确返回响应内容

通知处理成功后,接口需要直接返回以下纯文本:

success

处理响应时需要注意:

  • 不要返回 JSON,例如 {"result":"success"}。
  • 不要返回 HTML 页面。
  • 不要在 success 前后添加其他业务文本。
  • 不要返回重定向响应。
  • 尽快完成处理并返回响应。耗时任务可以交给内部队列异步执行。

如果验签失败、订单信息不一致或业务处理失败,可以返回:

failure

如果没有返回支付宝认可的成功响应,支付宝可能按照其通知机制重试。具体的重试次数和时间间隔,以当前接入文档为准。

5. 检查网关和安全策略

一般不需要在支付宝开放平台中配置“应用网关”。不过,如果系统前面部署了 API Gateway、Nginx、CDN 或 WAF,仍要确认通知路径已经正确放行。

Nginx 示例:

location = /api/alipay/notify {
    proxy_pass http://payment-service;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_connect_timeout 5s;
    proxy_read_timeout 15s;
}

需要重点检查:

  • 是否只允许浏览器常见的 User-Agent。
  • 是否拦截表单 POST。
  • 是否启用了 CSRF 校验。
  • 是否对没有携带 Token 的请求返回 401 或 403。
  • 请求是否因限流、IP 白名单或地区策略被拦截。
  • 是否将 HTTP 请求强制跳转到 HTTPS。
  • 多层网关之间是否存在路径重写错误。

对于 Spring Security,可以只让通知路径忽略 CSRF,不要关闭整个系统的 CSRF 防护:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.csrf(csrf -> csrf.ignoringRequestMatchers("/api/alipay/notify"));

    return http.build();
}

通知接口免登录,不代表可以省略安全校验。接口仍须通过支付宝签名、商户身份、订单号和金额等信息验证通知。

为什么浏览器能打开,通知仍然收不到

常见原因有:

  • 浏览器发送的是 GET 请求,而接口没有开放 POST。
  • 浏览器携带了登录 Cookie,支付宝服务器没有。
  • 通知接口被 CSRF、鉴权、WAF 或防火墙拦截。
  • HTTPS 证书存在兼容性问题。
  • 支付请求中没有实际传入 notify_url,或传入了其他环境的地址。
  • 路由返回了 301、302、401、403、404、405 或 500。
  • 接口收到了通知,但验签时使用了错误的支付宝公钥。
  • 参数读取方式有误,导致用于验签的原文发生变化。
  • 业务处理成功后,接口没有直接返回 success。
  • 应用部署在多台服务器上,但只检查了其中一台的日志。

安全与业务注意事项

异步通知可能重复到达,所以订单更新必须具备幂等性。例如,只有待支付订单才能首次更新为已支付状态。已经支付的订单再次收到相同通知时,应直接返回 success,不要重复扣减库存或发放权益。

处理通知时,至少要核对以下参数:

  • app_id
  • seller_id
  • out_trade_no
  • total_amount
  • trade_status

不能只根据 trade_status 更新订单,也不能因为请求来自某个固定 IP 就跳过验签。支付宝的网络出口策略可能发生调整。是否支持 IP 白名单及其具体范围,应以当前官方接入说明为准。

如果接口日志中完全没有请求,先检查支付请求是否实际传入了 notify_url,再排查公网链路。如果日志中有请求,但订单没有更新,则重点检查 HTTP 状态码、验签结果、公钥配置、金额校验和接口最终返回的内容。

备注:内容仅供参考。