where('config_key', 'WECHAT')->find(); if (!$wechatConfig || empty($wechatConfig['value'])) { throw new CommonException('微信公众号未配置'); } $configData = $wechatConfig['value']; // 如果是字符串,则解析JSON if (is_string($configData)) { $configData = json_decode($configData, true); } // 如果解析失败或不是数组,抛出异常 if (!is_array($configData) || empty($configData['app_id']) || empty($configData['app_secret'])) { throw new CommonException('微信公众号配置不完整'); } return $configData; } /** * 获取微信公众号授权URL * @param string $miniOpenid * @return array * @throws \Exception */ public function getAuthUrl(string $miniOpenid) { $config = $this->getWechatConfig(); // 生成state参数,用于传递小程序openid $state = base64_encode($miniOpenid); // 获取当前域名 $domain = request()->domain(); $redirectUri = $domain . '/api/wechat/callback'; // 构建微信授权URL $authUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query([ 'appid' => $config['app_id'], 'redirect_uri' => urlencode($redirectUri), 'response_type' => 'code', 'scope' => 'snsapi_userinfo', 'state' => $state ]) . '#wechat_redirect'; return [ 'auth_url' => $authUrl, 'redirect_uri' => $redirectUri ]; } /** * 处理微信授权回调 * @param string $code * @param string $state * @return array * @throws \Exception */ public function handleCallback(string $code, string $state) { $config = $this->getWechatConfig(); // 解析state获取小程序openid $miniOpenid = base64_decode($state); if (empty($miniOpenid)) { throw new CommonException('参数错误'); } // 通过code获取access_token $tokenUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query([ 'appid' => $config['app_id'], 'secret' => $config['app_secret'], 'code' => $code, 'grant_type' => 'authorization_code' ]); $tokenResponse = $this->httpGet($tokenUrl); $tokenData = json_decode($tokenResponse, true); if (!$tokenData || isset($tokenData['errcode'])) { throw new CommonException('获取微信授权失败:' . ($tokenData['errmsg'] ?? '未知错误')); } $accessToken = $tokenData['access_token']; $wechatOpenid = $tokenData['openid']; // 获取用户信息 $userInfoUrl = 'https://api.weixin.qq.com/sns/userinfo?' . http_build_query([ 'access_token' => $accessToken, 'openid' => $wechatOpenid, 'lang' => 'zh_CN' ]); $userInfoResponse = $this->httpGet($userInfoUrl); $userInfo = json_decode($userInfoResponse, true); if (!$userInfo || isset($userInfo['errcode'])) { throw new CommonException('获取微信用户信息失败:' . ($userInfo['errmsg'] ?? '未知错误')); } return [ 'mini_openid' => $miniOpenid, 'wechat_openid' => $wechatOpenid, 'nickname' => $userInfo['nickname'] ?? '', 'avatar' => $userInfo['headimgurl'] ?? '', 'sex' => $userInfo['sex'] ?? 0 ]; } /** * HTTP GET请求 * @param string $url * @return string * @throws \Exception */ private function httpGet(string $url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_TIMEOUT, 30); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($response === false || !empty($error)) { throw new CommonException('网络请求失败:' . $error); } if ($httpCode !== 200) { throw new CommonException('网络请求失败,HTTP状态码:' . $httpCode); } return $response; } }