获取unionid
/**
* @Description: curl请求
* @Author: Yang
* @param $url
* @param null $data
* @param string $method
* @param array $header
* @param bool $https
* @param int $timeout
* @return mixed
*/
function curl_request($url, $data = null, $method = 'GET', $header = array("content-type:application/json"), $https = true, $timeout = 50) {
$method = strtoupper($method);
$ch = curl_init(); //初始化
curl_setopt($ch, CURLOPT_URL, $url); //访问的URL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //只获取页面内容,但不输出
if ($https) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //https请求 不验证证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //https请求 不验证HOST
}
if ($method != "GET") {
// if($method == 'POST'){
// curl_setopt($ch, CURLOPT_POST, true);//请求方式为post请求
// }
// if ($method == 'PUT' || strtoupper($method) == 'DELETE') {
// curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); //设置请求方式
// }
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); //请求数据
}
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //模拟的header头
//curl_setopt($ch, CURLOPT_HEADER, false);//设置不需要头信息
$result = curl_exec($ch); //执行请求
// errlog("error: ".json_encode($result));die;
curl_close($ch); //关闭curl,释放资源
// echo "<pre>";var_dump( $header , $data , $result );
return $result;
// return json_decode($result,true);
}
<?php
namespace app\api\controller\v1\publics;
use app\common\controller\ApiController;
use app\services\publics\LoginServices;
use app\services\publics\SmsServices;
use app\services\huawei\ObsServices;
use app\services\user\UserServices;
use app\api\service\Jssdk;
use EasyAdmin\tool\CommonTool;
use think\Cache;
class Publics extends ApiController {
/**
* 公众号根据code获取openid
*/
public function wechatGetCode() {
$params = $this->request->getMore([
['code',''],
['invite_code',''],
['redirect_url',''],
['type',''],
]);
$code = $params['code'];
$invite_code = 0;
$redirect_uri = $params['redirect_url'];
$snsapi_type = $params['type']; // 空是静默授权 userinfo动态
if( $snsapi_type ) {
$snsapi_type = "snsapi_userinfo";
}else{
$snsapi_type = "snsapi_base";
}
// 发起授权
if( !$code ){
$wx_p_appid = sysconfig('wx_public','wx_p_appid');
// $redirect_uri = urlencode( $url );//重定向地址
$url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$wx_p_appid&redirect_uri=$redirect_uri&response_type=code&scope=$snsapi_type&state=1#wechat_redirect";
return app('json')->success( 'ok' , ['appid'=>$wx_p_appid,'url'=>$url] );
// header("Location:" . $url);
}
$wx_p_appid = sysconfig('wx_public','wx_p_appid');
$wx_p_secret_key = sysconfig('wx_public','wx_p_secret_key');
$oauth2Url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$wx_p_appid&secret=$wx_p_secret_key&code=$code&grant_type=authorization_code";
$oauth2 = curl_request( $oauth2Url, $data=null, 'GET', array("content-type:application/json"), $https=true );
$oauth2 = json_decode( $oauth2, true );
// echo "<pre>";var_dump( $oauth2 );die;
if( !isset($oauth2["openid"]) )return app('json')->fail( '微信登陆错误' , [] , 402 );
// 获得 access_token 和openid
$openid = $oauth2['openid'];
// 判断用户是否存在
$loginServices = app()->make(LoginServices::class);
$user_register = $loginServices->getOne(['wx_openid' => $openid ] , "wx_openid" );
if( $user_register ) {
$user_register['openid'] = $user_register['wx_openid'];
$data = $loginServices->wechatLogin( $user_register );
return app('json')->success( '登录成功' , $data );
}
// 用户头像昵称
$get_user_info_url = "https://api.weixin.qq.com/sns/userinfo?access_token=" . $oauth2["access_token"] . "&openid=$openid&lang=zh_CN";
$userinfo = curl_request( $get_user_info_url, $data=null, 'GET', array("content-type:application/json"), $https=true );
// file_put_contents( "txt/1.txt" , $userinfo . PHP_EOL , FILE_APPEND );
$userinfo = json_decode( $userinfo, true );
//获取unionid 上级参数
$wxpublic_access_token = $loginServices->get_access_token( $wx_p_appid , $wx_p_secret_key );
$get_user_info_url2 = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=$wxpublic_access_token&openid=$openid&lang=zh_CN";
$userinfo2 = curl_request( $get_user_info_url2 );
// file_put_contents( "txt/2.txt" , $userinfo2 . PHP_EOL , FILE_APPEND );
$userinfo2 = json_decode( $userinfo2, true );
if(data_get($userinfo2,"errcode")) {
$wxpublic_access_token = $loginServices->get_access_token( $wx_p_appid , $wx_p_secret_key , 1 );
$get_user_info_url2 = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=$wxpublic_access_token&openid=$openid&lang=zh_CN";
$userinfo2 = curl_request( $get_user_info_url2 );
$userinfo2 = json_decode( $userinfo2, true );
}
$qrSceneStr = data_get($userinfo2,"qr_scene_str");
if( $qrSceneStr ) {
$invite_code = $qrSceneStr;
}
if( $invite_code==0 && data_get($userinfo2,"qr_scene") ) {
$invite_code = $userinfo2['qr_scene'];
}
// echo "<pre>";var_dump( $code , $oauth2 , $userinfo );die;
// 注册登录
$data = $loginServices->wechatLogin( $userinfo , $invite_code );
return app('json')->success( '登录成功' , $data );
}
/**
* 获取微信jsddk配置
*/
public function wx_sdk() {
$params = $this->request->getMore([
['url',''],
]);
$url = $params['url'];
$JssdkServices = app()->make(Jssdk::class);
$data = $JssdkServices->getSignPackage( $url );
return app('json')->success( 'ok' , $data );
}
/**
* 获取微信access_token
*/
public function get_access_token( $wx_p_appid , $wx_p_secret_key , $update_token=0 ) {
$CacheServices = app()->make( Cache::class );
if( $update_token == 1 ) {
$access_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $wx_p_appid . "&secret=" . $wx_p_secret_key;
$access_url_arr = curl_request( $access_url, $data=null, 'GET', array("content-type:application/json"), $https=true );
$access_url_arr1 = json_decode( $access_url_arr, true );
// echo "<pre>";var_dump( $access_url_arr1 );die;
$wxpublic_access_token = $access_url_arr1["access_token"];
$CacheServices->set( "wxpublic_access_token" , $wxpublic_access_token , 6000 );
}else{
$wxpublic_access_token = $CacheServices->has( "wxpublic_access_token" );
if( $wxpublic_access_token ) {
$wxpublic_access_token = $CacheServices->get( "wxpublic_access_token" );
}else{
$access_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $wx_p_appid . "&secret=" . $wx_p_secret_key;
$access_url_arr = curl_request( $access_url, $data=null, 'GET', array("content-type:application/json"), $https=true );
$access_url_arr1 = json_decode( $access_url_arr, true );
// echo "<pre>";var_dump( $access_url_arr1 );die;
$wxpublic_access_token = $access_url_arr1["access_token"];
$CacheServices->set( "wxpublic_access_token" , $wxpublic_access_token , 6000 );
}
}
return $wxpublic_access_token;
}
}
frontend
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微信订阅通知</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<!-- <script src="__PUBLIC__/index/js/jquery-2.2.3.min.js"></script>-->
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background-color: #f8f8f8;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.settings-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
.settings-header {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #f0f0f0;
}
.settings-title {
font-size: 24px;
color: #333;
margin-bottom: 5px;
}
.settings-subtitle {
font-size: 14px;
color: #666;
}
.notification-toggle {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #f0f0f0;
margin-bottom: 20px;
}
.toggle-info {
flex: 1;
}
.toggle-title {
font-size: 16px;
color: #333;
margin-bottom: 3px;
}
.toggle-description {
font-size: 12px;
color: #999;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 26px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .3s;
border-radius: 26px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .3s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: #007aff;
}
input:checked + .toggle-slider:before {
transform: translateX(24px);
}
.user-info-section {
margin-bottom: 30px;
}
.section-title {
font-size: 16px;
color: #333;
margin-bottom: 15px;
font-weight: 600;
}
.info-item {
display: flex;
margin-bottom: 10px;
font-size: 14px;
}
.info-label {
flex: 0 0 80px;
color: #666;
}
.info-value {
flex: 1;
color: #333;
word-break: break-all;
}
.refresh-button {
width: 100%;
padding: 12px;
background-color: #007aff;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
margin-bottom: 20px;
}
.refresh-button:active {
background-color: #0066cc;
}
.settings-footer {
text-align: center;
font-size: 12px;
color: #999;
padding-top: 20px;
border-top: 1px solid #f0f0f0;
}
.toast {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 15px 20px;
border-radius: 5px;
z-index: 10000;
display: none;
}
</style>
</head>
<body>
<div id="app">
<div class="settings-container">
<div class="settings-header">
<h1 class="settings-title">消息订阅设置</h1>
<p class="settings-subtitle">管理您的订阅偏好</p>
</div>
<div class="settings-content">
<div class="notification-toggle">
<div class="toggle-info">
<div class="toggle-title">接收推送通知</div>
<div class="toggle-description">开启后将接收重要消息推送</div>
</div>
<label class="toggle-switch">
<input type="checkbox" v-model="notificationEnabled" @change="handleToggleChange">
<span class="toggle-slider"></span>
</label>
</div>
<!-- <div class="user-info-section">-->
<!-- <h2 class="section-title">用户信息</h2>-->
<!-- <div class="info-item">-->
<!-- <span class="info-label">OpenID:</span>-->
<!-- <span class="info-value">{{ openid || '未获取' }}</span>-->
<!-- </div>-->
<!-- <div class="info-item">-->
<!-- <span class="info-label">UnionID:</span>-->
<!-- <span class="info-value">{{ unionid || '未获取' }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <button class="refresh-button" @click="refreshUserInfo">刷新信息</button>-->
</div>
<div class="settings-footer">
此页面用于管理微信服务号的消息订阅设置
</div>
<div class="toast" id="toast">{{ toastMessage }}</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
notificationEnabled: true,
code: '',
toastMessage: ''
},
mounted() {
// 获取URL参数中的openid和unionid
const urlParams = new URLSearchParams(window.location.search);
this.code = urlParams.get('code') || '';
if(this.code){
this.wechatGetCode();
}
// 获取订阅状态
this.notificationEnabled = true;
},
methods: {
// 获取微信用户信息
wechatGetCode() {
// 调用后台接口,传递 code 参数
fetch(`wechatGetCode.html?code=${this.code}`) // API_URL 已在 public/scripts.html 中定义为 "/Index"
.then(response => response.json()) // 解析 JSON 响应
.then(data => {
console.log(data);
// if (data.success) {
// console.log(data)
// // 假设后台返回格式:{success: true, data: {openid: 'xxx', unionid: 'xxx'}}
//
// this.showToast('用户信息获取成功');
// } else {
// this.showToast('获取失败: ' + (data.message || '未知错误'));
// }
})
.catch(error => {
this.showToast('网络错误,请稍后重试');
console.error('请求失败:', error);
});
},
// 处理开关状态变化
handleToggleChange() {
// 保存订阅状态到localStorage
localStorage.setItem('notificationEnabled', this.notificationEnabled);
// 显示提示
this.showToast(this.notificationEnabled ? '已开启订阅通知' : '已关闭订阅通知');
},
// 显示提示消息
showToast(message) {
this.toastMessage = message;
const toast = document.getElementById('toast');
toast.textContent = message;
toast.style.display = 'block';
setTimeout(() => {
toast.style.display = 'none';
}, 2000);
}
}
});
</script>
</body>
</html>
配置地址 在微信开发者工具中访问
微信网页开发 / 网页授权
https://developers.weixin.qq.com/doc/service/guide/h5/auth.html
微信公众平台
https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

浙公网安备 33010602011771号