web = $cfg['web']; $this->mini = $cfg['mini']; $this->login = $cfg['login']; } /** * 获取微信二维码 */ public function makeQrUrl() { try { $state = bin2hex(random_bytes(16)); $ttl = (int)($this->login['state_ttl'] ?? 300); // 记录 state Redis::setEx("wx:login:state:$state", $ttl, json_encode([ 'redirect' => $this->web['redirect'], 'ts' => time(), ], JSON_UNESCAPED_UNICODE)); // 组装跳转地址 $qrUrl = sprintf( 'https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=%s&scope=%s&state=%s#wechat_redirect', $this->web['app_id'], urlencode($this->web['redirect']), 'code', 'snsapi_login', $state, ); $data = [ 'state' => $state, 'qr_url' => $qrUrl ]; return $data; } catch (\Throwable $th) { // var_dump($th->getFile().'|'.$th->getLine().'|'.$th->getMessage()); throw new BusinessException('获取微信二维码失败'); } } /** * 获取微信access_token */ public function wechatWebAccessToken(string $code, string $state): array { try { // 校验 state $stateJson = Redis::get("wx:login:state:$state"); if (!$stateJson) { throw new \RuntimeException('state 已过期或无效'); } // 拼接地址 $tokenUrl = sprintf( 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code', $this->web['app_id'], $this->web['app_secret'], $code ); // code 换 token $token = $this->httpGetJson($tokenUrl); if (!isset($token['access_token'], $token['openid'])) { throw new \RuntimeException('换取 access_token 失败:'. json_encode($token, JSON_UNESCAPED_UNICODE)); } return $token; } catch (\Throwable $th) { throw new BusinessException('获取 access_token 失败'); } } /** * 获取微信用户信息 * * @param array $token * @return array */ public function wechatWebUserInfo(array $accessToken): array { try { // 拉取用户信息 return $this->httpGetJson(sprintf( 'https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s', $accessToken['access_token'], $accessToken['openid'] )); } catch (\Throwable $th) { // var_dump($th->getFile().'|'.$th->getLine().'|'.$th->getMessage()); throw new BusinessException('获取用户信息失败'); } } /** * tools - httpGetJson */ protected function httpGetJson(string $url): array { $client = new Client([ 'verify' => false, // 禁用 SSL 验证 'timeout' => 10, ]); $json = $client->get($url)->getBody()->getContents(); return json_decode($json, true) ?: []; } //----------------------------小程序登录---------------------------------------- /** * 获取 openid / session_key * @param $code * @return mixed */ public function miniCode2Session($code) { $appid = $this->mini['app_id']; $secret = $this->mini['app_secret']; $url = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$code&grant_type=authorization_code"; return json_decode(file_get_contents($url), true); } /** * 获取 access_token * @return mixed */ public function getMiniAccessToken() { $appid = $this->mini['app_id']; $secret = $this->mini['app_secret']; $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appid&secret=$secret"; return json_decode(file_get_contents($url), true); } /** * 调用微信接口取手机号 * @param $access_token * @param $phone_code * @return mixed */ public function getMiniPhoneNumber($access_token, $phone_code) { $url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={$access_token}"; $data = json_encode([ 'code' => $phone_code ]); $options = [ 'http' => [ 'header' => "Content-type: application/json", 'method' => 'POST', 'content' => $data, ], ]; return json_decode(file_get_contents($url, false, stream_context_create($options)), true); } /** * 小程序获取openid * @param string $code * @return array * @throws BusinessException */ public function xcxOpenid(string $code): array { $config = [ 'app_id' => getenv('WECHAT_MINI_APPID'), 'secret' => getenv('WECHAT_MINI_SECRET'), 'response_type' => 'array', 'log' => [ 'level' => 'debug', 'file' => runtime_path().'/mzl/wechat.log', ] ]; // 创建微信小程序对象 $wechat = new xcxApplication($config); // 获取 session_key 和 openid return $wechat->getUtils()->codeToSession($code); } /** * 小程序获取手机号 * @param string $phone_code * @return mixed * @throws BusinessException */ public function xcxPhoneNumber(string $phone_code) { $config = [ 'app_id' => getenv('WECHAT_MINI_APPID'), 'secret' => getenv('WECHAT_MINI_SECRET'), 'response_type' => 'array', 'log' => [ 'level' => 'debug', 'file' => runtime_path().'/mzl/wechat.log', ] ]; // 创建微信小程序对象 $wechat = new xcxApplication($config); // getPhoneNumber $data = ['code' => $phone_code]; $result = $wechat->getClient()->postJson('wxa/business/getuserphonenumber', $data); if (isset($result['phone_info'])) { // 业务处理 $result['phone_info']['phoneNumber'] return $result; } else { throw new BusinessException($result['errmsg']); } } }