本文目录导读:

在PHP项目中处理支付回调验签,核心目的是验证收到的回调通知确实来自支付平台(如微信、支付宝),且数据未被篡改。
以下是通用且标准的验签步骤,并以支付宝和微信支付为例说明具体实现。
通用验签核心逻辑
无论哪种支付平台,验签的核心流程都是:
- 接收数据:获取支付平台POST过来的参数(通常是
$_POST或原始输入流php://input)。 - 去除签名本身:从参数中取出签名值(通常是
sign或sign_type字段),剩下的参数用于验签。 - 排序并拼接字符串:将剩余参数按照一定的规则(通常是ASCII码升序或指定顺序)拼接成待签名字符串。
- 获取平台公钥:使用支付平台提供的公钥(不是商户私钥)。
- 验证签名:使用对应的加密算法(如RSA2、MD5)对拼接好的字符串和签名值进行比对验证。
具体实现案例
案例1:支付宝回调验签(RSA2加密)
支付宝的回调数据通常通过 $_POST 直接传入。
<?php
/**
* 支付宝异步通知验签
*/
// 1. 获取支付宝回调的所有参数
$params = $_POST;
// 2. 获取签名和签名类型,并从参数中移除它们
$sign = $params['sign'] ?? '';
$signType = $params['sign_type'] ?? '';
unset($params['sign'], $params['sign_type']);
// 3. 对待验签参数进行排序并拼接
// 注意:支付宝要求按照字段名的ASCII码从小到大排序
ksort($params);
$stringToSign = '';
foreach ($params as $key => $value) {
// 注意:需要过滤掉空值、数组等(支付宝通常传递字符串)
if ($value !== '' && !is_array($value)) {
$stringToSign .= $key . '=' . $value . '&';
}
}
// 去掉最后一个多余的 & 符号
$stringToSign = rtrim($stringToSign, '&');
// 4. 获取支付宝公钥(从配置文件或数据库中读取)
$alipayPublicKey = file_get_contents('/path/to/alipay_public_key.pem'); // 或直接定义字符串
// 5. 使用 openssl 验证签名
// 支付宝签名算法通常是 RSA2(即 SHA256withRSA)
$result = openssl_verify(
$stringToSign,
base64_decode($sign),
$alipayPublicKey,
OPENSSL_ALGO_SHA256 // RSA2 对应这个常量,如果是 RSA 则用 OPENSSL_ALGO_SHA1
);
if ($result) {
// 验签成功!更新订单状态(注意检查金额、订单号、app_id 等)
echo 'success'; // 必须返回 'success',否则支付宝会重复通知
} else {
// 验签失败,记录日志,返回失败信息
echo 'fail';
}
?>
案例2:微信支付回调验签(HMAC-SHA256 或 MD5)
微信支付的回调数据是 XML 格式,通过 php://input 获取,签名结果需要在本地重新计算后比对。
<?php
/**
* 微信支付异步通知验签
*/
// 1. 获取原始输入流(XML格式)
$inputXml = file_get_contents('php://input');
$data = simplexml_load_string($inputXml, 'SimpleXMLElement', LIBXML_NOCDATA);
$params = json_decode(json_encode($data), true);
// 2. 获取签名并移除
$sign = $params['sign'] ?? '';
unset($params['sign']);
// 3. 对待签名字符串进行排序并拼接
ksort($params);
$stringToSign = '';
foreach ($params as $key => $value) {
// 微信要求:参数值为空时不参与签名
if ($value !== '' && $key !== 'sign') {
$stringToSign .= $key . '=' . $value . '&';
}
}
// 拼接商户 API 密钥(注意:是API密钥,不是商户证书)
$apiKey = 'your_api_key_here'; // 在微信商户平台设置的32位密钥
$stringToSign .= 'key=' . $apiKey;
// 4. 根据签名类型生成签名
$signType = $params['sign_type'] ?? 'MD5';
if ($signType === 'HMAC-SHA256') {
$localSign = strtoupper(hash_hmac('sha256', $stringToSign, $apiKey));
} else {
// 默认 MD5
$localSign = strtoupper(md5($stringToSign));
}
// 5. 比对签名
if ($localSign === $sign) {
// 验签成功!处理业务逻辑(更新订单状态等)
// 注意:还需要检查返回的 result_code 和 return_code 是否为 SUCCESS
echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
} else {
// 验签失败
echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
}
?>
关键注意事项(易错点)
-
参数过滤:
- 支付宝:需要过滤掉
sign和sign_type,且拼接时过滤掉空值。 - 微信:需要过滤掉
sign,空值不参与签名。
- 支付宝:需要过滤掉
-
编码问题:
拼接前请确保所有字段是 UTF-8 编码,避免中文或其他字符导致签名不一致。
-
微信 API 证书 vs 商户密钥:
- 微信验签用的是商户API密钥(32位字符串),不是商户证书。
- 微信退款、转账等操作才需要用到证书(
apiclient_cert.pem)。
-
重复通知处理:
- 支付平台可能会多次回调,必须确保业务处理逻辑是幂等的(例如通过订单号+状态判断是否已处理)。
-
金额比对:
- 验签通过后,一定还要比对
total_fee(订单金额)和out_trade_no(商户订单号)是否和数据库一致,防止恶意伪造订单。
- 验签通过后,一定还要比对
-
响应格式:
- 支付宝:成功输出
success(无空格),失败输出fail。 - 微信:成功输出
<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>。
- 支付宝:成功输出
何时使用 SDK?
对于生产环境,推荐直接使用官方 SDK(如 Alipay SDK for PHP 和 WeChatPay SDK),它们会自动处理以下细节:
- 签名算法选择(RSA1 vs RSA2、MD5 vs SHA256)
- 参数排序规则(不同版本可能有变化)
- Base64 编码/解码处理
// 支付宝 SDK 示例 use Alipay\EasySDK\Kernel\Factory; Factory::setOptions($config); $response = Factory::payment()->common()->verifyNotify($_POST); // 直接返回bool
但理解底层原理(即上述通用步骤)对于排查坑点、自定义处理非常有用。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播