diff --git a/app/service/MiniProgramWechatService.php b/app/service/MiniProgramWechatService.php index 1bb67ad..06971e6 100644 --- a/app/service/MiniProgramWechatService.php +++ b/app/service/MiniProgramWechatService.php @@ -9,6 +9,8 @@ use think\facade\Log; class MiniProgramWechatService { + private const ACCESS_TOKEN_ERROR_CODES = [40001, 40014, 42001]; + private string $miniAppCode; private array $miniApp; @@ -44,24 +46,46 @@ class MiniProgramWechatService public function mobileByPhoneCode(string $phoneCode): string { - $accessToken = $this->wechatAccessToken(); - if ($phoneCode === '' || $accessToken === '') { + if ($phoneCode === '') { 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'] ?? ''); + for ($attempt = 0; $attempt < 2; $attempt++) { + $accessToken = $this->wechatAccessToken($attempt === 1); + if ($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->decodeResponse($response, 'miniapi wx get phone number'); + if ($result === []) { + return ''; + } + + $errcode = intval($result['errcode'] ?? 0); + if ($errcode !== 0) { + Log::warning('miniapi wx get phone number failed: ' . json_encode($result, JSON_UNESCAPED_UNICODE)); + if ($attempt === 0 && in_array($errcode, self::ACCESS_TOKEN_ERROR_CODES, true)) { + Cache::delete($this->wechatAccessTokenCacheKey()); + continue; + } + return ''; + } + + $phoneInfo = $result['phone_info'] ?? []; + return (string)($phoneInfo['purePhoneNumber'] ?? $phoneInfo['phoneNumber'] ?? ''); + } + + return ''; } - private function wechatAccessToken(): string + private function wechatAccessToken(bool $forceRefresh = false): string { $config = $this->wechatConfig(); if ($config['appid'] === '' || $config['secret'] === '') { @@ -69,17 +93,26 @@ class MiniProgramWechatService return ''; } - $cacheKey = 'miniapp_access_token_' . $this->miniAppCode; - $cached = Cache::get($cacheKey); - if (!empty($cached)) { - return (string)$cached; + $cacheKey = $this->wechatAccessTokenCacheKey(); + if (!$forceRefresh) { + $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); + $response = Httpcurl::request( + 'https://api.weixin.qq.com/cgi-bin/stable_token', + 'post', + json_encode([ + 'grant_type' => 'client_credential', + 'appid' => $config['appid'], + 'secret' => $config['secret'], + 'force_refresh' => $forceRefresh, + ], JSON_UNESCAPED_UNICODE), + ['Content-Type: application/json'], + 8 + ); $result = $this->decodeWechatResponse($response, 'miniapi wx access token'); if (empty($result['access_token'])) { return ''; @@ -90,6 +123,12 @@ class MiniProgramWechatService return (string)$result['access_token']; } + private function wechatAccessTokenCacheKey(): string + { + $appid = (string)($this->miniApp['wechat_appid'] ?? ''); + return 'miniapp_stable_access_token_' . md5($this->miniAppCode . '|' . $appid); + } + private function wechatConfig(): array { return [