miniAppCode = $miniAppCode; $this->miniApp = $miniApp; } public function openidByLoginCode(string $loginCode, bool $required = false): string { $config = $this->wechatConfig(); if ($loginCode === '' || $config['appid'] === '' || $config['secret'] === '') { return ''; } $response = Httpcurl::request('https://api.weixin.qq.com/sns/jscode2session', 'get', [ 'appid' => $config['appid'], 'secret' => $config['secret'], 'js_code' => $loginCode, 'grant_type' => 'authorization_code', ], [], 8); $result = $this->decodeWechatResponse($response, 'miniapi wx code2session'); if (!empty($result['openid'])) { return (string)$result['openid']; } if ($required) { Log::warning('miniapi wx openid missing: ' . json_encode($result, JSON_UNESCAPED_UNICODE)); } return ''; } public function mobileByPhoneCode(string $phoneCode): string { $accessToken = $this->wechatAccessToken(); if ($phoneCode === '' || $accessToken === '') { return ''; } $response = Httpcurl::request( 'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=' . urlencode($accessToken), 'post', json_encode(['code' => $phoneCode], JSON_UNESCAPED_UNICODE), ['Content-Type: application/json'], 8 ); $result = $this->decodeWechatResponse($response, 'miniapi wx get phone number'); $phoneInfo = $result['phone_info'] ?? []; return (string)($phoneInfo['purePhoneNumber'] ?? $phoneInfo['phoneNumber'] ?? ''); } private function wechatAccessToken(): string { $config = $this->wechatConfig(); if ($config['appid'] === '' || $config['secret'] === '') { Log::warning('miniapi wx config missing: ' . $this->miniAppCode); return ''; } $cacheKey = 'miniapp_access_token_' . $this->miniAppCode; $cached = Cache::get($cacheKey); if (!empty($cached)) { return (string)$cached; } $response = Httpcurl::request('https://api.weixin.qq.com/cgi-bin/token', 'get', [ 'grant_type' => 'client_credential', 'appid' => $config['appid'], 'secret' => $config['secret'], ], [], 8); $result = $this->decodeWechatResponse($response, 'miniapi wx access token'); if (empty($result['access_token'])) { return ''; } $ttl = max(60, intval($result['expires_in'] ?? 7200) - 300); Cache::set($cacheKey, $result['access_token'], $ttl); return (string)$result['access_token']; } private function wechatConfig(): array { return [ 'appid' => trim((string)($this->miniApp['wechat_appid'] ?? '')), 'secret' => trim((string)($this->miniApp['wechat_secret'] ?? '')), ]; } private function decodeWechatResponse($response, string $scene): array { $result = $this->decodeResponse($response, $scene); if (isset($result['errcode']) && intval($result['errcode']) !== 0) { Log::warning($scene . ' failed: ' . json_encode($result, JSON_UNESCAPED_UNICODE)); return []; } return $result; } private function decodeResponse($response, string $scene): array { if (!is_array($response) || !isset($response[0]) || !empty($response[3])) { Log::error($scene . ' request failed: ' . json_encode($response, JSON_UNESCAPED_UNICODE)); return []; } $result = json_decode($response[0], true); if (!is_array($result)) { Log::error($scene . ' response decode failed: ' . $response[0]); return []; } return $result; } }