PAYATHON 2026

支付宝订单码支付成功后未收到异步通知回调

支付老李

结论

问题很可能是回调地址开头多了一个空格:

$notifyUrl = ' https://shopcd.net/alipay/notify.php';

https:// 前面的空格会让 notify_url 变成无效 URL。支付宝可能已经生成了交易通知数据,但无法识别投递地址,所以诊断工具中的“通知地址”为空,服务器也收不到请求。

改成下面这样即可:

$notifyUrl = 'https://shopcd.net/alipay/notify.php';

应用网关、授权回调地址和服务器 IP 白名单都不能替代交易接口中的 notify_url。调用 alipay.trade.precreate 时,需要通过 notify_url 单独传入异步通知地址。

为什么诊断工具中的“通知地址”为空

诊断结果里的“通知内容”以这段内容开头:

https://shopcd.net/alipay/notify.php?gmt_create=...

但“通知地址”是空的。结合代码中的前导空格,支付宝很可能没有将传入值解析成有效的通知地址。

正常情况下:

  • “通知地址”应显示 https://shopcd.net/alipay/notify.php
  • “通知内容”应包含 gmt_create、trade_status、out_trade_no、sign 等通知参数
  • 支付宝会向该地址发送 HTTP POST 请求

现在的诊断结果说明,通知地址可能在请求阶段就已经失效,而不是 notify.php 收到请求后没有正确读取参数。

修正预下单代码

传入 SDK 前可以先去除首尾空白,并校验 URL:

<?php

$notifyUrl = trim('https://shopcd.net/alipay/notify.php');

if (filter_var($notifyUrl, FILTER_VALIDATE_URL) === false) {
    throw new RuntimeException('支付宝异步通知地址不合法:' . $notifyUrl);
}

$request = new AlipayTradePrecreateRequest();

$bizcontent = json_encode([
    'out_trade_no'    => $orderId,
    'total_amount'    => $money,
    'subject'         => $good,
    'timeout_express' => '30m',
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

if ($bizcontent === false) {
    throw new RuntimeException('biz_content 编码失败:' . json_last_error_msg());
}

$request->setNotifyUrl($notifyUrl);
$request->setBizContent($bizcontent);

$response = $aop->execute($request);

还要记录预下单响应,确认接口是否调用成功,并检查是否返回了 sub_code、sub_msg 等错误信息。不能只看二维码是否生成。

正确接收异步通知

支付宝交易异步通知通常以 application/x-www-form-urlencoded 格式发送 POST 请求,因此业务参数应从 $_POST 读取,而不是 $_GET:

<?php

$logFile = __DIR__ . '/alipay_notify.log';

$rawBody = file_get_contents('php://input');
$postData = $_POST;

file_put_contents(
    $logFile,
    date('Y-m-d H:i:s') . ' raw=' . $rawBody . PHP_EOL,
    FILE_APPEND | LOCK_EX
);

file_put_contents(
    $logFile,
    date('Y-m-d H:i:s') . ' post=' .
    json_encode($postData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) .
    PHP_EOL,
    FILE_APPEND | LOCK_EX
);

if (empty($postData)) {
    http_response_code(400);
    exit('failure');
}

// 这里必须先验签,再处理订单。
// 验签通过后,还应核对 app_id、seller_id、out_trade_no、total_amount 等字段。

$tradeStatus = $postData['trade_status'] ?? '';

if ($tradeStatus === 'TRADE_SUCCESS' || $tradeStatus === 'TRADE_FINISHED') {
    // 使用 out_trade_no 查询本地订单。
    // 校验金额和商户身份后,以幂等方式更新订单状态。
}

// 全部处理成功后必须直接返回 success。
echo 'success';

php://input 适合用来排查原始请求。正式处理业务时,仍应以验签后的参数为准。

具体验签方式需要和 alipay-sdk-php-all-3.0.20 的密钥配置保持一致。常见调用形式如下:

$verified = $aop->rsaCheckV1($_POST, null, 'RSA2');

不同的 SDK 包装方式和证书模式可能有所区别,需要以项目实际使用的 AopClient 方法签名及密钥配置为准。验签未通过时,不能更新订单状态。

还需要检查的事项

修正空格后,如果新订单仍收不到回调,可以按以下顺序检查:

  1. 重新创建一笔预下单交易

    已创建的交易使用的是创建时提交的 notify_url。修改代码后,需要重新调用 alipay.trade.precreate,并使用新二维码测试。

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

    notify.php 不能只在内网使用,也不能要求登录、Cookie、验证码或人工操作。

  3. 检查 HTTPS 配置

    确认证书有效、证书链完整、域名匹配,服务器也能正常完成 TLS 握手。

  4. 检查重定向和安全策略

    通知地址最好直接返回结果,不要依赖 HTTP 301 或 302 跳转。同时检查 WAF、CDN、防火墙以及 Nginx 或 Apache 规则,确认它们没有拦截支付宝发送的 POST 请求。

  5. 确认 PHP 有日志写入权限

    如果使用相对路径:

    file_put_contents('alipay_notify.log', ...);

    文件位置将取决于 PHP 进程的当前工作目录,该目录也可能没有写入权限。排查时可以改用:

    $logFile = __DIR__ . '/alipay_notify.log';

    同时检查 file_put_contents() 的返回值和 PHP 错误日志。没有生成日志,不一定说明请求从未到达服务器。

  6. 查看 Web 服务器访问日志

    Nginx 或 Apache 的访问日志通常比 PHP 文件日志更可靠。如果访问日志中完全没有对应的 POST 请求,应检查通知投递、域名解析、网络和 HTTPS 层。如果访问日志有请求,但 PHP 日志没有,则要检查路由、脚本权限和 PHP 运行错误。

  7. 正确返回 success

    验签和业务处理完成后,响应正文必须是纯文本:

    success
    

    不要附加 JSON、HTML、调试信息或 BOM。如果没有返回支付宝认可的成功响应,平台通常会按照通知机制继续重试。

与开放平台配置的关系

这些配置的用途并不相同:

  • 应用网关不是交易异步通知地址。
  • 授权回调地址用于 OAuth 等授权流程,不负责接收支付结果通知。
  • 服务器 IP 白名单主要限制应用调用接口时的来源 IP,不能代替 notify_url。
  • notify_url 决定当前交易的异步通知投递位置,必须在预下单请求中正确提交。

当前应先去掉 notify_url 前面的空格,然后重新预下单,再结合支付宝诊断记录、Web 服务器访问日志和 PHP 日志检查完整的通知链路。

备注:内容仅供参考。