赞
踩
目前微信小程序是没办法长期订阅的,除了什么教育、交通、医疗行业外。
那么想实现长期订阅,首先就要在开放平台绑定微信公众号和微信小程序。
用户注册小程序的同时,引导用户关注公众号。
如果用户不关注公众号,那么依然无法给用户长期发送消息
如果不适用于兄弟们,那就不用往下看了。
总结一下:
①:用户注册小程序,用户对于小程序的openid和unionid入库。
②:引导用户关注公众号,根据数据库中用户的unionid,更新用户对于公众号的openid信息。
③:公众号发送消息,需要用到用户在公众号中的openid。
④:公众号发送模板消息,然后在消息中跳转回小程序即可。
先普及一下知识。微信开放平台和微信公众平台是两个不同的平台。
微信开放平台上可以绑定公众号和小程序。
微信公众平台上可以选择公众号登录或者小程序登录,进而进入它们的管理后台。
公众号的appid和appsecret和小程序的不一样!
公众号的openid和小程序的openid不一样!
但是,绑定在同一个开放平台上的公众号的unionid和小程序的unionid是相同的。
获取用户基本信息
的权限。这个一定要有。以下代码,我们只需要看event事件触发时候的那一部分即可。
//路由 //发送公众号通知 Route::post('sendPublicMessage', 'home/Wechat/sendPublicMessage'); //公众号服务器配置以及回调接口 Route::any('subscribe', 'home/Wechat/subscribe'); //控制器代码-- Wechat类中 <?php use think\Db; use think\facade\Log; use think\Request; private $appId = 'your appid'; //公众号appid private $appSecret = 'your appsecret';//公众号appsecret class Wechat { /** * Description:获取token * @return mixed * @date 2024/4/26 14:12 * @author zls */ public function getAccessToken() { //获取access_token $tokenAccessUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}"; $access_token_json = file_get_contents($tokenAccessUrl); $access_token_array = json_decode($access_token_json, true); return $access_token_array['access_token'] ?: ''; } /** * Description:公众号回调接口 * @param Request $request * @return \think\response\Json * @date 2024/4/25 17:29 * @author zls */ public function subscribe(Request $request) { //接收参数,验签的标识 $echoStr = $request->param('echostr'); if (!empty($echoStr)) { //验签-配置服务器配置 $this->valid($request->param()); echo $echoStr; exit; } else { $this->responseMsg($request->param()); exit; } } /** * Description:公众号服务器验签 * @param $data * @return bool * @date 2024/4/26 9:40 * @author zls */ public function valid($data) { $echoStr = $data['echoStr']; $signature = $data['signature']; $timestamp = $data['timestamp']; $nonce = $data['nonce']; $token = "123456";//公众号后台自定义的token $tmpArr = array($token, $timestamp, $nonce); sort($tmpArr, SORT_STRING); $tmpStr = implode($tmpArr); $tmpStr = sha1($tmpStr); if ($tmpStr == $signature) { return true; //不可以在调用的方法中echo $echoStr; 必须要在主方法中echo; // echo $echoStr; // exit; } else { Log::info("验签失败"); return json(false); } } /** * Description:响应消息 * @return void * @date 2024/4/26 9:56 * @author zls */ public function responseMsg($data) { $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; if (!empty($postStr)) { $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); $RX_TYPE = trim($postObj->MsgType); //处理用户发送到公众号的各种类型的消息。 //其实我们只需要关注(event)事件接口即可!别的不用管也行。只是给大家普及一下。 switch ($RX_TYPE) { //关注、取关等事件 case "event": $result = $this->receiveEvent($postObj); break; //用户发送文字消息 case "text": $result = $this->receiveText($postObj); break; //用户发送图片消息 以下不再一一注释 case "image": $result = $this->receiveImage($postObj); break; case "location": $result = $this->receiveLocation($postObj); break; case "voice": $result = $this->receiveVoice($postObj); break; case "video": $result = $this->receiveVideo($postObj); break; case "link": $result = $this->receiveLink($postObj); break; default: $result = "unknown msg type: " . $RX_TYPE; break; } Log::info('Result' . $result); echo $result; } else { echo ""; exit; } } /** * Description:发送公众号消息推送 * @param Request $request * @return void * @date 2024/4/8 10:19 * @author zls */ public function sendPublicMessage(Request $request) { //获取access_token $access_token = $this->getAccessToken(); //接收者(用户)的OpenID $toUserOpenId = $request->param('openId'); //模板ID $templateId = $request->param('templateId'); //模板内容,这里以键值对形式表示 $data = array( 'thing1' => array('value' => '内容1', 'color' => '#173177'), 'thing4' => array('value' => '内容2', 'color' => '#173177'), 'time5' => array('value' => date('Y-m-d H:i:s', time()), 'color' => '#173177'), 'thing6' => array('value' => '内容2', 'color' => '#173177'), ); //模板其他参数-要跳转到哪个小程序 $miniProgram = array('appid' => '小程序的appid', 'pagepath' => 'pages/index/index'); $time = time(); $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=$access_token"; //组装发送的模板数据 $postData = array( 'touser' => $toUserOpenId, 'template_id' => $templateId, 'url' => "http://www.baidu.com", 'miniprogram' => $miniProgram, 'data' => $data, ); //对数组进行JSON编码 $postData = json_encode($postData, JSON_UNESCAPED_UNICODE); //发送模板消息 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $result = curl_exec($ch); if (curl_errno($ch)) { echo 'Error:' . curl_error($ch); } curl_close($ch); //输出返回结果 echo $result; } /** * Description:获取公众号中的用户信息 * @param $access_token * @param $open_id * @return mixed|string * @date 2024/4/26 14:25 * @author zls */ public function getPublicUserInfo($access_token, $open_id) { //获取access_token $userInfo = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=$access_token&openid=$open_id"; $userInfo_json = file_get_contents($userInfo); return json_decode($userInfo_json, true); } /** * Description:接收事件消息 * @param $object * @return false|string|null * @date 2024/4/26 13:59 * @author zls */ private function receiveEvent($object) { $result = ""; switch ($object->Event) { case "subscribe": //关注公众号事件 //获取token $access_token = $this->getAccessToken(); //获取用户的unionid $publicUserInfo = $this->getPublicUserInfo($access_token, $object->FromUserName); $unionId = $publicUserInfo['unionid']; //更新到用户表 $update_data['public_open_id'] = $object->FromUserName; $update_data['is_follow'] = 1; //是否关注 Db::table('user_table')->where(['weixin_unionid' => $unionId])->update($update_data); //欢迎语 $content = '感谢关注!'; //$content .= (!empty($object->EventKey)) ? ("\n来自二维码场景 " . str_replace("qrscene_", "", $object->EventKey)) : ""; break; case "unsubscribe": //取关事件 //获取token $access_token = $this->getAccessToken(); //获取用户的unionid $publicUserInfo = $this->getPublicUserInfo($access_token, $object->FromUserName); $unionId = $publicUserInfo['unionid']; //更新用户表的状态 $update_data['public_open_id'] = $object->FromUserName; $update_data['is_follow'] = 2; Db::table('user_table')->where(['weixin_unionid' => $unionId])->update($update_data); $content = "取消关注"; break; //下面的时间可以不用看了~ case "SCAN": $content = "扫描场景 " . $object->EventKey . "; 扫描value "; break; case "CLICK": switch ($object->EventKey) { case "COMPANY": $content = array(); $content[] = array("Title" => "多图文1标题", "Description" => "", "PicUrl" => "", "Url" => ""); break; default: $content = "点击菜单:" . $object->EventKey; break; } break; case "LOCATION": $content = "上传位置:纬度 " . $object->Latitude . ";经度 " . $object->Longitude; break; case "VIEW": $content = "跳转链接 " . $object->EventKey; break; case "MASSSENDJOBFINISH": $content = "消息ID:" . $object->MsgID . ",结果:" . $object->Status . ",粉丝数:" . $object->TotalCount . ",过滤:" . $object->FilterCount . ",发送成功:" . $object->SentCount . ",发送失败:" . $object->ErrorCount; break; default: $content = "" . $object->Event; break; } if (is_array($content)) { if (isset($content[0])) { $result = $this->transmitNews($object, $content); } else if (isset($content['MusicUrl'])) { $result = $this->transmitMusic($object, $content); } } else { $result = $this->transmitText($object, $content); } return $result; } //接收文本消息 private function receiveText($object) { $keyword = trim($object->Content); $content = ""; $result = ""; //根据key 查企业的登记信息 $sql = ""; $row = []; $defaulttxt = !empty($row) ? $row['default'] : ''; if (empty($row) || empty($row['register_code'])) { $content = "";//不提醒也不提示 } else { $register_code = $row['register_code']; $content = $row['welcome']; $default_company = $row['name']; //如果用户发送的关键词中含有注册俩字-则执行注册 //以下是一些注册的逻辑 表结构不一样,大家不看也罢~ if (strstr($keyword, '注册')) { //注册功能 $acc = str_replace('注册', '', $keyword); $account = trim($acc); $openId = trim($object->FromUserName); if (empty($account)) { $content = "抱歉,注册失败,格式不正确(1004). 请联系客服咨询."; } else { //首先检查当前微信号和注册账号是否已经注册过了 $sql = ""; $row = []; if (!empty($row) && $row['cust_id'] > 0) { if ($row['login_name1'] == $account && strlen($row['weixin_openid1']) > 5) { //已经注册过了 $content = $default_company . '欢迎你! 你的账号已经注册过(1001), 无需重复注册!'; } elseif ($row['login_name2'] == $account && strlen($row['weixin_openid2']) > 5) { //已经注册过了 $content = $default_company . '欢迎你! 你的账号已经注册过(1001), 无需重复注册!'; } else { if ($row['login_name1'] == $account) { //数据库更新用户信息操作--省略 $content = $default_company . '欢迎你! 注册成功!可以使用业主系统啦,请访问公众号菜单“客户登录” '; } elseif ($row['login_name2'] == $account) { //数据库更新用户信息操作--省略 $content = $default_company . '欢迎你! 注册成功!可以使用业主系统啦,请访问公众号菜单“客户登录”! 下次直接访问公众号菜单登录, 永久免密码登陆!'; } } } else { $content = "抱歉,注册失败(1000). 请联系客服咨询."; } } } elseif (strstr($keyword, $register_code)) { $acc = str_replace($register_code, '', $keyword); $content = "欢迎你注册" . $register_code . "企业员工账号!"; $account = trim($acc); $openId = $object->FromUserName; //首先检查当前微信号和注册账号是否已经注册过了; $sql = ""; $checkRow = []; if (!empty($checkRow) && $checkRow['user_id'] > 0) { if (strlen($checkRow['weixin_openid']) > 5) { $content = $default_company . '欢迎你! 你的登录账号已注册过, 无需重复注册(1004)'; } else { //此处最好获取用户的信息 //后续考虑 //数据库更新用户信息操作--省略 $content = $default_company . '欢迎你! 绑定员工账号成功!请访问公众号菜单“员工登录”! 下次直接访问公众号菜单登录, 永久免密码登陆!'; } } else { //账号不存在 $content = "抱歉,注册失败(1003). 账号不存在, 请联系公司人事专员咨询开通."; } if (empty($content)) { $content = ""; } } elseif (strstr($keyword, '工人')) { $acc = str_replace('工人', '', $keyword); $content = "欢迎你注册施工产业工人端口!"; $account = trim($acc); $openId = $object->FromUserName; //首先检查当前微信号和注册账号是否已经注册过了; $sql = ""; $checkRow = []; if (!empty($checkRow) && $checkRow['user_id'] > 0) { if (strlen($checkRow['weixin_openid']) > 5) { $content = $default_company . '欢迎你! 你的账号已注册过, 无需重复注册(1004)'; } else { //此处最好获取用户的信息 //后续考虑 //数据库更新用户信息操作--省略 $content = $default_company . '欢迎你! 登录账号绑定成功!请访问公众号菜单“工人帮”! 下次直接访问公众号菜单登录, 永久免密码登陆!'; } } else { //账号不存在 $content = "抱歉,注册失败(1003). 账号不存在, 请联系公司人事专员咨询开通."; } if (empty($content)) { $content = ""; } } elseif (strstr($keyword, '伙伴')) { //合作伙伴注册 $acc = str_replace('伙伴', '', $keyword); //$content = "欢迎你注册**会员, 注册账号是:".$acc.", from:".$object->FromUserName." to:".$object->ToUserName; $content = "欢迎你注册" . $default_company . "企业的合作伙伴会员!"; $account = trim($acc); $openId = $object->FromUserName; //用户微信ID:from //首先检查当前微信号和注册账号是否已经注册过了; //数据库操作--省略 if (!empty($checkRow) && $checkRow['sup_id'] > 0) { if ($checkRow['status'] == '0') { $content = '欢迎你! 你的伙伴账号已被冻结, 请联系公司人事专员咨询开通!'; } else { if (strlen($checkRow['weixin_openid1']) > 5) { $content = '欢迎你! 你的伙伴账号已经注册过, 请不要重复注册!'; } else { //此处最好获取用户的信息 //后续考虑 $serno = 'SUP-' . guid(); $GLOBALS['db']->query("UPDATE " . $GLOBALS['pms']->scmtable('supplier') . " SET weixin_openid1='" . $openId . "' WHERE sup_id='" . $checkRow['sup_id'] . "'"); $content .= '恭喜注册伙伴账号成功!现在可以正常使用合作伙伴业务系统啦! 下次直接访问公众号菜单登录, 永久免密码登陆!'; //创建新的staff信息 $supply_staff = array('ref_supply_id' => trim($checkRow['sup_id']), 'name' => $checkRow['name'], 'if_allow_weixin' => 1, 'login_name1' => $checkRow['login_name1'], 'weixin_openid1' => $openId, 'if_master' => 1, 'if_job' => 1); $GLOBALS['db']->autoExecute($GLOBALS['pms']->scmtable('supply_staff'), $supply_staff, 'INSERT'); } } } else { $content = $defaulttxt;//不符合提醒格式,给客户发一个默认消息 } } } if (is_array($content)) { if (isset($content[0]['PicUrl'])) { $result = $this->transmitNews($object, $content); } else if (isset($content['MusicUrl'])) { $result = $this->transmitMusic($object, $content); } } else { $result = $this->transmitText($object, $content); } return $result; } //接收图片消息 private function receiveImage($object) { $content = array("MediaId" => $object->MediaId); return $this->transmitImage($object, $content); } //接收位置消息 private function receiveLocation($object) { $content = "你发送的是位置,纬度为:" . $object->Location_X . ";经度为:" . $object->Location_Y . ";缩放级别为:" . $object->Scale . ";位置为:" . $object->Label; return $this->transmitText($object, $content); } //接收语音消息 private function receiveVoice($object) { if (isset($object->Recognition) && !empty($object->Recognition)) { $content = "你刚才说的是:" . $object->Recognition; $result = $this->transmitText($object, $content); } else { $content = array("MediaId" => $object->MediaId); $result = $this->transmitVoice($object, $content); } return $result; } //接收视频消息 private function receiveVideo($object) { $content = array("MediaId" => $object->MediaId, "ThumbMediaId" => $object->ThumbMediaId, "Title" => "", "Description" => ""); return $this->transmitVideo($object, $content); } //接收链接消息 private function receiveLink($object) { $content = "你发送的是链接,标题为:" . $object->Title . ";内容为:" . $object->Description . ";链接地址为:" . $object->Url; return $this->transmitText($object, $content); } //回复文本消息 private function transmitText($object, $content) { if (empty($content)) { return false; } $xmlTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[%s]]></Content> </xml>"; return sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time(), $content); } //回复图片消息 private function transmitImage($object, $imageArray) { $itemTpl = "<Image> <MediaId><![CDATA[%s]]></MediaId> </Image>"; $item_str = sprintf($itemTpl, $imageArray['MediaId']); $xmlTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[image]]></MsgType> $item_str </xml>"; return sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time()); } //回复语音消息 private function transmitVoice($object, $voiceArray) { $itemTpl = "<Voice> <MediaId><![CDATA[%s]]></MediaId> </Voice>"; $item_str = sprintf($itemTpl, $voiceArray['MediaId']); $xmlTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[voice]]></MsgType> $item_str </xml>"; return sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time()); } //回复视频消息 private function transmitVideo($object, $videoArray) { $itemTpl = "<Video> <MediaId><![CDATA[%s]]></MediaId> <ThumbMediaId><![CDATA[%s]]></ThumbMediaId> <Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description> </Video>"; $item_str = sprintf($itemTpl, $videoArray['MediaId'], $videoArray['ThumbMediaId'], $videoArray['Title'], $videoArray['Description']); $xmlTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[video]]></MsgType> $item_str </xml>"; return sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time()); } //回复图文消息 private function transmitNews($object, $newsArray) { if (!is_array($newsArray)) { return []; } $itemTpl = "<item> <Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description> <PicUrl><![CDATA[%s]]></PicUrl> <Url><![CDATA[%s]]></Url> </item>"; $item_str = ""; foreach ($newsArray as $item) { $item_str .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']); } $xmlTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[news]]></MsgType> <ArticleCount>%s</ArticleCount> <Articles>$item_str</Articles> </xml>"; return sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time(), count($newsArray)); } //回复音乐消息 private function transmitMusic($object, $musicArray) { $itemTpl = "<Music> <Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description> <MusicUrl><![CDATA[%s]]></MusicUrl> <HQMusicUrl><![CDATA[%s]]></HQMusicUrl> </Music>"; $item_str = sprintf($itemTpl, $musicArray['Title'], $musicArray['Description'], $musicArray['MusicUrl'], $musicArray['HQMusicUrl']); $xmlTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[music]]></MsgType> $item_str </xml>"; return sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time()); } //回复多客服消息 private function transmitService($object) { $xmlTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[transfer_customer_service]]></MsgType> </xml>"; return sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time()); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。