Files
xin-procurement/app/Services/WechatService.php
T
2026-08-06 10:49:51 +08:00

100 lines
3.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services;
use App\Exceptions\RepositoryException;
use EasyWeChat\Kernel\Exceptions\HttpException;
use EasyWeChat\MiniApp\Application;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* 微信小程序服务(基于 EasyWeChat 6.x
*
* 封装 code2Session / 手机号解密;配置读取 site_config('wechatMini')
* (后台「小程序设置」面板维护,存 sys_site_config 表)。
*
* 测试策略:通过 setHttpClient() 注入 Symfony MockHttpClient 拦截微信 HTTP 调用。
*/
class WechatService
{
private ?Application $app = null;
private ?HttpClientInterface $httpClient = null;
/**
* 注入自定义 HttpClient(测试注入 MockHttpClient;注入后强制重建 Application
*/
public function setHttpClient(HttpClientInterface $httpClient): void
{
$this->httpClient = $httpClient;
$this->app = null;
}
/**
* code2Session:小程序 wx.login 的 code 换取 openid / session_key
*
* @param string $code wx.login 返回的临时登录凭证
* @return array{openid: string, session_key: string, unionid?: string}
*/
public function code2Session(string $code): array
{
try {
/** @var array{openid: string, session_key: string, unionid?: string} $session */
$session = $this->app()->getUtils()->codeToSession($code);
} catch (HttpException|TransportExceptionInterface $e) {
throw new RepositoryException('微信登录失败:' . $e->getMessage());
}
return $session;
}
/**
* 获取手机号:wx.getPhoneNumber 的 phoneCode 换取手机号
*
* @param string $phoneCode 手机号授权事件返回的动态令牌
* @return string 用户手机号
*/
public function getPhone(string $phoneCode): string
{
try {
$result = $this->app()->getUtils()->getPhoneNumber($phoneCode);
} catch (HttpException|TransportExceptionInterface $e) {
throw new RepositoryException('获取手机号失败:' . $e->getMessage());
}
$phone = (string) ($result['phone_info']['phoneNumber']
?? $result['phone_info']['purePhoneNumber']
?? '');
if ($phone === '') {
throw new RepositoryException('获取手机号失败:微信未返回有效手机号');
}
return $phone;
}
/**
* EasyWeChat 小程序应用实例(懒构建单例)
*/
protected function app(): Application
{
if ($this->app === null) {
$config = (array) site_config('wechatMini', []);
if (empty($config['appid']) || empty($config['secret'])) {
throw new RepositoryException('微信小程序尚未配置(WECHAT_MINI_APPID / WECHAT_MINI_SECRET');
}
$this->app = new Application([
'app_id' => (string) $config['appid'],
'secret' => (string) $config['secret'],
]);
if ($this->httpClient !== null) {
$this->app->setHttpClient($this->httpClient);
}
}
return $this->app;
}
}