Files
miniapi/app/service/MiniProgramWechatService.php
gaofeng e29687c5ff 提交
2026-08-13 15:18:53 +08:00

167 lines
5.4 KiB
PHP

<?php
declare (strict_types=1);
namespace app\service;
use api\Httpcurl;
use think\facade\Cache;
use think\facade\Log;
class MiniProgramWechatService
{
private const ACCESS_TOKEN_ERROR_CODES = [40001, 40014, 42001];
private string $miniAppCode;
private array $miniApp;
public function __construct(string $miniAppCode, array $miniApp)
{
$this->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
{
if ($phoneCode === '') {
return '';
}
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(bool $forceRefresh = false): string
{
$config = $this->wechatConfig();
if ($config['appid'] === '' || $config['secret'] === '') {
Log::warning('miniapi wx config missing: ' . $this->miniAppCode);
return '';
}
$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/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 '';
}
$ttl = max(60, intval($result['expires_in'] ?? 7200) - 300);
Cache::set($cacheKey, $result['access_token'], $ttl);
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 [
'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;
}
}