微擎复制可用
微擎访问绕过登陆校验请查看 blog.csdn.net/fuchto/arti…
文中 微擎 WeAccount::create($id); 等函数绕过绕过登陆校验直接使用是无法使用的会报错
需要溯源到对应的方法中 传参调用
如果让用户在同一个页点击两次 同意授权可能会发生 发送两条消息的情况 可以试试 location.reload(); 刷新一下试试
第一步 公众号配置 选择对应的模版
开发此功能必须是 服务号
官方文档 developers.weixin.qq.com/doc/offiacc…
设置公众号的 相关配置 业务域名 js域名 授权域名
第二步 创建数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ims_template_log
-- ----------------------------
DROP TABLE IF EXISTS `ims_template_log`;
CREATE TABLE `ims_template_log` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`openid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户openid',
`template_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '模版id',
`uniacid` int(11) NULL DEFAULT NULL COMMENT '公众号id',
`status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '订阅状态',
`number` int(11) NULL DEFAULT 1 COMMENT '同意次数',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 61 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for ims_template_msg
-- ----------------------------
DROP TABLE IF EXISTS `ims_template_msg`;
CREATE TABLE `ims_template_msg` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uniacid` int(11) NULL DEFAULT NULL COMMENT '公众号id',
`head_title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '网页标题',
`headimage` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像地址',
`content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '显示文案',
`bg_image` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '背景图',
`button_text` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '按钮内容',
`herad_media_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像media_id',
`bg_media_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '背景图media_id',
`template` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '模版内容',
`createtime` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for ims_template_sendLog
-- ----------------------------
DROP TABLE IF EXISTS `ims_template_sendLog`;
CREATE TABLE `ims_template_sendLog` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`msg_id` int(11) NULL DEFAULT NULL COMMENT '消息id templat_msg',
`uniacid` int(11) NULL DEFAULT NULL,
`sum` int(11) NULL DEFAULT NULL COMMENT '推送数',
`success` int(11) NULL DEFAULT NULL COMMENT '推送成功数',
`start_time` int(11) NULL DEFAULT NULL COMMENT '开始数据',
`end_time` int(11) NULL DEFAULT NULL COMMENT '结束时间',
`status` int(2) NULL DEFAULT NULL COMMENT '推送状态:0=推送中,1=推送完成',
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '推送模版标题',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
template_msg 中的 template 格式为
[
{
"name":"模版名称",
"type":"跳转类型",
"appid":"小程序appid",
"pages":"小程序路径",
"jump_url":"跳转链接",
"template_id":"模版id",
"模版id":{
"模版参数key":"模版参数value",
"模版参数key":"模版参数value",
"模版参数key":"模版参数value"
}
},
{
"name":"模版名称",
"type":"跳转类型",
"appid":"小程序appid",
"pages":"小程序路径",
"jump_url":"跳转链接",
"template_id":"模版id",
"模版id":{
"模版参数key":"模版参数value",
"模版参数key":"模版参数value",
"模版参数key":"模版参数value"
}
}
]
生成对应的 授权页链接
open.weixin.qq.com/connect/oau…
若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 公众号的唯一标识 |
redirect_uri | 是 | 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理 |
response_type | 是 | 返回类型,请填写code |
scope | 是 | 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 ) |
state | 否 | 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
#wechat_redirect | 是 | 无论直接打开还是做页面302重定向时候,必须带此参数 |
官方网页
developers.weixin.qq.com/doc/offiacc…
第二步:通过code换取网页授权access_token
首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。
获取code后,请求以下链接获取access_token: api.weixin.qq.com/sns/oauth2/…
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 公众号的唯一标识 |
secret | 是 | 公众号的appsecret |
code | 是 | 填写第一步获取的code参数 |
grant_type | 是 | 填写为authorization_code |
微擎代码
$id = $_GPC['uniacid'];
$code = $_GPC['code'];
$account_api = WeAccount::create($id);
// 获取到用户 openid
$OauthInfo = $account_api->getOauthInfo($code);
$wechats_info = pdo_get("account_wechats",['uniacid'=>$id]);
$appid = $wechats_info['key'];
$openid = $OauthInfo['openid'];
// 微信 jsapi_ticket
$Ticket = $account_api->getJsApiTicket();
// 生成时间戳
$time = TIMESTAMP;
// 随机字符串
$noncestr = mt_rand(10000,99999);
// 获取当前网页
$thisUrl = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING'];
// 拼接签名
$str = "jsapi_ticket={$Ticket}&noncestr={$noncestr}×tamp={$time}&url={$thisUrl}";
// 生成签名
$signature=sha1($str);
$info = pdo_get('template_msg',['uniacid'=>$id,'id'=>$_GPC['id']]);
$template_info = json_decode($info['template'],true);
// 获取所有已设置的模版
// $templateList = getTemplateList($id);
$templateId = '';
foreach ($template_info as $key => $value){
$templateId .=trim($value['template_id']).',';
}
// foreach ($templateList['data'] as $key => $value){
// $templateId .=$value['priTmplId'].',';
// }
$templateId = substr($templateId,0,strlen($templateId)-1);
template('platform/template_home');
//获取私有模板列表
function getTemplateList($uniacid){
$account_api = WeAccount::create($uniacid);
$token = $account_api->getAccessToken();
$url = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token={$token}";
$response = ihttp_get($url);
$data = json_decode($response['content'],true);
return $data;
}
html 文件中 使用 开放标签 拉起授权页
第一步 引入js文件
在需要调用JS接口的页面引入如下JS文件:res.wx.qq.com/open/js/jwe… (支持https)
如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:res2.wx.qq.com/open/js/jwe… (支持https)
备注:支持使用 AMD/CMD 标准模块加载方法加载
<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
第二步 配置对应参数
$(document).ready(function() {
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: 'appid', // 必填,公众号的唯一标识
timestamp: '{$time}', // 必填,生成签名的时间戳
nonceStr: '{$noncestr}', // 必填,生成签名的随机串
signature: '{$signature}',// 必填,签名
jsApiList: ['addEventListener'], // 必填,需要使用的JS接口列表
openTagList :['wx-open-subscribe'] // 必填 使用的 开放标签
});
})
wx.ready(function () {
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中
console.log('验证成功');
});
wx.error(function (res) {
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名
console.log('验证失败');
});
第三步 使用开放标签
{$templateId} 为获取到的 模版id 多个模版id用,隔开 开发标签中的按钮 只有在 手机端才显示 pc端不显示按钮
<wx-open-subscribe template="{$templateId}" id="subscribe-btn">
<script type="text/wxtag-template" slot="style">
<style>
.subscribe-btn {
border: none;
padding: 10px;
border-radius: 5px;
color: #ff605e;
background-color: #fff;
font-size: 16px;
}
</style>
</script>
<script type="text/wxtag-template">
<button class="subscribe-btn">
这是按钮显示文字
</button>
</script>
</wx-open-subscribe>
第四步 提交数据给后端接收
var btn = document.getElementById('subscribe-btn');
// 加载成功
btn.addEventListener('success', function (e) {
console.log('success', e.detail);
if(e.detail.errMsg == "subscribe:ok"){
console.log('同意');
console.log(e.detail.subscribeDetails);
$.ajax({
type : 'post',
url : "url",
async:false,
data : {
uniacid : "{$id}",
openid : "{$openid}",
template : e.detail.subscribeDetails,
},
dataType : 'json',
success:function(datas){
console.log(datas);
if(datas.code == 1){
alert(datas.msg);
// WeixinJSBridge.call('closeWindow');
}else{
alert(datas.msg);
}
}
})
}else{
console.log('不同意');
}
});
// 加载失败
btn.addEventListener('error',function (e) {
alert("加载失败");
console.log('fail', e.detail);
});
注意在 授权也中 用户订阅几次则可以发送几次消息
和一次性订阅消息不一样 无论用户 同意几次 永远只能发送一次
如果用户在已发送的消息中 拒绝接收此消息则当前用户 无论发送几次给当前用户都不会 发送成功
如果用户在已发送的消息中 点击接收此消息 则用户在授权页点击一次授权按钮 无论取消还是允许 次数都会加一 在授权页面中 在外面允许的 模版消息不会在出现到 授权列表里
发送消息
$uniacid = $_GPC['uniacid'];
if(empty($uniacid)){
echo("uniacid 公众号id 不能为空");
die;
}
if(empty($_GPC['id'])){
echo("消息id 不能为空");
die;
}
if(empty($_GPC['template_id'])){
echo("模版id 不能为空");
die;
}
$user = pdo_getall("template_log",['uniacid'=>$uniacid,'template_id'=>$_GPC['template_id'],'status'=>'accept','number >'=> 0]);
$template_info = pdo_get("template_msg",['uniacid'=>$uniacid,'id'=>$_GPC['id']]);
if($user){
// 总人数
$count = count($user);
$i = 0;
$send_data = [
'sum' => $count,
'msg_id' => $template_info['id'],
'uniacid' => $uniacid,
'start_time' => TIMESTAMP,
'status' => 0
];
$template = json_decode($template_info['template'],true);
foreach ($template as $k => $v){
if($_GPC['template_id'] == $v['template_id']){
$send_data['title'] = $v['name'];
}
}
pdo_insert('template_sendLog',$send_data);
$sendedid = pdo_insertid();
foreach ($user as $key => $value){
$status = send_template_msg($uniacid,$template,$value);
if($status === true){
$i = $i + 1;
pdo_update("template_log",['number' => $value['number'] - 1],['id'=>$value['id']]);
}
}
$sended = [
'status' => 1,
'success' => $i,
'end_time' => TIMESTAMP,
];
pdo_update('template_sendLog',$sended,['id'=> $sendedid]);
echo '发送完成';
}else{
echo "暂无符合条件用户";
}
function send_template_msg($uniacid,$template,$user){
$account_api = WeAccount::create($uniacid);
$token = $account_api->getAccessToken();
$url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend?access_token={$token}";
$data['touser'] = $user['openid'];
$data['template_id'] = $user['template_id'];
foreach ($template as $key => $value){
if($value['template_id'] == $user['template_id']){
if($value['type'] == 1){
$data['page'] = ($value['jump_url']);
}elseif($value['type'] == 2){
$data['miniprogram']['appid'] = $value['appid'];
$data['miniprogram']['pagepath'] = $value['pages'];
}
foreach ($value[$value['template_id']] as $k => $v){
$data['data'][$k]['value'] = $v;
}
}
}
$response = ihttp_post($url,json_encode($data));
$result = json_decode($response['content'],true);
// 有一种情况是 已发送消息给 用户 用户在模版中 选择拒绝接收此类消息时 下次发送消息时 报错43101 因此设置此条消息可发送次数为0
// 当用户在已接收的模版中选择 接收此模版消息时则是永久接收 选择访问时则没有选择这个模版的选项 因此在add_log 方法中提交过来的数据 永久接收的消息数据为同意则当前模版可发送次数+1
if($result['errcode'] == 43101){
pdo_update('ims_template_log',['number' => 0],['id'=>$user['id']]);
}
if($result['errcode'] == 0){
return true;
}else{
var_dump($result);
return false;
}
}
有问题请联系我