checkSignature(); } // 2. 处理微信事件推送(POST请求) if ($_SERVER['REQUEST_METHOD'] === 'POST') { return $this->handleEvent($EmployeeOpenidModel); } return 'success'; } /** * 处理微信事件 */ private function handleEvent($EmployeeOpenidModel) { // 获取微信推送的原始数据 $xmlData = file_get_contents('php://input'); $xml = simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA); if (!$xml) { return 'success'; } $xmlData = $this->xmlToArray($xmlData); $response_data_xml = $this->decryptMsg($xmlData['Encrypt']); $response_data = $this->xmlToArray($response_data_xml); Log::info('wechat_subscribe_info', '微信公众号事件,解密内容', ['xmlData' => $xmlData, 'response_data' => $response_data]); // 提取关键信息 $fromUsername = (string)$response_data['FromUserName']; // 用户的公众号OpenID $toUsername = (string)$response_data['ToUserName']; // 公众号原始ID $event = (string)$response_data['Event']; // 事件类型 // 处理关注事件 if ($event == 'subscribe') { try { // 尝试获取用户UnionID $Official = new Official(); $official_user_info = $Official->getApp()->user->get($fromUsername); Log::info('wechat_subscribe_info', '微信公众号事件,获取用户信息', ['FromUserName' => $fromUsername, 'response_data' => $official_user_info]); $unionid = isset($official_user_info['unionid']) ? $official_user_info['unionid'] : ''; if ($unionid) { // 1. 有UnionID,直接绑定公众号OpenID $user_open_data = $EmployeeOpenidModel->where(['unionid' => $unionid])->first(); if ($user_open_data) { $user_open_data->official_openid = $fromUsername; $user_open_data->save(); } else { //新增记录 $insert_data = [ 'unionid' => $unionid, 'official_openid' => $fromUsername, 'insert_time' => time(), 'type' => 1 ]; $EmployeeOpenidModel->insertGetId($insert_data); } } else { Log::info('wechat_subscribe_error', '获取UnionID失败', [ 'data' => $fromUsername, 'request_data' => $official_user_info ]); } } catch (\Exception $e) { Log::error('wechat_subscribe_exception', '处理异常', [ 'error' => $e->getMessage() ]); } } // 重要:必须返回success return 'success'; } /** * 解密消息 */ private function decryptMsg($encrypt) { try { // Base64解码 $encrypted = base64_decode($encrypt); $encodingAesKey = config('wechat.openplat.aes_key', ''); $appId = config('wechat.openplat.app_id', ''); // 解密 $key = base64_decode($encodingAesKey . '='); $iv = substr($key, 0, 16); $decrypted = openssl_decrypt( $encrypted, 'AES-256-CBC', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv ); // 去除填充的字符 $pad = ord(substr($decrypted, -1)); $decrypted = substr($decrypted, 0, -$pad); // 解析内容 $content = substr($decrypted, 16); // 去掉16个随机字节 $xmlLen = unpack('N', substr($content, 0, 4))[1]; // 验证AppId $appId = substr($content, 4 + $xmlLen); if ($appId != $appId) { Log::error('wechat_appid_error', 'AppId不匹配', ['got' => $appId, 'expect' => $appId]); return false; } // 返回XML内容 return substr($content, 4, $xmlLen); } catch (\Exception $e) { Log::error('wechat_decrypt_error', '解密异常', ['error' => $e->getMessage()]); return false; } } /** * 验证服务器地址有效性 */ private function checkSignature() { $signature = $_GET["signature"] ?? ''; $timestamp = $_GET["timestamp"] ?? ''; $nonce = $_GET["nonce"] ?? ''; $echostr = $_GET["echostr"] ?? ''; if (!$signature || !$timestamp || !$nonce) { return 'Invalid request'; } $token = config('wechat.openplat.token', 'your_token_here'); $tmpArr = [$token, $timestamp, $nonce]; sort($tmpArr, SORT_STRING); $tmpStr = sha1(implode($tmpArr)); if ($tmpStr == $signature) { return $echostr; // 验证成功,返回echostr } else { return 'Invalid signature'; } } /** * XML转数组 * @param string $xml XML字符串 * @return array */ private function xmlToArray($xml) { // 禁止引用外部xml实体 libxml_disable_entity_loader(true); // 加载XML字符串 $xmlObject = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); if (!$xmlObject) { return []; } // 将SimpleXMLElement对象转换为JSON,再转换为数组 $json = json_encode($xmlObject); $array = json_decode($json, true); return $array ?: []; } /** * 公众号测试消息订阅推送 * @author 唐远望 * @version 1.0 * @date 2026-03-10 */ public function test() { $message_data = ['notice_type'=>'low_price_goods']; $response_data = SubscriptionJobs::dispatchSync($message_data); return json_send(['code' => 'success', 'msg' => '执行成功', 'data' => $response_data]); } }