PAYATHON 2026

支付宝支付二级域名异步回调失败如何排查

支付小周

明确结论

二级域名本身通常不会导致支付宝异步回调失败。同步跳转正常,只能说明支付请求和同步返回链路基本可用,不能证明支付宝可以访问程序 B 的 notify_url。

建议优先检查以下几项:

  1. 程序 B 生成的 notify_url 是否确实指向二级域名。
  2. 该地址能否从公网直接访问,是否存在登录、验证码、IP 限制或来源校验。
  3. 二级域名的 DNS、HTTPS、反向代理和服务器路由是否配置正确。
  4. 程序 B 是否正确验签,并在处理成功后返回支付宝要求的成功文本。
  5. 支付宝后台配置、应用、商户号和支付参数是否与程序 B 使用的账号一致。

为什么同步正常、异步失败

支付流程中的同步返回和异步通知是两条不同的链路:

  • 同步返回:用户支付完成后,浏览器跳转到 return_url。
  • 异步通知:支付宝服务器向商户服务器发起 HTTP 请求,访问 notify_url。

因此,浏览器能够打开二级域名,并不代表支付宝服务器也能访问该地址。常见原因包括:

  • notify_url 仍然写成了程序 A 的一级域名;
  • 二级域名可以访问首页,但没有配置对应的接口路由;
  • Nginx 或 Apache 将二级域名转发到了错误的程序;
  • HTTPS 证书不匹配、证书链不完整或 TLS 配置异常;
  • 回调地址被登录中间件、CSRF 校验、鉴权组件或 WAF 拦截;
  • 回调接口返回了 404、403、500 或其他非预期内容;
  • 程序收到了通知,但验签失败或没有返回 success;
  • 程序 B 使用了错误的支付宝公钥、应用私钥、app_id 或商户配置;
  • 回调处理失败后重复执行,导致订单状态或业务逻辑异常。

第一步:确认实际发送的 notify_url

不要只检查配置文件,还应查看程序 B 实际发起支付请求时提交的参数。重点确认:

  • 域名是否为程序 B 的二级域名;
  • 路径是否正确;
  • 是否使用了 https;
  • 是否包含错误的端口、路径或多余空格;
  • 是否被框架中的默认配置覆盖。

例如,支付请求中应明确设置类似参数:

$request->setReturnUrl('https://pay.example.com/alipay/return');
$request->setNotifyUrl('https://pay.example.com/alipay/notify');

接口名称会因支付框架和 SDK 版本而不同,实际项目应以当前 SDK 的接口为准。关键不在于方法名,而在于最终提交给支付宝的 notify_url 值。

可以在发送支付请求前,记录经过脱敏处理的参数:

logger()->info('alipay payment request', [
    'app_id' => $request->getAppId(),
    'notify_url' => $request->getNotifyUrl(),
    'return_url' => $request->getReturnUrl(),
    'out_trade_no' => $request->getOutTradeNo(),
]);

不要把私钥、完整签名或敏感用户信息写入日志。

第二步:从公网测试回调地址

在服务器外部网络环境中访问回调地址,例如:

curl -i -X POST \
  'https://pay.example.com/alipay/notify' \
  -d 'test=1'

重点观察:

  • DNS 是否解析到正确服务器;
  • 是否能够建立 HTTPS 连接;
  • HTTP 状态码是否为 200;
  • 是否出现 301、302 跳转;
  • 是否返回 403、404 或 500;
  • 是否被跳转到登录页;
  • 是否被网关或 WAF 拦截。

测试请求没有真实支付宝签名,业务层返回验签失败是正常的。但在网络层和路由层,必须能够到达正确的回调程序。

如果服务器配置了 Nginx,可以检查二级域名对应的站点配置是否指向程序 B:

server {
    listen 443 ssl;
    server_name pay.example.com;

    root /var/www/program-b/public;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9000;
    }
}

如果程序使用 PHP-FPM、Node.js、Java 或其他运行环境,fastcgi_pass、反向代理地址和路由写法需要根据实际部署方式调整。

第三步:检查回调接口是否被拦截

异步通知接口通常不适合套用普通网页接口的全部安全中间件。检查程序 B 是否对该路径启用了:

  • 用户登录校验;
  • CSRF 校验;
  • 管理员权限校验;
  • 请求来源校验;
  • Referer 校验;
  • 固定 IP 白名单;
  • 强制 JSON 请求头;
  • 只允许 GET 请求;
  • 只允许特定 User-Agent;
  • WAF 或防火墙拦截。

支付宝通知通常由服务端发起,不能依赖浏览器 Cookie、Referer 或用户登录状态。回调接口应允许接收支付宝发送的 POST 参数,并通过支付宝签名机制完成身份验证。

第四步:检查验签和支付宝配置

程序 B 应使用与当前支付宝应用匹配的配置,至少核对:

  • app_id;
  • 商户号;
  • 应用私钥;
  • 支付宝公钥;
  • 签名算法;
  • 网关地址;
  • 沙箱或正式环境;
  • 回调参数中的订单号和金额。

如果程序 A 和程序 B 共用支付框架,不代表两套程序可以直接共用全部配置。尤其要确认程序 B 没有读取程序 A 的环境变量、缓存配置或旧配置文件。

伪代码示例如下:

public function notify()
{
    $params = request()->post();

    // 具体验签方法以当前支付宝 SDK 为准
    $verified = $this->alipayClient->verifyNotify($params);

    if (!$verified) {
        logger()->warning('alipay notify signature verification failed', [
            'out_trade_no' => $params['out_trade_no'] ?? null,
        ]);

        return response('fail', 200);
    }

    $outTradeNo = $params['out_trade_no'] ?? '';
    $tradeStatus = $params['trade_status'] ?? '';

    if (in_array($tradeStatus, ['TRADE_SUCCESS', 'TRADE_FINISHED'], true)) {
        // 根据订单号查询本地订单
        // 校验金额、商户号和应用标识
        // 使用事务更新订单状态
        // 已完成的订单不得重复发货或重复记账
    }

    // 确认处理成功后再返回
    return response('success', 200);
}

这里的 verifyNotify() 仅用于说明处理流程,实际方法名可能是 SDK 提供的 rsaCheckV1、通知验证器或框架封装方法。不能只凭请求参数中的 trade_status 判断支付成功,仍应先完成验签,并核对订单号、金额、商户和应用信息。

第五步:确认返回内容和状态码

异步回调接口即使已经收到通知,如果返回内容不符合要求,支付宝仍可能认为通知处理失败并继续重试。

检查以下内容:

  • HTTP 状态码是否为 200;
  • 成功处理后,响应正文是否为支付宝 SDK 要求的成功文本,通常是 success;
  • 是否输出了 HTML、调试信息、异常堆栈或 JSON;
  • 是否在返回前发生了重定向;
  • 是否因为异常导致连接中断。

建议在回调入口和出口记录日志:

logger()->info('alipay notify received', [
    'out_trade_no' => request('out_trade_no'),
    'trade_status' => request('trade_status'),
]);

// 验签、校验订单并完成业务处理

logger()->info('alipay notify processed', [
    'out_trade_no' => request('out_trade_no'),
]);

return response('success', 200);

生产环境不要直接输出异常堆栈,也不要因为重复通知返回失败。异步通知可能会重试,因此接口必须具备幂等性:同一个 out_trade_no 重复到达时,只能完成一次发货、入账或权益发放。

第六步:检查服务器和应用日志

建议同时查看以下日志:

  • Nginx 或 Apache access log;
  • Web 服务器 error log;
  • 应用日志;
  • PHP-FPM、Node.js 或 Java 运行日志;
  • WAF、防火墙和负载均衡日志;
  • DNS 和证书监控记录。

可以通过请求时间、订单号或回调路径进行关联。通常可以这样判断:

  • 完全没有访问日志:优先检查 notify_url、DNS、证书、网络和防火墙;
  • 有访问日志但返回 404:检查二级域名路由和部署目录;
  • 返回 403:检查鉴权、WAF、权限和访问控制;
  • 返回 500:检查应用异常、配置和数据库连接;
  • 应用收到请求但验签失败:检查支付宝公钥、签名算法、原始参数和环境配置;
  • 验签成功但订单未更新:检查订单查询、金额校验、事务和幂等逻辑;
  • 业务处理成功但支付宝仍重试:检查响应正文、响应状态码和响应时机。

还需要核对支付宝后台配置的情况

如果使用的是同一个支付宝应用,回调域名是否需要在后台配置,以及配置项的具体名称,取决于所使用的产品、接口和当前平台规则。不能仅凭“已经添加一级域名和二级域名”判断配置一定生效。

应以当前支付产品的官方控制台配置和 SDK 文档为准,并确认:

  • 程序 B 使用的 app_id 与后台配置属于同一应用;
  • 正式环境和沙箱环境没有混用;
  • 使用的支付产品确实要求配置域名;
  • 配置保存后已经生效;
  • 域名拼写、协议和端口与实际 notify_url 一致。

不过,即使后台配置正确,只要服务器无法访问回调地址、接口返回错误或验签失败,异步通知仍然无法正常完成。

建议的排查顺序

先记录程序 B 最终提交的 notify_url,再从公网使用 curl 测试该地址,随后查看服务器访问日志,确认请求是否到达程序 B。若请求已经到达,再依次检查路由、鉴权中间件、验签、订单校验、幂等处理和最终响应内容。

多数情况下,问题不在于使用一级域名还是二级域名,而在于二级域名没有正确指向程序 B,或者回调接口虽然收到了请求,却没有按照支付宝通知协议完成处理并返回成功。

备注:内容仅供参考。