本文目录导读:

在 PHP 中处理支付宝异步通知(Asynchronous Notification)的验签,是接入支付宝支付后最关键的环节,只有验签通过,才能确认该通知是支付宝官方发送的,防止伪造。
以下是使用官方 SDK 和原生代码两种最常用的验签实现方法。
核心概念
支付宝异步通知使用的是 RSA2(SHA256withRSA) 签名算法(推荐)或 RSA。 验签需要用到支付宝公钥(是支付宝平台上的公钥,不是应用私钥)。
流程梳理:
- 接收支付宝 POST 过来的
$_POST数据。 - 取出
sign和sign_type字段。 - 将剩余参数(去除
sign、sign_type,且空值不参与)按照字典序排序,拼接成字符串。 - 使用支付宝公钥,通过 RSA2 算法验证签名是否一致。
使用官方 SDK(推荐,最简单)
支付宝官方提供了 alipay-sdk-php,如果项目已通过 Composer 安装,直接调用即可。
安装(如果还没装):
composer require alipay/alipay-sdk-php
异步通知处理代码:
<?php
// 引入自动加载文件
require_once 'vendor/autoload.php';
// 引入相关类(根据 SDK 版本不同,命名空间可能略有差异)
use Alipay\EasySDK\Kernel\Factory;
use Alipay\EasySDK\Kernel\Util\ResponseChecker;
use Alipay\EasySDK\Kernel\Config;
// 1. 获取支付宝 POST 过来的原始数据
$data = $_POST;
// 2. 初始化配置(填入你的配置)
$options = new Config();
$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipay.com';
$options->signType = 'RSA2'; // 签名类型
$options->appId = '你的APPID';
$options->merchantPrivateKey = '你的应用私钥(原始字符串,不要PEM头)'; // 注意格式
// 关键:这里是支付宝公钥(不是应用公钥)
$options->alipayPublicKey = '你的支付宝公钥(原始字符串,不要PEM头)';
Factory::setOptions($options);
try {
// 3. 验签(SDK内部封装了排序和验签逻辑)
// 注意:SDK的 verify 方法需要传入未解码的原始数组
$result = Factory::payment()->common()->verifyNotify($data);
if ($result === true) { // 验签成功
// 打印日志记录
file_put_contents('notify_log.txt', json_encode($data) . PHP_EOL, FILE_APPEND);
// --- 业务处理 ---
// 1. 检查商户订单号 out_trade_no
// 2. 检查交易状态 trade_status 是否为 TRADE_SUCCESS
// 3. 检查金额 total_amount 是否与订单一致
// 4. 检查 AppID 是否匹配
// 幂等性处理:检查该订单是否已处理,避免重复发货
// ... 你的业务逻辑 ...
// 4. 验签成功且业务处理完成,必须原样输出 "success"(不要输出其他内容)
echo 'success';
} else {
// 验签失败
echo 'fail';
// 记录日志
}
} catch (\Exception $e) {
// 异常处理
file_put_contents('notify_error.log', $e->getMessage() . PHP_EOL, FILE_APPEND);
echo 'fail';
}
原生 PHP 手动验签(无 SDK 依赖)
如果你不想引入庞大的 SDK,或者需要底层逻辑,可以手动实现。
<?php
// 手动验签函数
function verifyAlipayNotify($data, $alipayPublicKey)
{
// 1. 剔除 sign 和 sign_type
$sign = $data['sign'] ?? '';
unset($data['sign']);
unset($data['sign_type']);
// 2. 去除空值(如果值为空字符串或 null,不参与签名)
$filteredData = array_filter($data, function ($value) {
return $value !== '' && $value !== null;
});
// 3. 按照字典序排序(根据键名 ASCII 码升序排列)
ksort($filteredData);
// 4. 拼接成字符串 key1=value1&key2=value2
$signStr = urldecode(http_build_query($filteredData));
// 5. 验证签名
// 注意:支付宝公钥可以是PEM格式,也可以是一行字符串,这里需要拼接成PEM
$publicKey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($alipayPublicKey, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
$res = openssl_get_publickey($publicKey);
if (!$res) {
return false; // 公钥格式错误
}
// 使用 OPENSSL_ALGO_SHA256 (RSA2)
$result = openssl_verify($signStr, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
// 释放资源
openssl_free_key($res);
return $result === 1; // 1 表示验证成功
}
// --- 使用示例 ---
$data = $_POST;
// 这里填入你的支付宝公钥(在支付宝开放平台获取)
$alipayPublicKey = '你的支付宝公钥字符串';
if (verifyAlipayNotify($data, $alipayPublicKey)) {
// 验签通过
// 处理业务逻辑...
// 注意:需要再次校验业务参数
// 1. app_id 是否是你自己的
// 2. out_trade_no 是否存在
// 3. total_amount 是否匹配
// 4. seller_id 是否匹配
echo 'success';
} else {
echo 'fail';
}
关键安全校验清单(业务层面)
验签只是第一步,为了防止海豚攻击(重放攻击)和逻辑漏洞,必须再检查以下内容:
app_id:必须等于你自己应用的 AppID。out_trade_no:必须是系统中存在的订单号。total_amount:必须与你数据库中该订单的金额完全一致(注意浮点数比较,建议使用字符串比较或bccomp)。seller_id:必须是你自己的支付宝账号对应的 ID。trade_status:只有当值为TRADE_SUCCESS时,才进行财务入账操作,其他状态(如WAIT_BUYER_PAY)忽略。
常见踩坑点(高频问题)
- 公钥填错了:在支付宝后台,你需要使用的是 “支付宝公钥”,不要填成“应用公钥”或“应用私钥”。
- 空值参与签名:
array_filter去掉空值是必须的,否则验签失败。 - 解码问题:如果使用
http_build_query,注意该函数默认会进行urlencode,但在手动拼接时需要确保没有转义特殊字符,推荐使用原生的拼接方式或urldecode包裹。 - 返回字符串:验签并处理成功后,必须输出一个独立的单词
success(不要带引号、空格或换行——有些服务器会自动加换行,通常没问题,但建议不要额外 echo 其他内容),如果输出其他内容,支付宝会认为处理失败,并在接下来的 24 小时内按一定频率重发通知(最多重发 8 次)。 - 日志记录:强烈建议将
$_POST的原始数据记录到日志文件,方便排查问题,但注意不要记录sign字段泄露签名信息(或者可以记录,问题不大,但出于安全可打码)。
推荐使用官方 SDK 进行验签,因为官方 SDK 处理了:openssl 库的兼容性、公钥格式转换、字符编码等问题,手动验签虽然在上述代码中能跑通,但遇到环境差异(如 PHP 7.x 和 8.x 的 openssl_verify 行为差异,或者中文参数编码问题)时容易踩坑。
最终操作步骤:
- 拿到
$_POST数据。 - 调用 SDK 的
verifyNotify(或手动验签)。 true,再检查订单金额/状态/AppID。- 处理完业务,输出
success。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播