payload = $payload; $this->wechat = Wechat::miniProgram(); } // 小程序登录 public function login() { // https://developers.weixin.qq.com/community/develop/doc/00022c683e8a80b29bed2142b56c01 if (empty($this->payload['sessionId'])) { error_stop('未获取到登陆态, 请重试', -1); } $sessionData = redis_cache($this->payload['sessionId']); if (empty($sessionData)) { error_stop('登陆态已过期, 请重试', -1); } // 如果前端传了手机号凭证(新版 code 或传统 iv+encryptedData),解析手机号 $mobile = ''; if (!empty($this->payload['code']) || (!empty($this->payload['iv']) && !empty($this->payload['encryptedData']))) { $mobile = $this->getUserPhoneNumber(); } $wechatUser = [ 'openid' => $sessionData['openid'], 'unionid' => $sessionData['unionid'] ?? '', 'mobile' => $mobile, 'avatar' => '', 'nickname' => '', ]; return $wechatUser; } public function bind() { if (empty($this->payload['sessionId'])) { error_stop('未获取到登陆态, 请重试', -1); } $sessionData = redis_cache($this->payload['sessionId']); if (empty($sessionData)) { error_stop('登陆态已过期, 请重试', -1); } $wechatUser = [ 'openid' => $sessionData['openid'], 'unionid' => $sessionData['unionid'] ?? '', 'avatar' => '', 'nickname' => '', ]; return $wechatUser; } // 解密微信小程序手机号(支持新版 code 与传统 iv + encryptedData) public function getUserPhoneNumber() { // 1. 优先使用新版微信小程序通过 code 获取手机号 API if (!empty($this->payload['code'])) { try { $response = $this->wechat->auth->httpPostJson('wxa/business/getuserphonenumber', [ 'code' => $this->payload['code'] ]); if (isset($response['errcode']) && (int)$response['errcode'] === 0 && !empty($response['phone_info']['purePhoneNumber'])) { if (!empty($response['phone_info']['countryCode']) && $response['phone_info']['countryCode'] !== '86') { error_stop('仅支持大陆地区手机号'); } return $response['phone_info']['purePhoneNumber']; } if (!empty($response['errmsg']) && empty($this->payload['encryptedData'])) { error_stop('获取手机号失败: ' . $response['errmsg']); } } catch (\Exception $e) { if (empty($this->payload['encryptedData'])) { error_stop('获取手机号异常: ' . $e->getMessage()); } } } // 2. 兼容传统 iv + encryptedData 模式 if (empty($this->payload['sessionId'])) { error_stop('未获取到登陆态, 请重试', -1); } $sessionData = redis_cache($this->payload['sessionId']); if (empty($sessionData)) { error_stop('登陆态已过期, 请重试', -1); } if (empty($this->payload['iv']) || empty($this->payload['encryptedData'])) { error_stop('缺少手机号授权凭证'); } $phoneInfo = $this->wechat->encryptor->decryptData($sessionData['session_key'], $this->payload['iv'], $this->payload['encryptedData']); if (empty($phoneInfo['purePhoneNumber'])) { error_stop('获取失败,请重试'); } if ($phoneInfo['countryCode'] !== '86') { error_stop('仅支持大陆地区手机号'); } return $phoneInfo['purePhoneNumber']; } /** * 获取session_id, 缓存 session_key, openid (unionid), 自动登录 * * @return string */ public function getSessionId() { if (empty($this->payload['code'])) { error_stop('缺少code参数'); } $decryptData = $this->wechat->auth->session($this->payload['code']); if(!empty($decryptData['errmsg'])) { error_stop($decryptData['errmsg']); } if (empty($decryptData['session_key'])) { error_stop('未获取到登陆态, 请重试', -1); } $auto_login = $this->payload['auto_login'] ?? false; // 自动登录流程 if($auto_login) { $oauthInfo = ThirdOauth::getByOpenid($decryptData['openid']); if($oauthInfo && $oauthInfo->user_id) { $auth = Auth::instance(); $ret = $auth->direct($oauthInfo->user_id); if ($ret) { set_token_in_header($auth->getToken()); } } } $session_id = Random::uuid(); redis_cache($session_id, $decryptData, 60 * 60 * 24 * 7); // session_key缓存保留一周 return ['session_id' => $session_id, 'auto_login' => $auto_login]; } }