支付宝订单码支付成功后未收到异步通知回调
结论
问题很可能是回调地址开头多了一个空格:
$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 方法签名及密钥配置为准。验签未通过时,不能更新订单状态。
还需要检查的事项
修正空格后,如果新订单仍收不到回调,可以按以下顺序检查:
-
重新创建一笔预下单交易
已创建的交易使用的是创建时提交的
notify_url。修改代码后,需要重新调用alipay.trade.precreate,并使用新二维码测试。 -
确认地址可以从公网访问
notify.php不能只在内网使用,也不能要求登录、Cookie、验证码或人工操作。 -
检查 HTTPS 配置
确认证书有效、证书链完整、域名匹配,服务器也能正常完成 TLS 握手。
-
检查重定向和安全策略
通知地址最好直接返回结果,不要依赖 HTTP 301 或 302 跳转。同时检查 WAF、CDN、防火墙以及 Nginx 或 Apache 规则,确认它们没有拦截支付宝发送的 POST 请求。
-
确认 PHP 有日志写入权限
如果使用相对路径:
file_put_contents('alipay_notify.log', ...);文件位置将取决于 PHP 进程的当前工作目录,该目录也可能没有写入权限。排查时可以改用:
$logFile = __DIR__ . '/alipay_notify.log';同时检查
file_put_contents()的返回值和 PHP 错误日志。没有生成日志,不一定说明请求从未到达服务器。 -
查看 Web 服务器访问日志
Nginx 或 Apache 的访问日志通常比 PHP 文件日志更可靠。如果访问日志中完全没有对应的 POST 请求,应检查通知投递、域名解析、网络和 HTTPS 层。如果访问日志有请求,但 PHP 日志没有,则要检查路由、脚本权限和 PHP 运行错误。
-
正确返回
success验签和业务处理完成后,响应正文必须是纯文本:
success不要附加 JSON、HTML、调试信息或 BOM。如果没有返回支付宝认可的成功响应,平台通常会按照通知机制继续重试。
与开放平台配置的关系
这些配置的用途并不相同:
- 应用网关不是交易异步通知地址。
- 授权回调地址用于 OAuth 等授权流程,不负责接收支付结果通知。
- 服务器 IP 白名单主要限制应用调用接口时的来源 IP,不能代替
notify_url。 notify_url决定当前交易的异步通知投递位置,必须在预下单请求中正确提交。
当前应先去掉 notify_url 前面的空格,然后重新预下单,再结合支付宝诊断记录、Web 服务器访问日志和 PHP 日志检查完整的通知链路。
备注:内容仅供参考。
