文件
Henglianshutong/addons/shopro/service/third/wechat/MiniProgram.php

170 行
5.5 KiB
PHP
可执行文件

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace addons\shopro\service\third\wechat;
use addons\shopro\facade\Wechat;
use fast\Random;
use app\common\library\Auth;
use app\admin\model\shopro\ThirdOauth;
class MiniProgram
{
public $wechat;
protected $request;
protected $payload;
public function __construct($payload = [])
{
$this->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,
'openid' => $decryptData['openid'] ?? '',
'auto_login' => $auto_login
];
}
}