本文目录导读:

在PHP项目中处理微信支付回调,核心流程如下:
接收回调数据
// 获取微信服务器POST的原始数据
$xmlData = file_get_contents('php://input');
// 将XML转换为数组
$data = simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA);
$data = json_decode(json_encode($data), true);
验证签名
function verifySign($data, $key) {
// 1. 过滤空值并排序
ksort($data);
// 2. 拼接字符串
$string = '';
foreach ($data as $k => $v) {
if ($k != 'sign' && $v != '') {
$string .= $k . '=' . $v . '&';
}
}
// 3. 拼接商户密钥
$string = $string . 'key=' . $key;
// 4. MD5加密并转大写
$sign = strtoupper(md5($string));
// 5. 对比签名
return $sign === $data['sign'];
}
完整的回调处理代码
<?php
// notify.php
// 微信支付回调处理
class WechatPayNotify {
private $appid = '你的APPID';
private $mchid = '你的商户号';
private $key = '你的商户密钥';
public function handle() {
try {
// 1. 获取回调数据
$xmlData = file_get_contents('php://input');
Log::write('微信回调数据:' . $xmlData);
// 2. 解析XML
$data = $this->xmlToArray($xmlData);
// 3. 验证签名
if (!$this->verifySign($data)) {
throw new \Exception('签名验证失败');
}
// 4. 验证业务结果
if ($data['return_code'] != 'SUCCESS' || $data['result_code'] != 'SUCCESS') {
throw new \Exception('支付失败:' . $data['err_code_des']);
}
// 5. 处理订单
$orderNo = $data['out_trade_no'];
$transactionId = $data['transaction_id'];
$totalFee = $data['total_fee']; // 单位:分
// 查询订单状态,避免重复处理
if ($this->isOrderProcessed($transactionId)) {
return $this->responseSuccess();
}
// 执行订单更新逻辑
$this->updateOrderStatus($orderNo, $transactionId, $totalFee);
// 6. 返回成功响应
return $this->responseSuccess();
} catch (\Exception $e) {
Log::error('回调处理失败:' . $e->getMessage());
return $this->responseFail();
}
}
// XML转数组
private function xmlToArray($xml) {
$result = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
return json_decode(json_encode($result), true);
}
// 验证签名
private function verifySign($data) {
if (!isset($data['sign'])) {
return false;
}
// 过滤空值并排序
$signData = array_filter($data, function($value) {
return $value !== '';
});
unset($signData['sign']);
ksort($signData);
// 拼接字符串
$string = '';
foreach ($signData as $k => $v) {
$string .= $k . '=' . $v . '&';
}
$string .= 'key=' . $this->key;
// 生成签名
$sign = strtoupper(md5($string));
return $sign === $data['sign'];
}
// 检查订单是否已处理(幂等性处理)
private function isOrderProcessed($transactionId) {
// 查询数据库是否已存在该交易记录
// return Db::table('orders')->where('transaction_id', $transactionId)->find();
return false;
}
// 更新订单状态
private function updateOrderStatus($orderNo, $transactionId, $totalFee) {
// 1. 开启事务
Db::beginTransaction();
try {
// 2. 更新订单状态
Db::table('orders')
->where('order_no', $orderNo)
->update([
'status' => 1, // 已支付
'transaction_id' => $transactionId,
'pay_time' => date('Y-m-d H:i:s'),
'total_fee' => $totalFee
]);
// 3. 记录支付日志
Db::table('pay_logs')->insert([
'order_no' => $orderNo,
'transaction_id' => $transactionId,
'total_fee' => $totalFee,
'create_time' => date('Y-m-d H:i:s')
]);
// 4. 提交事务
Db::commit();
// 5. 其他业务处理(发送通知等)
// $this->afterPay($orderNo);
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
// 返回成功响应
private function responseSuccess() {
$result = '<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>';
echo $result;
exit;
}
// 返回失败响应
private function responseFail() {
$result = '<xml>
<return_code><![CDATA[FAIL]]></return_code>
<return_msg><![CDATA[处理失败]]></return_msg>
</xml>';
echo $result;
exit;
}
}
// 使用示例
$notify = new WechatPayNotify();
$notify->handle();
?>
关键注意事项
1 幂等性处理
- 使用 transaction_id 作为唯一标识,防止重复处理
- 在更新订单前检查是否已处理
2 日志记录
// 记录所有回调数据
file_put_contents('/tmp/wechat_notify.log', date('Y-m-d H:i:s') . " " . $xmlData . "\n", FILE_APPEND);
3 返回格式
微信要求返回XML格式:
<!-- 成功 --> <xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> </xml> <!-- 失败 --> <xml> <return_code><![CDATA[FAIL]]></return_code> <return_msg><![CDATA[参数格式校验错误]]></return_msg> </xml>
4 安全性检查
// 1. 验证商户号
if ($data['mch_id'] != $this->mchid) {
throw new \Exception('商户号不匹配');
}
// 2. 验证APPID
if ($data['appid'] != $this->appid) {
throw new \Exception('APPID不匹配');
}
// 3. 验证金额(防止篡改)
$order = Db::table('orders')->where('order_no', $data['out_trade_no'])->find();
if ($order && $order['total_fee'] != $data['total_fee']) {
throw new \Exception('金额不匹配');
}
使用框架(ThinkPHP/Laravel)简化
// ThinkPHP 6 示例
use think\facade\Log;
use think\facade\Db;
class Notify
{
public function handle()
{
$xmlData = file_get_contents('php://input');
$data = json_decode(json_encode(simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
Log::info('微信回调', $data);
// 验证签名...
// 处理订单...
return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}
}
测试建议
- 使用微信沙箱环境测试
- 设置1元测试支付
- 在回调URL后添加临时参数方便调试
- 使用ngrok等工具让外网访问本地服务
建议使用成熟的微信支付SDK(如wechatpay/wechatpay)简化开发流程。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播