date('Y-m-d H:i:s'), 'status' => 'ok', 'server' => [ 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'] ?? '', 'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? '', 'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? '', 'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'] ?? '', ] ]; // 记录详细日志 Log::info('接口测试 - 请求信息:' . json_encode($data, JSON_UNESCAPED_UNICODE)); return json($data, JSON_UNESCAPED_UNICODE); } catch (\Exception $e) { $error = [ 'error' => $e->getMessage(), 'time' => date('Y-m-d H:i:s') ]; Log::error('接口测试错误:' . json_encode($error, JSON_UNESCAPED_UNICODE)); return json($error, JSON_UNESCAPED_UNICODE); } } public function index() { try { // 设置响应头 header('Content-Type: text/html; charset=utf-8'); // 记录原始请求信息 $requestInfo = [ 'time' => date('Y-m-d H:i:s'), 'method' => Request::method(), 'url' => Request::url(true), 'ip' => Request::ip(), 'params' => Request::param(), 'headers' => getallheaders(), 'server' => [ 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'] ?? '', 'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? '', 'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? '', 'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'] ?? '', 'QUERY_STRING' => $_SERVER['QUERY_STRING'] ?? '', 'HTTP_USER_AGENT' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'HTTP_REFERER' => $_SERVER['HTTP_REFERER'] ?? '', ] ]; Log::info('微信接口访问请求信息:' . json_encode($requestInfo, JSON_UNESCAPED_UNICODE)); // 获取原始数据 $rawData = file_get_contents("php://input"); if (!empty($rawData)) { $rawData = mb_convert_encoding($rawData, 'UTF-8', 'UTF-8,GBK,GB2312'); Log::info('微信接口原始数据:' . $rawData); } // 检查请求方法 if (Request::method() == 'GET') { Log::info('微信接口:GET请求,进行签名验证'); // 首次验证服务器地址的有效性 $this->checkSignature(); } elseif (Request::method() == 'POST') { Log::info('微信接口:POST请求,处理消息'); // 接收消息并回复 $response = $this->receiveMessage(); // 直接输出回复的XML字符串 echo $response; exit; } else { Log::error('微信接口:不支持的请求方法 - ' . Request::method()); echo ''; exit; } } catch (\Exception $e) { Log::error('微信接口错误:' . $e->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); // 返回空字符串,避免微信服务器重试 echo ''; exit; } } // 验证签名 protected function checkSignature() { try { $signature = Request::get('signature'); $timestamp = Request::get('timestamp'); $nonce = Request::get('nonce'); $echostr = Request::get('echostr'); $debugInfo = [ 'signature' => $signature, 'timestamp' => $timestamp, 'nonce' => $nonce, 'echostr' => $echostr, 'url' => Request::url(true), 'time' => date('Y-m-d H:i:s') ]; Log::info('微信验证参数:', $debugInfo); if (empty($signature) || empty($timestamp) || empty($nonce)) { Log::error('微信验证参数缺失', $debugInfo); exit; } $token = AdminConfig::where('config_name', 'wechat_token')->value('config_value'); if (empty($token)) { Log::error('微信token未配置'); exit; } Log::info('微信token:' . $token); $tmpArr = array($token, $timestamp, $nonce); sort($tmpArr, SORT_STRING); $tmpStr = implode($tmpArr); $tmpStr = sha1($tmpStr); $verifyInfo = [ 'tmpStr' => $tmpStr, 'signature' => $signature, 'token' => $token, 'timestamp' => $timestamp, 'nonce' => $nonce ]; Log::info('签名验证:', $verifyInfo); if ($tmpStr == $signature) { Log::info('微信签名验证成功'); echo $echostr; exit; } else { Log::error('微信签名验证失败', $verifyInfo); exit; } } catch (\Exception $e) { Log::error('微信验证签名错误:' . $e->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); exit; } } // 接收消息 protected function receiveMessage() { try { $postStr = file_get_contents("php://input"); if (empty($postStr)) { Log::error('微信消息:接收数据为空'); return ''; } // 转换编码 $postStr = mb_convert_encoding($postStr, 'UTF-8', 'UTF-8,GBK,GB2312'); Log::info('微信消息:' . $postStr); libxml_disable_entity_loader(true); $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); if ($postObj === false) { Log::error('微信消息:XML解析失败'); return ''; } // 检查消息类型 $msgType = strtolower((string) $postObj->MsgType); Log::info('微信消息类型:' . $msgType); // 处理事件消息 if ($msgType == 'event') { $event = strtolower((string) $postObj->Event); Log::info('微信事件类型:' . $event); // 处理扫码事件和关注事件 if ($event == 'scan' || $event == 'subscribe') { $scene_str = trim((string) $postObj->EventKey); // 如果是关注事件,需要去掉前缀 'qrscene_' if ($event == 'subscribe' && strpos($scene_str, 'qrscene_') === 0) { $scene_str = substr($scene_str, 8); } $fromUsername = trim((string) $postObj->FromUserName); $toUsername = trim((string) $postObj->ToUserName); $ticket = (string) $postObj->Ticket; Log::info('微信扫码/关注事件', [ 'event' => $event, 'scene_str' => $scene_str, 'fromUsername' => $fromUsername, 'toUsername' => $toUsername, 'ticket' => $ticket, 'time' => date('Y-m-d H:i:s') ]); // 创建票据保存目录 $upload_dir = 'public/storage/uploads/ticket/'; if (!is_dir($upload_dir)) { mkdir($upload_dir, 0755, true); Log::info('创建票据保存目录:' . $upload_dir); } // 保存扫码数据 $data = [ 'openid' => $fromUsername, 'scene_str' => $scene_str, 'ticket' => $ticket, 'scan_time' => time(), 'event' => $event ]; $content = json_encode($data, JSON_UNESCAPED_UNICODE); $file = $upload_dir . $scene_str . "_" . $ticket . ".json"; file_put_contents($file, $content); Log::info('保存扫码数据到文件:' . $file); // 获取用户信息 try { $accessToken = $this->getGZHAccessToken(); $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$accessToken}&openid={$fromUsername}&lang=zh_CN"; $client = new Client(['verify' => false]); $response = $client->get($url); $userInfo = json_decode($response->getBody(), true); Log::info('获取用户信息结果:' . json_encode($userInfo, JSON_UNESCAPED_UNICODE)); if (isset($userInfo['openid'])) { // 保存用户信息到数据库 $user = User::where('openid', $fromUsername)->find(); if (!$user) { $user = new User; $user->openid = $fromUsername; $user->name = $userInfo['nickname'] ?? ''; $user->avatar = $userInfo['headimgurl'] ?? ''; $user->save(); Log::info('创建新用户', ['user' => $user->toArray()]); } else { // 更新用户信息 $user->name = $userInfo['nickname'] ?? $user->name; $user->avatar = $userInfo['headimgurl'] ?? $user->avatar; $user->save(); Log::info('更新用户信息', ['user' => $user->toArray()]); } // 更新票据文件中的用户信息 $data['user_info'] = [ 'uid' => $user->uid, 'name' => $user->name, 'avatar' => $user->avatar, 'openid' => $user->openid ]; file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE)); Log::info('更新票据文件中的用户信息'); } } catch (\Exception $e) { Log::error('获取用户信息失败:' . $e->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); } return 'success'; } } return 'success'; } catch (\Exception $e) { Log::error('处理微信消息错误:' . $e->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); return 'success'; } } // 发送小程序卡片消息的方法 private function sendMiniProgramCard($openid) { $accessToken = $this->getGZHAccessToken(); print_r($accessToken); if (!$accessToken) { // 处理获取access_token失败的情况 return; } $postData = json_encode([ 'touser' => $openid, // 接收者(用户)的openid 'msgtype' => 'miniprogrampage', 'miniprogrampage' => [ 'title' => '小程序标题', 'appid' => '小程序appid', 'pagepath' => '小程序路径', 'thumb_media_id' => '你的thumb_media_id' ] ], JSON_UNESCAPED_UNICODE); $url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={$accessToken}"; $result = json_decode(file_get_contents($url, false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-type: application/json\r\n", 'content' => $postData, 'timeout' => 60 // 超时时间(单位:s) ] ])), true); // 处理返回结果 if ($result['errcode'] == 0) { // 发送成功 } else { // 发送失败 } } // 获取公众号Access token public function getGZHAccessToken() { // 从数据库获取配置 $appid = AdminConfig::where('config_name', 'wechat_appid')->value('config_value'); $secret = AdminConfig::where('config_name', 'wechat_appsecret')->value('config_value'); if (empty($appid) || empty($secret)) { throw new \Exception('微信配置信息未设置'); } // 构建请求URL $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}"; try { // 使用 GuzzleHttp 发送请求,禁用SSL验证 $client = new Client([ 'verify' => false ]); $response = $client->get($url); $data = json_decode($response->getBody(), true); if (!isset($data['access_token'])) { throw new \Exception("获取access_token失败: {$data['errmsg']}", $data['errcode'] ?? -1); } return $data['access_token']; } catch (\Exception $e) { Log::error('获取access_token失败:' . $e->getMessage()); throw $e; } } /** * 获取微信登录二维码 * @return \think\response\Json */ public function getLoginTicket() { try { Log::info('开始获取微信登录二维码'); // 获取access_token $access_token = $this->getGZHAccessToken(); Log::info('获取access_token成功:' . $access_token); // 构建请求URL - 使用正确的接口生成临时二维码 $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$access_token}"; // 生成唯一场景值 $scene_str = md5(uniqid() . time()); Log::info('生成场景值:' . $scene_str); // 准备请求数据 - 生成临时二维码,有效期5分钟 $postData = json_encode([ 'expire_seconds' => 300, // 5分钟有效期 'action_name' => 'QR_STR_SCENE', 'action_info' => [ 'scene' => [ 'scene_str' => $scene_str ] ] ]); // 发送请求获取ticket $client = new Client(['verify' => false]); $response = $client->post($url, [ 'body' => $postData, 'headers' => [ 'Content-Type' => 'application/json' ] ]); $result = json_decode($response->getBody(), true); Log::info('微信返回结果:' . json_encode($result, JSON_UNESCAPED_UNICODE)); if (isset($result['errcode']) && $result['errcode'] != 0) { Log::error('获取二维码失败:' . $result['errmsg']); return json(['code' => 1, 'msg' => '获取二维码失败:' . $result['errmsg']]); } // 使用ticket获取二维码图片URL $ticket = urlencode($result['ticket']); $qrcodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={$ticket}"; // 将场景值保存到缓存中,用于后续验证 Cache::set('wx_login_scene_' . $result['ticket'], [ 'scene_str' => $scene_str, 'create_time' => time(), 'expire_time' => time() + 300 ], 300); Log::info('二维码生成成功', [ 'ticket' => $result['ticket'], 'scene_str' => $scene_str, 'expire_time' => time() + 300 ]); return json([ 'code' => 0, 'msg' => '获取二维码成功', 'data' => [ 'ticket' => $result['ticket'], 'expire_seconds' => $result['expire_seconds'], 'url' => $qrcodeUrl, 'scene_str' => $scene_str ] ]); } catch (\Exception $e) { Log::error('获取微信登录二维码失败:' . $e->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); return json(['code' => 1, 'msg' => '获取二维码失败:' . $e->getMessage()]); } } /** * 重新生成二维码 * @return \think\response\Json */ public function reGenerateQrcode() { try { $scene_str = Request::post('scene_str'); if (empty($scene_str)) { return json(['code' => 1, 'msg' => '参数错误']); } // 清除旧的缓存 Cache::delete('wx_login_scene_' . $scene_str); // 获取access_token $access_token = $this->getGZHAccessToken(); // 构建请求URL $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$access_token}"; // 生成新的场景值 $new_scene_str = md5(uniqid() . time()); // 准备请求数据 $postData = json_encode([ 'expire_seconds' => 300, 'action_name' => 'QR_STR_SCENE', 'action_info' => [ 'scene' => [ 'scene_str' => $new_scene_str ] ] ]); // 发送请求获取ticket $client = new Client(['verify' => false]); $response = $client->post($url, [ 'body' => $postData, 'headers' => [ 'Content-Type' => 'application/json' ] ]); $result = json_decode($response->getBody(), true); if (isset($result['errcode']) && $result['errcode'] != 0) { return json(['code' => 1, 'msg' => '获取二维码失败:' . $result['errmsg']]); } // 使用ticket获取二维码图片URL $ticket = urlencode($result['ticket']); $qrcodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={$ticket}"; // 将新的场景值保存到缓存中 Cache::set('wx_login_scene_' . $new_scene_str, [ 'scene_str' => $new_scene_str, 'create_time' => time(), 'expire_time' => time() + 300 ], 300); return json([ 'code' => 0, 'msg' => '获取二维码成功', 'data' => [ 'ticket' => $result['ticket'], 'expire_seconds' => $result['expire_seconds'], 'url' => $qrcodeUrl, 'scene_str' => $new_scene_str ] ]); } catch (\Exception $e) { Log::error('重新生成二维码失败:' . $e->getMessage()); return json(['code' => 1, 'msg' => '重新生成二维码失败:' . $e->getMessage()]); } } /** * 检查微信扫码登录状态 * @return \think\response\Json */ public function checkLoginStatus() { try { $scene_str = Request::post('scene_str'); $ticket = Request::post('ticket'); Log::info('检查登录状态', [ 'scene_str' => $scene_str, 'ticket' => $ticket, 'time' => date('Y-m-d H:i:s') ]); if (empty($scene_str) || empty($ticket)) { Log::warning('检查登录状态参数错误', [ 'scene_str' => $scene_str, 'ticket' => $ticket ]); return json(['code' => 0, 'msg' => '参数错误']); } // 检查票据文件 $file = 'public/storage/uploads/ticket/' . $scene_str . "_" . $ticket . ".json"; Log::info('检查票据文件:' . $file); if (file_exists($file)) { $loginData = json_decode(file_get_contents($file), true); Log::info('票据文件内容:' . json_encode($loginData, JSON_UNESCAPED_UNICODE)); if ($loginData) { // 检查是否包含用户信息 if (isset($loginData['user_info'])) { Log::info('用户已扫码登录', [ 'user_info' => $loginData['user_info'] ]); // 设置登录session session('user_id', $loginData['user_info']['uid']); session('user_info', $loginData['user_info']); session('openid', $loginData['user_info']['openid']); // 删除临时文件 unlink($file); return json(['code' => 1, 'msg' => '登录成功']); } else { Log::info('等待获取用户信息'); return json(['code' => 0, 'msg' => '正在获取用户信息,请稍候...']); } } } Log::info('等待扫码'); return json(['code' => 0, 'msg' => '等待扫码']); } catch (\Exception $e) { Log::error('检查登录状态失败:' . $e->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); return json(['code' => 0, 'msg' => '系统错误']); } } /** * 根据openid获取用户信息 * @param string $openid * @return array|null */ private function getUserInfoByOpenid($openid) { try { // 这里需要根据你的用户表结构来实现 // 示例:从用户表中查询openid对应的用户信息 $user = User::where('openid', $openid)->find(); if ($user) { return $user->toArray(); } return null; } catch (\Exception $e) { Log::error('获取用户信息失败:' . $e->getMessage()); return null; } } /** * 测试微信服务器访问 */ public function testWechat() { try { // 记录所有请求信息 $requestInfo = [ 'time' => date('Y-m-d H:i:s'), 'method' => Request::method(), 'url' => Request::url(true), 'ip' => Request::ip(), 'params' => Request::param(), 'headers' => getallheaders(), 'server' => [ 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'] ?? '', 'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? '', 'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? '', 'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'] ?? '', 'QUERY_STRING' => $_SERVER['QUERY_STRING'] ?? '', 'HTTP_USER_AGENT' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'HTTP_REFERER' => $_SERVER['HTTP_REFERER'] ?? '', ] ]; Log::info('微信测试接口访问:' . json_encode($requestInfo, JSON_UNESCAPED_UNICODE)); // 获取原始数据 $rawData = file_get_contents("php://input"); if (!empty($rawData)) { $rawData = mb_convert_encoding($rawData, 'UTF-8', 'UTF-8,GBK,GB2312'); Log::info('微信测试接口原始数据:' . $rawData); } // 如果是GET请求,进行签名验证 if (Request::method() == 'GET') { $signature = Request::get('signature'); $timestamp = Request::get('timestamp'); $nonce = Request::get('nonce'); $echostr = Request::get('echostr'); Log::info('微信测试接口验证参数:', [ 'signature' => $signature, 'timestamp' => $timestamp, 'nonce' => $nonce, 'echostr' => $echostr ]); if (!empty($signature) && !empty($timestamp) && !empty($nonce)) { $token = AdminConfig::where('config_name', 'wechat_token')->value('config_value'); if (empty($token)) { Log::error('微信token未配置'); return 'token not configured'; } $tmpArr = array($token, $timestamp, $nonce); sort($tmpArr, SORT_STRING); $tmpStr = implode($tmpArr); $tmpStr = sha1($tmpStr); if ($tmpStr == $signature) { Log::info('微信测试接口验证成功'); return $echostr; } else { Log::error('微信测试接口验证失败'); return 'signature verification failed'; } } } return 'success'; } catch (\Exception $e) { Log::error('微信测试接口错误:' . $e->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); return 'error'; } } }