本文目录导读:

- 数据准备(原始报文)
- 参数排序与拼接(核心环节)
- 平台公钥验证(关键安全点)
- 响应支付平台(业务逻辑)
- 完整示例(整合微信v2 + 支付成功逻辑)
- 关键注意事项(安全防护)
- 各平台差异速查表
- 推荐工具库(提高安全性)
在PHP中实现支付回调验签,主要涉及数据接收、签名验证和幂等性处理三个核心步骤,以下是通用且安全的实现流程(以微信支付/支付宝为例,原理相同):
数据准备(原始报文)
支付平台会以 POST 方式发送回调数据,你需要获取原始的请求体(注意:不是 $_POST,而是 php://input)。
// 获取原始请求体(字符串)
$rawContent = file_get_contents('php://input');
// 如果是JSON格式(微信支付v3、支付宝新接口)
$data = json_decode($rawContent, true);
// 如果是表单格式(传统支付宝/旧微信支付)
// $data = $_POST;
参数排序与拼接(核心环节)
无论哪种支付平台,验签的核心逻辑都是: 将收到的参数(除 sign 字段外)按照规则排序,拼接成字符串,然后使用平台公钥进行签名验证。
微信支付(v2 普通商户版)
// 1. 过滤掉空值和 sign 字段 $params = $data; unset($params['sign']); // 2. 按字段名 ASCII 码升序排序 ksort($params); // 3. 拼接成 key=value&key2=value2 格式(URL编码) $signStr = urldecode(http_build_query($params)); // 4. 拼接商户key(v2是使用MD5或HMAC-SHA256) $signStr .= '&key=' . $merchantKey; // 商户平台设置的API密钥 // 5. 生成签名(MD5大写) $calculatedSign = strtoupper(md5($signStr));
微信支付(v3 新版,推荐)
// 1. 获取请求头中的签名信息
$timestamp = $_SERVER['HTTP_WECHATPAY_TIMESTAMP'];
$nonce = $_SERVER['HTTP_WECHATPAY_NONCE'];
$signature = $_SERVER['HTTP_WECHATPAY_SIGNATURE'];
$serialNo = $_SERVER['HTTP_WECHATPAY_SERIAL'];
// 2. 构造验签串
$message = $timestamp . "\n" . $nonce . "\n" . $rawContent . "\n";
// 3. 使用微信支付平台公钥(证书中获取)进行RSA验签
$publicKey = openssl_pkey_get_public(file_get_contents('wechatpay_public.pem'));
$result = openssl_verify(
$message,
base64_decode($signature),
$publicKey,
OPENSSL_ALGO_SHA256
);
平台公钥验证(关键安全点)
使用支付平台提供的公钥证书(不是商户私钥!)进行 RSA 验证。
// 支付宝验签示例(RSA2)
$alipayPublicKey = file_get_contents('alipay_public_key.pem');
$res = openssl_verify(
$signStr, // 待验证的签名原文
base64_decode($data['sign']), // 对方传来的签名(base64解码)
$alipayPublicKey,
OPENSSL_ALGO_SHA256
);
if ($res === 1) {
// 验签成功
} elseif ($res === 0) {
// 验签失败
} else {
// 证书错误
}
⚠️ 重要安全建议:
- 使用平台公钥,不要使用商户自己的私钥验签。
- 公钥从支付平台官方下载,不要通过回调数据中传输的证书获取(防止中间人攻击)。
- 建议使用 cURL 或 Guzzle 等 HTTP 客户端,不要直接用
file_get_contents(存在 SSL 漏洞风险)。
响应支付平台(业务逻辑)
验签成功后,需要处理业务逻辑(更新订单状态、减库存等),然后回复支付平台成功,避免重复回调。
// 验签 + 业务处理成功后
http_response_code(200);
echo '{"code":"SUCCESS","message":"成功"}'; // 微信v3格式
// echo 'success'; // 支付宝/微信v2格式
// 业务失败时
http_response_code(500);
echo '{"code":"FAIL","message":"处理失败"}';
完整示例(整合微信v2 + 支付成功逻辑)
public function handleWechatCallback()
{
$rawContent = file_get_contents('php://input');
$data = json_decode($rawContent, true);
// 1. 验签
$params = $data;
unset($params['sign']);
ksort($params);
$signStr = urldecode(http_build_query($params));
$signStr .= '&key=' . $this->merchantKey;
$calculatedSign = strtoupper(md5($signStr));
if ($calculatedSign !== $data['sign']) {
http_response_code(400);
return '验签失败';
}
// 2. 幂等性检查(防止重复通知)
if (Order::where('order_no', $data['out_trade_no'])->where('status', 'paid')->exists()) {
return 'success'; // 已处理过,直接返回成功
}
// 3. 业务处理(事务)
DB::transaction(function() use ($data) {
// 更新订单状态
// 减库存等
});
// 4. 返回成功给微信
return 'success';
}
关键注意事项(安全防护)
- SSL 校验:回调请求应使用 HTTPS,并对证书进行校验。
- 幂等性:必须保证同一订单只处理一次(使用数据库唯一约束或 Redis 锁)。
- 时间戳校验:检查回调时间与服务器时间差是否在合理范围(如 5 分钟),防止重放攻击。
- 金额校验:将回调金额与订单金额比对(注意分/元单位换算)。
- 日志记录:记录原始请求和验签结果,便于排查问题。
- 超时限制:业务处理应尽快完成(建议 < 5 秒),长时间处理可先返回成功,异步处理。
各平台差异速查表
| 平台 | 签名算法 | 签名参数 | 拼接格式 | 公钥来源 |
|---|---|---|---|---|
| 微信支付v2 | MD5/HMAC-SHA256 | 所有非空参数(除 sign) | a=1&b=2&key=商户key |
自己生成商户key |
| 微信支付v3 | RSA-SHA256 | 时间戳+随机串+请求体 | 时间戳\n随机串\n请求体\n |
微信支付平台证书 |
| 支付宝 | RSA2 | 所有非空参数(除 sign) | a=1&b=2 |
支付宝公钥 |
| 支付宝海外 | RSA | 同上 | 同上 | 支付宝公钥 |
推荐工具库(提高安全性)
use WechatPay\GuzzleMiddleware\WechatPayMiddleware; // 微信官方SDK // 或使用 EasyWeChat:https://easywechat.com/
使用官方 SDK 可以避免很多底层细节错误,但理解验签原理仍然是排查问题的关键技能。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播