PHP项目支付回调与验签完整指南
基础架构设计
<?php
// 支付回调处理类
class PaymentCallbackHandler {
private $config;
public function __construct($config) {
$this->config = $config;
}
/**
* 统一入口处理回调
*/
public function handle($gateway) {
try {
// 1. 获取原始数据
$rawData = $this->getRawData();
// 2. 格式转换
$data = $this->parseData($rawData);
// 3. 验签
if (!$this->verifySignature($data)) {
throw new \Exception('签名验证失败');
}
// 4. 检查订单状态
$order = $this->checkOrder($data);
// 5. 处理业务逻辑
return $this->processOrder($order, $data);
} catch (\Exception $e) {
$this->logError($e->getMessage());
return $this->failureResponse();
}
}
/**
* 获取原始请求数据
*/
private function getRawData() {
return file_get_contents('php://input');
}
/**
* 解析不同格式的数据
*/
private function parseData($rawData) {
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (strpos($contentType, 'application/json') !== false) {
return json_decode($rawData, true);
} elseif (strpos($contentType, 'application/xml') !== false) {
return $this->xmlToArray($rawData);
} else {
// 表单格式
parse_str($rawData, $data);
return $data;
}
}
/**
* XML转数组
*/
private function xmlToArray($xml) {
$xmlObj = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
return json_decode(json_encode($xmlObj), true);
}
/**
* 记录日志
*/
private function logError($message) {
error_log(date('Y-m-d H:i:s') . ' - 回调错误: ' . $message . PHP_EOL, 3,
__DIR__ . '/logs/payment_callback.log');
}
/**
* 成功响应
*/
protected function successResponse() {
return json_encode(['code' => 'SUCCESS', 'message' => '处理成功']);
}
/**
* 失败响应
*/
protected function failureResponse($message = 'FAIL') {
return json_encode(['code' => 'FAIL', 'message' => $message]);
}
}
?>
各大支付平台验签实现
1 微信支付验签
<?php
class WechatPayHandler extends PaymentCallbackHandler {
/**
* 微信支付验签
*/
protected function verifySignature($data) {
$sign = $data['sign'] ?? '';
unset($data['sign']);
// 按字典序排序
ksort($data);
// 生成签名串
$stringA = '';
foreach ($data as $key => $value) {
if ($value !== '' && !is_null($value) && $key !== 'sign') {
$stringA .= $key . '=' . $value . '&';
}
}
$stringSignTemp = $stringA . 'key=' . $this->config['api_key'];
$signature = strtoupper(md5($stringSignTemp));
return $signature === $sign;
}
/**
* 处理微信回调逻辑
*/
protected function processOrder($order, $data) {
// 微信回调业务逻辑
return $this->successResponse();
}
}
?>
2 支付宝验签
<?php
class AlipayHandler extends PaymentCallbackHandler {
/**
* 支付宝RSA2验签
*/
protected function verifySignature($data) {
$sign = $data['sign'];
unset($data['sign']);
unset($data['sign_type']);
// 生成待签名字符串
$content = $this->buildSignContent($data);
// 验证签名
$publicKey = openssl_pkey_get_public($this->config['public_key']);
$result = openssl_verify(
$content,
base64_decode($sign),
$publicKey,
OPENSSL_ALGO_SHA256
);
return $result === 1;
}
/**
* 构造签名字符串
*/
private function buildSignContent($data) {
ksort($data);
$content = '';
foreach ($data as $key => $value) {
if ($value !== '' && !is_null($value) && $value !== 'null') {
$content .= $key . '=' . $value . '&';
}
}
return rtrim($content, '&');
}
}
?>
3 Stripe Webhook验签
<?php
class StripeHandler extends PaymentCallbackHandler {
/**
* Stripe验签
*/
public function handle($gateway = 'stripe') {
$payload = $this->getRawData();
$sigHeader = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '';
try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$sigHeader,
$this->config['webhook_secret']
);
return $this->processStripeEvent($event);
} catch (\UnexpectedValueException $e) {
// 无效的payload
return $this->failureResponse('Invalid payload');
} catch (\Stripe\Exception\SignatureVerificationException $e) {
// 签名验证失败
return $this->failureResponse('Invalid signature');
}
}
private function processStripeEvent($event) {
// 处理不同类型的Stripe事件
switch ($event->type) {
case 'payment_intent.succeeded':
// 支付成功
return $this->successResponse();
case 'payment_intent.payment_failed':
// 支付失败
return $this->failureResponse('Payment failed');
default:
return $this->successResponse();
}
}
}
?>
完整的业务实现示例
<?php
// 支付回调控制器
class PaymentController {
/**
* 处理支付回调
*/
public function callback($gateway, $orderNo) {
// 验证请求IP(可选)
$this->checkIP();
// 初始化回调处理
$handler = $this->createHandler($gateway);
$result = $handler->handle();
// 设置响应格式
header('Content-Type: application/json');
echo $result;
}
/**
* 创建处理器
*/
private function createHandler($gateway) {
switch ($gateway) {
case 'wechat':
return new WechatPayHandler($this->getConfig('wechat'));
case 'alipay':
return new AlipayHandler($this->getConfig('alipay'));
case 'stripe':
return new StripeHandler($this->getConfig('stripe'));
default:
throw new \Exception("Unsupported gateway: {$gateway}");
}
}
/**
* 检查IP有效性
*/
private function checkIP() {
// 实现IP白名单验证
}
/**
* 获取配置
*/
private function getConfig($gateway) {
// 从配置文件获取支付网关配置
return require APP_PATH . "/config/{$gateway}.php";
}
}
?>
安全最佳实践
<?php
class PaymentSecurity {
/**
* 回调处理安全防护
*/
public static function secureCallback() {
// 1. HTTPS强制
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
throw new \Exception('必须使用HTTPS');
}
// 2. 时间限制(防止重放攻击)
self::checkTimestamp();
// 3. 订单幂等性处理
self::checkIdempotent();
// 4. 日志记录
self::logCallback();
}
/**
* 检查时间戳
*/
private static function checkTimestamp() {
$timestamp = $_POST['timestamp'] ?? time();
if (abs(time() - $timestamp) > 300) { // 5分钟有效
throw new \Exception('请求过期');
}
}
/**
* 幂等性检查
*/
private static function checkIdempotent() {
// 使用Redis或数据库做锁
$lockKey = "payment_callback:" . $_POST['order_id'];
if (!RedisCache::getLock($lockKey, 60)) {
throw new \Exception('重复请求');
}
}
/**
* 记录回调日志
*/
private static function logCallback() {
$logData = [
'ip' => $_SERVER['REMOTE_ADDR'],
'data' => $_POST,
'time' => date('Y-m-d H:i:s')
];
// 写入日志
file_put_contents(
LOG_PATH . '/callback_' . date('Ymd') . '.log',
json_encode($logData) . PHP_EOL,
FILE_APPEND
);
}
}
?>
响应格式示例
<?php
// 成功响应
$successResponse = [
'code' => 'SUCCESS',
'message' => '处理成功',
'timestamp' => time()
];
// 异步处理响应
$asyncResponse = [
'code' => 'PENDING',
'message' => '异步处理中'
];
// 失败响应
$failResponse = [
'code' => 'FAIL',
'message' => '处理失败',
'error_code' => 'INVALID_SIGN'
];
?>
测试用例
<?php
class PaymentCallbackTest extends PHPUnit\Framework\TestCase {
/**
* 测试微信验签
*/
public function testWechatSignature() {
$config = [
'api_key' => 'your_api_key'
];
$handler = new WechatPayHandler($config);
// 构造测试数据
$data = [
'appid' => 'wx123',
'mch_id' => '123456',
'out_trade_no' => 'ORDER20240001',
'result_code' => 'SUCCESS'
];
// 计算签名
ksort($data);
$stringA = '';
foreach ($data as $key => $value) {
$stringA .= $key . '=' . $value . '&';
}
$data['sign'] = strtoupper(md5($stringA . 'key=' . $config['api_key']));
// 验证
$this->assertTrue($handler->verifySignature($data));
}
}
?>
这些示例涵盖了PHP支付回调的核心实现,包括:

- 基础架构 - 统一处理入口和异常处理
- 各大支付平台 - 微信、支付宝、Stripe的具体实现
- 安全防护 - IP验证、重放攻击防护、幂等性
- 最佳实践 - 日志记录、错误处理、测试
根据实际业务需求,你可能需要调整具体的配置和业务逻辑,记得在生产环境中使用正式密钥并做好安全防护。