微信公众号及服务号开发
微信开发文档:https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html
微信公众号(a、服务号,b、订阅号)配置:
1.基本配置->设置appSecret
2.基本配置->设置IP白名单
3.设置->公众号设置->功能设置: 业务域名、JS接口安全域名、网页授权接口域名(仅a、服务号有该功能,该功能接口可实现基于微信用户(未关注公众号的用户授权登录(静默,非静默)也可以拿到该用户的基本信息,及openid,但手机号拿不到,微信接口不给)的oauth2授权登录),将一个txt文件放在域名根目录,并且可在浏览器打开
4.基本配置->服务器配置->启用
服务器地址(URL) :该地址为唯一的与该微信公众号的token验证及消息接、转、发接口
http://shgfdgda.com/api/wx/check_sign_dyh
令牌(Token) : 用于第一次接口与微信公众号的token验证,消息解密
shaezd
消息加解密密钥(EncodingAESKey)
fdfddsssdfdggtgdrfvfrdsfdsdsssssssf
5.设置->公众号设置->授权管理 : 第三方平台授权与自己的开发者模式不冲突,且一个公众号可授权多个平台开发
6.公众号开发者模式可调用公众平台上面的图文、video等资源
7.服务号和订阅号,每次用户主动发消息或触发菜单,都会回传FromUserName(即openid),根据openid可获取unionid及用户基本信息;
WeChat.php公共基类
<?php namespace app\common;
/** * 微信公众平台操作类 */
class WeChat { private $_appid; private $_appsecret; //微信公众平台请求开发者的服务器需要token private $_token; //标识qrcodeticket的类型,是永久还是临时 const
QRCODE_TYPE_TEMP
= 1; const
QRCODE_TYPE_TEMP_STR
= 2; const
QRCODE_TYPE_LIMIT
= 3; const
QRCODE_TYPE_LIMIT_STR
= 4;
/** * 构造函数 *
@param
string $id appid *
@param
string $secret app秘钥 */
public function __construct($id, $secret, $token) { $this->_appid = $id; $this->_appsecret = $secret; $this->_token = $token; }
/** * 用于第一次验证URl合法性 */
public function firstValid() { //校验签名的合法性 if ($this->_checkSignature()) { //签名合法,告知微信服务器 echo $_GET['echostr']; } }
/** * 验证签名 *
@return
[type] [description] */
private function _checkSignature() { //获取由微信服务器发过来的数据 $signature = $_GET['signature']; $timestamp = $_GET['timestamp']; $nonce = $_GET['nonce']; //开始验证数据 $tmp_arr = array($this->_token, $timestamp, $nonce); sort($tmp_arr,
SORT_STRING
); $tmp_str = implode($tmp_arr); $tmp_str = sha1($tmp_str); //对比数据 if ($signature == $tmp_str) { return true; } else { return false; } }
/** * 消息类型判断 *
@return
array */
public function responseMsg() { //因为很多都设置了register_globals禁止,不能用$GLOBALS["HTTP_RAW_POST_DATA"] 改用file_get_contents("php://input")即可 $postStr = file_get_contents("php://input"); if (!empty($postStr)) { $postObj = $this->object_to_array(simplexml_load_string($postStr, 'SimpleXMLElement',
LIBXML_NOCDATA
)); $RX_TYPE = trim($postObj['MsgType']); //用户发送的消息类型判断 switch ($RX_TYPE) { case "text": //文本消息 return array('type' => 'text', 'msg' => '文本', 'obj' => $postObj); break; case "image": //图片消息 return array('type' => 'image', 'msg' => '图片', 'obj' => $postObj); break; case "voice": //语音消息 return array('type' => 'voice', 'msg' => '语音', 'obj' => $postObj); break; case "video": //视频消息 return array('type' => 'video', 'msg' => '视频', 'obj' => $postObj); break; case "location"://位置消息 return array('type' => 'location', 'msg' => '位置', 'obj' => $postObj); break; case "link": //链接消息 return array('type' => 'link', 'msg' => '链接', 'obj' => $postObj); break; case "event": //触发事件 return array('type' => 'event', 'msg' => '事件', 'obj' => $postObj); break; default: return array('type' => 'unknow msg type', 'msg' => '未知', 'obj' => $postObj); break; } } else { echo ""; exit; } }
/** * 获取 access_tonken值 *
@param
string $token_file 用来存储的文件 *
@return
access_token */
public function getAccessToken($token_file = './access_token') { //处理是否过期问题,将access_token存储到文件 $life_time = 7200; if (file_exists($token_file) && time() - filemtime($token_file) < $life_time) { // 存在有效的access_token 直接返回文件内容 return file_get_contents($token_file); } //接口URL $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $this->_appid . "&secret=" . $this->_appsecret; //发送GET请求 $result = $this->_request('get', $url); if (!$result) { return false; } //处理数据 $result_obj = json_decode($result); //写入到文件 file_put_contents($token_file, $result_obj->access_token); return $result_obj->access_token; }
/** * 获取Ticket *
@param
string $content 二维码内容 *
@param
int $type 二维码类型 1 临时整形 2临时字符串 3永久整形 4永久字符串 *
@param
int $expire 有效时间 *
@return
ticket */
public function getQRCodeTicket($content, $type = 2, $expire = 604800) { $access_token = $this->getAccessToken(); $url = 'https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=' . $access_token; $type_list = array( self::
QRCODE_TYPE_TEMP
=> 'QR_SCENE', self::
QRCODE_TYPE_TEMP_STR
=> 'QR_STR_SCENE', self::
QRCODE_TYPE_LIMIT
=> 'QR_LIMIT_SCENE', self::
QRCODE_TYPE_LIMIT_STR
=> 'QR_LIMIT_STR_SCENE' ); $action_name = $type_list[$type]; //post发送的数据 switch ($type) { case self::
QRCODE_TYPE_TEMP
: $data_arr['expire_seconds'] = $expire; $data_arr['action_name'] = $action_name; $data_arr['action_info']['scene']['scene_id'] = $content; break; case self::
QRCODE_TYPE_TEMP_STR
: $data_arr['expire_seconds'] = $expire; $data_arr['action_name'] = $action_name; $data_arr['action_info']['scene']['scene_str'] = $content; break; case self::
QRCODE_TYPE_LIMIT
: $data_arr['action_name'] = $action_name; $data_arr['action_info']['scene']['scene_id'] = $content; break; case self::
QRCODE_TYPE_LIMIT_STR
: $data_arr['action_name'] = $action_name; $data_arr['action_info']['scene']['scene_str'] = $content; break; } $data = json_encode($data_arr); $result = $this->_request('post', $url, $data); if (!$result) { return false; } $result_obj = json_decode($result); return $result_obj->ticket; }
/** * 根据ticket获取二维码 *
@param
int|string $content qrcode内容标识 *
@param
[type] $file 存储为文件的地址,如果null直接输出 *
@param
integer $type 类型 *
@param
integer $expire 如果是临时,标识有效期 *
@return
[type] */
public function getQRCode($content, $file = NULL, $type = 2, $expire = 604800) { //获取ticket $ticket = $this->getQRCodeTicket($content, $type = 2, $expire = 604800); $url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=$ticket"; //发送,取得图片数据 $result = $this->_request('get', $url); if ($file) { file_put_contents($file, $result); } else { header('Content-Type:image/jpeg'); echo $result; } }
/** * 封装发送http请求 *
@param
string $method 请求方式 get/post *
@param
string $url 请求目标的url *
@param
array $data post发送的数据 *
@param
bool $ssl 是否为https协议 *
@return
响应主体 */
private function _request($method = 'get', $url, $data = array(), $ssl = true) { //curl完成,先开启curl模块 //初始化一个curl资源 $curl = curl_init(); //设置curl选项 curl_setopt($curl,
CURLOPT_URL
, $url);//url //请求的代理信息 $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0 FirePHP/0.7.4'; curl_setopt($curl,
CURLOPT_USERAGENT
, $user_agent); //referer头,请求来源 curl_setopt($curl,
CURLOPT_AUTOREFERER
, true); curl_setopt($curl,
CURLOPT_TIMEOUT
, 30);//设置超时时间 //SSL相关 if ($ssl) { //禁用后,curl将终止从服务端进行验证; curl_setopt($curl,
CURLOPT_SSL_VERIFYPEER
, false); //检查服务器SSL证书是否存在一个公用名 curl_setopt($curl,
CURLOPT_SSL_VERIFYHOST
, 2); } //判断请求方式post还是get if (strtolower($method) == 'post') {
/**************处理post相关选项******************/
//是否为post请求 ,处理请求数据 curl_setopt($curl,
CURLOPT_POST
, true); curl_setopt($curl,
CURLOPT_POSTFIELDS
, $data); } //是否处理响应头 curl_setopt($curl,
CURLOPT_HEADER
, false); //是否返回响应结果 curl_setopt($curl,
CURLOPT_RETURNTRANSFER
, true); //发出请求 $response = curl_exec($curl); if (false === $response) { echo '<br>', curl_error($curl), '<br>'; return false; } //关闭curl curl_close($curl); return $response; } //对象转数组 private function object_to_array($object) { $object = json_decode( json_encode( $object),true); return $object; } }
控制器微信消息收发代码Wx.php
<?php namespace app\td_api\controller; use app\common\DesUtils; use app\common\RedisCache; use app\common\SimpleLog; use app\common\WeChat; use think\Db; //数据采集 class Wx extends Base { public function _initialize() { parent::
_initialize
(); //方法token解禁 $this->beforeActionList = [ 'api_des_decrypt' => ['except' => 'online_consulting,qm_get_msg,get_media,get_media_list,check_signature,check_signature_dyh,send_msg,get_menu,set_menu,test'], //api_des_decrypt 必须作为第一个前置方法,位置不能跟其他的替换 'checkToken' => ['except' => 'get_openid,online_consulting,qm_get_msg,get_media,get_media_list,check_signature,check_signature_dyh,send_msg,get_menu,set_menu,test'], 'check_params' => ['except' => 'online_consulting,qm_get_msg,get_media,get_media_list,check_signature,check_signature_dyh,send_msg,get_menu,set_menu,test'], ]; } //服务号配置的基本信息 private $appid = ''; private $app_secret = ''; private $token = ''; private $qm_token = ''; //订阅号配置的基本信息 private $dyh_appid = ''; private $dyh_app_secret = ''; private $dyh_token = ''; private $dyh_qm_token = '';
/** * 根据code获取openid,并且根据openid获取unionid */
public function get_openid() { $params = $this->k;//request()->param(); $s_td_c_id = empty($params['s_td_c_id']) ? 1 : $params['s_td_c_id']; $td_channel_event = Db::name('td_channel_event')->where('channel_type', $s_td_c_id)->find(); //$log = new SimpleLog('get_openid'); //$log->write_log('channel_type:' . $td_channel_event['channel_type']); //$log->write_log($params); if ($td_channel_event['channel_type'] == 1) { //服务号 if (empty($params['code'])) { return $this->api_des_encrypt('', 'code不能为空', 0); //return ['code'=>0,'msg'=>'code不能为空']; } $appid = $this->appid; $app_secret = $this->app_secret; $userinfo = $this->get_userinfo($appid, $app_secret, 1, $params['code']); //$log->write_log($userinfo); return $this->api_des_encrypt($userinfo['data'], $userinfo['msg'], $userinfo['code']); } elseif ($td_channel_event['channel_type'] == 2) { //订阅号 $appid = $this->dyh_appid; $app_secret = $this->dyh_app_secret; if (empty($params['openId'])) { return $this->api_des_encrypt('', 'openId不能为空', 0); //return ['code'=>0,'msg'=>'code不能为空']; } $userinfo = $this->get_userinfo($appid, $app_secret, 2, '', $params['openId']); //$log->write_log($userinfo); return $this->api_des_encrypt($userinfo['data'], $userinfo['msg'], $userinfo['code']); } //$log = new SimpleLog('get_openid'); //$log->write_log($params); //auth2返回的access_token,2小时内登录有效,超过2小时需要重新刷新获取 /* $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid . '&secret=' . $app_secret . '&code=' . $params['code'] . '&grant_type=authorization_code'; $res = json_decode(curl_request($url), true); //$log->write_log($res); if (empty($res['openid'])) { return $this->api_des_encrypt('', 'code错误', 0); //return ['code'=>0,'msg'=>'code错误']; } //获取普通的openid $pt_access_token = $this->get_token(); $union_url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' . $pt_access_token . '&openid=' . $res['openid'] . '&lang=zh_CN'; $union_request = curl_request($union_url); //$log->write_log($union_request); if ($union_request) { return $this->api_des_encrypt(json_decode($union_request, true), 'success', 1); //return ['code'=>1,'msg'=>'success','data'=>json_decode($union_request,true)]; } else { return $this->api_des_encrypt('', 'err', 0); //return ['code'=>0,'msg'=>'err']; }*/ } //服务号根据code网页授权获取openid private function get_oauth_openid($code) { //服务号 if (empty($code)) { return false; } $appid = $this->appid; $app_secret = $this->app_secret; $userinfo = $this->get_userinfo($appid, $app_secret, 1, $code); $openid = empty($userinfo['data']['openid']) ? '' : $userinfo['data']['openid']; return $openid; } //获取用户信息及unionId,服务号可根据oauth2的access_token获取未关注该公众号的用户信息,即通过oauth2授权第三方网页登录并获取微信用户信息 private function get_userinfo($appid, $app_secret, $channel_type = 1, $code = '', $open_id = '') { if ($channel_type == 1) { //服务号 if (empty($code)) { return ['code' => 0, 'msg' => 'code不能为空', 'data' => '']; } //$log =new SimpleLog('get_userinfo'); //oauth2返回的access_token,2小时内登录有效,超过2小时需要重新刷新获取 $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid . '&secret=' . $app_secret . '&code=' . $code . '&grant_type=authorization_code'; $res = json_decode(curl_request($url), true); //$log->write_log('----access_token----'); //$log->write_log($res); if (empty($res['openid'])) { //return $this->api_des_encrypt('', 'code错误', 0); return ['code' => 0, 'msg' => 'code错误', 'data' => '']; } $access_token = $res['access_token']; $open_id = $res['openid']; $refresh_token = $res['refresh_token']; //检验oauth2授权凭证access_token是否有效 $check_auth_url = 'https://api.weixin.qq.com/sns/auth?access_token=' . $access_token . '&openid=' . $open_id; $check_auth = json_decode(curl_request($check_auth_url), true); //$log->write_log($check_auth); if ($check_auth['errcode'] != 0) { //已失效,刷新 $refresh_token_url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=' . $appid . '&grant_type=refresh_token&refresh_token=' . $refresh_token; $res = json_decode(curl_request($refresh_token_url), true); //$log->write_log('----refresh_token----'); //$log->write_log($res); $access_token = $res['access_token']; $open_id = $res['openid']; //$refresh_token = $res['refresh_token']; //更新access_token 和 refresh_token } //获取code的接口及参数"https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; //如果获取code的scope=snsapi_base(静默模式,不弹出授权页面,直接跳转,只能获取用户openid),这时下面的接口报错,返回{"errcode":48001,"errmsg":"api unauthorized, hints: [ req_id: tGpFvXmoRa-bPuQfa ]"} // 只有 scope=snsapi_userinfo 才能用下面的接口获取用户信息, /*$userinfo_url = 'https://api.weixin.qq.com/sns/userinfo?access_token=' . $access_token . '&openid=' . $open_id . '&lang=zh_CN'; $userinfo = json_decode(curl_request($userinfo_url), true); //$log->write_log('----userinfo----'); //$log->write_log($userinfo); if ($userinfo) { return ['code' => 1, 'msg' => 'success', 'data' => json_decode($userinfo, true)]; } else { //return $this->api_des_encrypt('', 'err', 0); return ['code' => 0, 'msg' => 'err', 'data' => '']; }*/ } elseif ($channel_type == 2) { //订阅号 if (empty($open_id)) { return ['code' => 0, 'msg' => 'openid不能为空', 'data' => '']; } } //获取普通的unionid $pt_access_token = $this->get_token(0, $channel_type); $union_url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' . $pt_access_token . '&openid=' . $open_id . '&lang=zh_CN'; $union_request = curl_request($union_url); //$log->write_log($union_request); if ($union_request) { //return $this->api_des_encrypt(json_decode($union_request, true), 'success', 1); return ['code' => 1, 'msg' => 'success', 'data' => json_decode($union_request, true)]; } else { //return $this->api_des_encrypt('', 'err', 0); return ['code' => 0, 'msg' => 'err', 'data' => '']; } } //服务号服务器配置验证接口 /*public function check_signature() { //header('content-type:text'); // 第三方收到公众号平台发送的消息 //$log =new SimpleLog('wx_message'); //$log->write_log($_GET); $signature = $_GET["signature"]; $timestamp = $_GET["timestamp"]; $nonce = $_GET["nonce"]; $token = 'shauetd'; $tmpArr = array($token, $timestamp, $nonce); sort($tmpArr, SORT_STRING); $tmpStr = implode($tmpArr); $tmpStr = sha1($tmpStr); //$log->write_log($tmpStr); if ($tmpStr == $signature) { //$log->write_log($_GET['echostr']); return (int)$_GET['echostr']; } else { //$log->write_log('err'); return false; } }*/
/** * 服务号服务器配置token验证接口及消息接收响应唯一接口 */
public function check_signature() { /*$params = request()->param();//request()->param(); $type = $params['type']; //服务号 $appid = $this->appid; $app_secret = $this->app_secret; $token = $this->token;*/ //订阅号 $appid = $this->appid; $app_secret = $this->app_secret; $token = $this->token; //微信公众平台配置开发者服务器信息的时候填的URL,第一次配置需要把echostr回传给微信,验证开发者服务器接收消息的接口可正常对外访问 if (isset($_GET['echostr'])) { if ($this->checkSignature()) { echo $_GET['echostr']; exit; } } $wx = new WeChat($appid, $app_secret, $token); //判断消息类型 $result = $wx->responseMsg(); //$log = new SimpleLog('check_signature'); //$log->write_log($result); //图文id $media_id = 'wD1UC-jxH7MPrDmL62shO4xFtW1Oqz7xC3whi7AIRSQ'; $media = $this->get_media($media_id, 1); $redirect = urlencode('http://bc.xgs521.com/index.html/?s_td_c_id=1'); $media['tz'] = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; //"https://ykf-webchat.7moor.com/wapchat.html?accessId=29614f30-4fff-11eb-b9d6-bbf82f53d9cd&fromUrl=http://www.baidu.com&urlTitle=AI智能实验室&language=ZHCN&otherParams={\"peerId\":\"10049041\"}" //$log->write_log($result['obj']->FromUserName); //用户的openid //$log->write_log($result['obj']->FromUserName[0]); //用户的openid //$log->write_log($result['obj']['EventKey']); //$this->transmitText($result['obj'], 'http://bc.td.pdvm.shuhengio.com/?s_td_c_id=4&openid='.$result['obj']->FromUserName); //$result['type'] == 'text' $this->event_produce($result, 1); //事件入库及业务逻辑处理 //回复各种类型的消息,后面不会再执行任何代码 if ($result['type'] == 'text') { //$qm_resp = $this->qm_send_msg($result['obj'], $this->appid, $this->qm_token); //$log->write_log('----------------------发送给七陌的消息得到的响应-----------------------'); //$log->write_log($qm_resp); echo ''; exit; } elseif ($result['type'] == 'event') { //事件 if ($result['obj']['Event'] == 'CLICK') { //点击事件 if (!empty($result['obj']['EventKey']) && $result['obj']['EventKey'] == 'tuijiancai') { $this->transmitText($result['obj'], 'http://bc.xgs521.com/index.html?s_td_c_id=1'); }if (!empty($result['obj']['EventKey']) && $result['obj']['EventKey'] == 'tuijiancai4') { $this->transmitText($result['obj'], 'http://bc.xgs521.com/index2.html?s_td_c_id=1'); } else { $this->transmitNews($result['obj'], $media); } } } echo ''; exit; //$log->write_log('end'); } //七陌第三方客服接收消息 private function qm_send_msg($res, $appid, $qm_token) { //$log = new SimpleLog('qm_send_msg'); $array = $this->qm_make_signature($res, $appid, $qm_token); //$log->write_log($array); $url = "https://ykf-wechat.7moor.com/Icallback"; $json_str = json_encode($array,
JSON_UNESCAPED_UNICODE
); //eblog("请求报文",$json_str,'test_'.date("Ymd"),'test/'); $head = array( 'Content-Type: application/json; charset=utf-8', 'Content-Length:' . strlen($json_str) ); $curl = curl_init(); curl_setopt($curl,
CURLOPT_URL
, $url); curl_setopt($curl,
CURLOPT_HEADER
, true); //类型为json curl_setopt($curl,
CURLOPT_HTTPHEADER
, array( 'Content-Type: application/json; charset=utf-8', 'Content-Length:' . strlen($json_str) ) ); //post传递 curl_setopt($curl,
CURLOPT_POST
, 1); curl_setopt($curl,
CURLOPT_POSTFIELDS
, ($json_str)); curl_setopt($curl,
CURLOPT_RETURNTRANSFER
, 1); $ress = curl_exec($curl); $errorno = curl_errno($curl); curl_close($curl); return $ress; //return $result = curl_request($url,'post',$array,$head,0); } //服务号获取七陌客服回复的消息 public function qm_get_msg() { $param = request()->param(); //$log = new SimpleLog('qm_get_msg'); //$log->write_log($param); return true; } //订阅号获取七陌客服回复的消息 /*public function dyh_qm_get_msg(){ $array['msg_signature']=$this->qm_make_signature($res,$appid,$qm_token); $url="http://wechat.7moor.com/Icallback"; $json_str=json_encode($array,JSON_UNESCAPED_UNICODE); //eblog("请求报文",$json_str,'test_'.date("Ymd"),'test/'); $head =array( 'Content-Type: application/json; charset=utf-8', 'Content-Length:' . strlen($json_str) ); return $result = curl_request($url,'post',$array,$head); }*/ private function qm_make_signature($res, $appid, $qm_token) { $nonce = rand(100000, 999999); $array = array( 'app_id' => $appid, 'open_id' => $res['FromUserName'], 'nick_name' => $res['FromUserName'], 'msgType' => $res['MsgType'], 'content' => $res['Content'], 'timestamp' => $res['CreateTime'], 'nonce' => intval($nonce), ); $mix_arr = array( 'open_id' => $res['FromUserName'], 'timestamp' => $res['CreateTime'], 'nonce' => intval($nonce), 'token' => $qm_token, ); $mix_arrs = array_combine($mix_arr, $mix_arr); ksort($mix_arrs, 2); $sign_string = ''; foreach ($mix_arrs as $val) { $sign_string .= $val; } //eblog("签名串",$sign_string,'test_'.date("Ymd"),'test/'); //$new_sign = strtoupper(sha1($sign_string)); $new_sign = sha1($sign_string); $array['msg_signature'] = $new_sign; return $array; }
/** * 订阅号服务器配置token验证接口及消息接收响应唯一接口 */
public function check_signature_dyh() { /*$params = request()->param();//request()->param(); $type = $params['type']; //服务号 $appid = $this->appid; $app_secret = $this->app_secret; $token = $this->token;*/ //订阅号 $appid = $this->dyh_appid; $app_secret = $this->dyh_app_secret; $token = $this->dyh_token; //微信公众平台配置开发者服务器信息的时候填的URL,第一次配置需要把echostr回传给微信,验证开发者服务器接收消息的接口可正常对外访问 if (isset($_GET['echostr'])) { if ($this->checkSignature()) { echo $_GET['echostr']; exit; } } $wx = new WeChat($appid, $app_secret, $token); //判断消息类型 $result = $wx->responseMsg(); //$log = new SimpleLog('check_signature_dyh'); //$log->write_log($result); //$log->write_log(object_to_array($result)); //图文id $media_id = 'PQfyghqX3zdaADzWLRAvYwOYY_H8_ZpqyOvK6CsxrMM'; $media = $this->get_media($media_id, 2); //openid必须拼接上去 $media['tz'] = 'http://bc.xgs521.com/index.html?s_td_c_id=4&openid=' . $result['obj']['FromUserName']; //$log->write_log($result['obj']['FromUserName']); $this->event_produce($result, 2); //$result['obj']->FromUserName 用户的openid //$this->transmitText($result['obj'], 'http://bc.td.pdvm.shuhengio.com/?s_td_c_id=4&openid='.$result['obj']->FromUserName); //回复各种类型的消息,后面不会再执行任何代码,view类型会自动跳转 if ($result['type'] == 'text') { echo ''; exit; } elseif ($result['type'] == 'event') { //事件 if ($result['obj']['Event'] == 'CLICK') { //点击事件 if (!empty($result['obj']['EventKey']) && $result['obj']['EventKey'] == 'tuijiancai') { //$this->transmitText($result['obj'], 'http://bc.xgs521.com/index.html?s_td_c_id=4'); //openid必须拼接上去 $index = 'http://bc.xgs521.com/index.html?s_td_c_id=4&openid=' . $result['obj']['FromUserName']; $media['tz'] = $index; $this->transmitNews($result['obj'], $media); }elseif (!empty($result['obj']['EventKey']) && $result['obj']['EventKey'] == 'tuijiancai4') { //$this->transmitText($result['obj'], 'http://bc.xgs521.com/index2.html?s_td_c_id=4'); //openid必须拼接上去 $index = 'http://bc.xgs521.com/index2.html?s_td_c_id=4&openid=' . $result['obj']['FromUserName']; $media['tz'] = $index; $this->transmitNews($result['obj'], $media); }elseif (!empty($result['obj']['EventKey']) && $result['obj']['EventKey'] == 'zaixianzixun') { //在线咨询文章 $media_id = 'PQfyghqX3zdaADzWLRAvY4S0AuW4AuJhCjLCJrk37_M'; $media = $this->get_media($media_id, 2); // $customer_info = profile('','',$result['obj']['FromUserName']); $customer_id = $customer_info['customer_id']; $name = $customer_id.'_'.time(); $url = 'https://wx.td.shuhengio.com/public/static/img/'.$customer_id.'/'.$name.'.png'; $data = []; $data['customer_id'] = $customer_id; $data['name'] = $name; $data['nonce'] = rand(100000,999999); $data['time'] = time(); $curl = curl_request('http://wx.td.shuhengio.com/td_api/image_produce/merge_picture','post',$data); //$log->write_log($curl); $zaixianzixun = "https://ykf-webchat.7moor.com/wapchat.html?accessId=29614f30-4fff-11eb-b9d6-bbf82f53d9cd&fromUrl=http://www.baidu.com&urlTitle=AI智能实验室&language=ZHCN&otherParams={\"peerId\":\"10049041\"}&customField={\"openid\":\"" . $result['obj']['FromUserName'] . "\",\"img\":\"" . $url . "\"}"; //$log->write_log($zaixianzixun); //$zaixianzixun = "https://ykf-webchat.7moor.com/wapchat.html?accessId=29614f30-4fff-11eb-b9d6-bbf82f53d9cd&fromUrl=http://www.baidu.com&urlTitle=AI智能实验室&language=ZHCN&otherParams={\"peerId\":\"10049041\"}&customField={\"openid\":\"" . $result['obj']['FromUserName'] . "\"}"; $media['tz'] = $zaixianzixun; $this->transmitNews($result['obj'], $media); } else { $this->transmitNews($result['obj'], $media); } } } echo ''; exit; //$this->transmitNews($result['obj'], $media); //$log->write_log('end'); /*if ($result['type'] == 'text') { $this->transmitText($result['obj'], 'http://bc.td.pdvm.shuhengio.com?s_td_c_id=4&openid='.$result['obj']->FromUserName); //文本消息 if (strpos($result['obj']->Content, '潜力') !== false) { $time = time(); $code = []; $code['code'] = substr($time, -6); //Db::name("weixin_code")->save($code); $this->transmitText($result['obj'], '你的随机验证码为:' . $code['code']); } else { $this->transmitText($result['obj'], '您获取验证码信息不正确,请回复:获取潜力值'); } }*/ }
/** * 根据消息类型操作表 */
private function event_produce($result, $wx_type = 1, $type = 2, $count = 1) { //$log = new SimpleLog('event_produce'); //$log->write_log('--------------开始---------------'); $open_id = $result['obj']['FromUserName']; $event_content = ''; //事件描述 //$log->write_log($result); $event = ''; if ($result['obj']['MsgType'] == 'event') { if ($result['obj']['Event'] == 'subscribe' || $result['obj']['Event'] == 'unsubscribe') { $event_content = $result['obj']['Event']; } elseif ($result['obj']['Event'] == 'LOCATION') { $event_content = 'LOCATION';//$result['obj']['Latitude'].'*'.$result['obj']['Longitude'].':'.$result['obj']['Precision'] }elseif ($result['obj']['Event'] == 'VIEW') { $event = 'VIEW'; $event_content = $result['obj']['EventKey'];//$result['obj']['Latitude'].'*'.$result['obj']['Longitude'].':'.$result['obj']['Precision'] } else { $event_content = $result['obj']['EventKey']; } } //$log->write_log($event_content); //用户发的消息暂时不处理 /*elseif ($result['obj']->MsgType == 'text'){ $event_content=$result['obj']->Content; //事件描述 }*/ if (empty($event_content)) { return 0; } $profile = profile('', '', $open_id); $customer_id = $profile['customer_id']; //$log->write_log($customer_id); $event_time = time(); return $this->produce_event_type($event_content, $customer_id, $event_time, $count, $type, $wx_type,$event); } //处理时间类型入库 private function produce_event_type($id, $customer_id, $event_time, $count = 1, $type = 1, $wx_type = 0,$event='') { //$log = new SimpleLog('produce_event_type'); //$log->write_log('--------------开始---------------'); //$log->write_log($id); //$log->write_log('--------------customer_id---------------'); //$log->write_log($customer_id); //$log->write_log($type); //$log->write_log($wx_type); if ($type == 1) { //web端 //在规则表种找到该元素事件对应的处理规则 $regular = Db::name('td_page_element_event')->where('id', $id)->where('wx_type', $wx_type)->select(); } elseif ($type == 2) { //微信端,无customer_id,有open_id if($event == 'VIEW'){ $regulars = Db::name('td_wx_event')->where('event','VIEW')->where('wx_type', $wx_type)->select(); $regular = []; foreach ($regulars as $k=>$v){ if(false !== strpos($id,$v['event_content'])){ $regular[] = $v; } } }else{ $regular = Db::name('td_wx_event')->where('event_content',$id)->where('wx_type', $wx_type)->select(); } } //$log->write_log($regular); if (!$regular) { return 0; } foreach ($regular as $k=>$v){ //$log->write_log('--------------td_wx_event---------------'); $arr = []; $arr['event_type'] = $v['event_content']; $arr['begin_time'] = $event_time; $arr['end_time'] = $event_time; $time = date('Y-m-d H:i:s', time()); $data = []; if ($v['operation'] == 1) { //action新增 $data['type'] = $v['operation_type']; $data['name'] = $v['operation_name']; $data['action_id'] = $v['operation_type'] . '-' . $v['operation_name']; $data['article_id'] = $v['article_id']; $data['article_name'] = $v['article_name']; $data['customer_id'] = $customer_id; $data['create_time'] = $time; $data['update_time'] = $time; $action_id = Db::name('action')->insertGetId($data); $arr['new_data'] = $data; $arr['operation_type'] = 2; $arr['operation'] = 1; $arr['sign_id'] = $action_id; $arr['count'] = $v['count']; $arr['element_id'] = $id; $arr['type'] = 2; $this->data_log($arr); } elseif ($v['operation'] == 2) { //tag新增,先查看数据库中是否存在唯一信息,有就加 $weight = json_decode($v['weight'], true); //$log->write_log($data); if (empty($weight[0])) { continue; } $tag = Db::name('tag')->where('tag_id', $v['operation_name'])->where('customer_id', $customer_id)->find(); if ($tag) { $arr['old_data'] = $data; //更新 if (false !== strpos($weight[0], '+')) { $data['weight'] = (int)$tag['weight'] + intval($weight[0]); } else { $data['weight'] = intval($weight[0]); } $data['count'] = $tag['count'] + $v['count']; //$log->write_log($data); Db::name('tag')->where('tag_id', $v['operation_name'])->where('customer_id', $customer_id)->update(['count' => $data['count'], 'weight' => $data['weight']]); $arr['new_data'] = $data; $arr['operation_type'] = 1; $arr['operation'] = 2; $arr['sign_id'] = $tag['id']; $arr['count'] = $count; $arr['element_id'] = $id; $arr['type'] = 2; $this->data_log($arr); continue; } $data['tag_id'] = $v['operation_name']; $data['name'] = $v['operation_name']; $data['type'] = $v['operation_type']; $data['count'] = intval($v['count']); $data['weight'] = intval($weight[0]); $data['is_valid'] = 1; $data['customer_id'] = $customer_id; $data['create_time'] = $time; $data['update_time'] = $time; $tag_id = Db::name('tag')->insertGetId($data); $arr['new_data'] = $data; $arr['operation_type'] = 2; $arr['operation'] = 2; $arr['sign_id'] = $tag_id; $arr['count'] = $count; $arr['element_id'] = $id; $arr['type'] = 2; $this->data_log($arr); } //$data = $regular; //根据处理规则入库 //$log->write_log('--------------data---------------'); //$log->write_log($data); } return 1; } //验证签名 public function checkSignature() { $signature = $_GET["signature"]; $timestamp = $_GET["timestamp"]; $nonce = $_GET["nonce"]; $token = $this->token; $tmpArr = array($token, $timestamp, $nonce); sort($tmpArr); $tmpStr = implode($tmpArr); $tmpStr = sha1($tmpStr); if ($tmpStr == $signature) { return true; } else { return false; } }
/** * 接收文本消息 */
public function receiveText($object) { $content = "你发送的是文本,内容为:" . $object['Content']; $result = $this->transmitText($object, $content); return $result; }
/** * 回复文本消息 */
public function transmitText($object, $content) { $textTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[%s]]></Content> </xml>"; $result = sprintf($textTpl, $object['FromUserName'], $object['ToUserName'], time(), $content); //file_put_contents('./test.txt', $result); echo $result; die; }
/** * 回复图文消息 */
public function transmitNews($object, $news) { $textTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[news]]></MsgType> <ArticleCount>1</ArticleCount> <Articles> <item> <Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description> <PicUrl><![CDATA[%s]]></PicUrl> <Url><![CDATA[%s]]></Url> </item> </Articles> </xml>"; $result = sprintf($textTpl, $object['FromUserName'], $object['ToUserName'], time(), $news['title'], $news['digest'], $news['thumb_url'], $news['tz']); echo $result; die; }
/** * 回复图片消息 */
public function transmitImg($object, $img_id) { $textTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Image> <MediaId><![CDATA[%s]]></MediaId> </Image> </xml>"; $result = sprintf($textTpl, $object['FromUserName'], $object['ToUserName'], time(), $img_id); echo $result; die; } //获取素材详细信息,type=1服务号,$type=2订阅号 media_id =PQfyghqX3zdaADzWLRAvYwOYY_H8_ZpqyOvK6CsxrMM private function get_media($media_id, $type = 2) { $url = 'https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=' . $this->get_token(0, $type); $data = []; $data['media_id'] = $media_id; $res = json_decode(curl_request($url, 'post', json_encode($data)), true); return $res['news_item'][0]; } /* ["title"] => string(12) "测试用例" ["author"] => string(4) "mars" ["digest"] => string(24) "这是一篇测试文章" ["content"] => string(38) "<p>这是一篇测试文章<br /></p>" ["content_source_url"] => string(0) "" ["thumb_media_id"] => string(43) "PQfyghqX3zdaADzWLRAvY046q2D496w35PAuJoj4ySk" ["show_cover_pic"] => int(0) ["url"] => string(190) "http://mp.weixin.qq.com/s?__biz=Mzg2NzU1NjYwMg==&mid=100000001&idx=1&sn=df360f8be89000cbdee593e73ca7537a&chksm=4eb88c9479cf0582a3840d4f6c33cb5879d0fab47dc5c00b76698e2955ac2f0eabd0d330204d#rd" ["thumb_url"] => string(135) "http://mmbiz.qpic.cn/mmbiz_jpg/9maSMBSfiblWsAw7PjVKX10Ohb6GPeJyBuSZ9pJGtl2G9gicQIxlIzYErnnzamxO8Ribqd5WSXDnTEbicHM8TZV5ow/0?wx_fmt=jpeg" ["need_open_comment"] => int(0) ["only_fans_can_comment"] => int(0)*/ /*public function get_media(){ $param = request()->param(); $media_id =$param['media_id']; $type =2; $url = 'https://api.weixin.qq.com/cgi-bin/material/get_material?access_token='. $this->get_token(0, $type); $data = []; $data['media_id'] = $media_id; $res = json_decode(curl_request($url, 'post', json_encode($data)), true); dump($res['news_item'][0]); return $res['news_item'][0]; }*/ //获取公众号平台的永久图文列表 public function get_media_list() { $param = request()->param(); $type = empty($param['type']) ? 2 : $param['type']; //type=1服务号,$type=2订阅号 $media_type = empty($param['media_type']) ? 'news' : $param['media_type']; //素材类型 $arr = ['image', 'video', 'voice', 'news']; if (!in_array($media_type, $arr)) { return '参数校验未通过'; } $data = []; $data['type'] = $media_type;//素材的类型,图片(image)、视频(video)、语音 (voice)、图文(news) $data['offset'] = 0; $data['count'] = 10; $url = 'https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=' . $this->get_token(0, $type); $res = json_decode(curl_request($url, 'post', json_encode($data)), true); return $res; }
/** * 获取普通的access_token,用来做除auth2(获取openid)以外的基础接口请求,如果是微信服务号根据code拿openid,则需要auth2的access_token */
private function get_token($status = 0, $type = 1) { //服务号 $appid = $this->appid; $app_secret = $this->app_secret; //订阅号 if ($type == 2) { $appid = $this->dyh_appid; $app_secret = $this->dyh_app_secret; } //$status = empty($_GET['status'])?0:1; // 调用接口 curl返回access_token $redis = RedisCache::
get_instance
(); if ($type == 2) { $access_token = $redis->get('td:dyh:access_token'); } else { $access_token = $redis->get('td:fwh:access_token'); } if (empty($access_token) || $status == 1) { $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appid . "&secret=" . $app_secret; // echo $url .'<br />' ; $res = curl_request($url); $arr = json_decode($res, true); $access_token = $arr['access_token']; if ($type == 2) { $redis->set('td:dyh:access_token', $access_token, 7000); } else { $redis->set('td:fwh:access_token', $access_token, 7000); } } return $access_token; } //获取公众号菜单 public function get_menu() { $params = request()->param(); $type = empty($params['type']) ? 1 : 2; $url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=" . $this->get_token(0, $type); $res = curl_request($url); $res = json_decode($res, true); if (!empty($res['errcode']) && $res['errcode'] == 40001) { $url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=" . $this->get_token(1, $type); $res = curl_request($url); $res = json_decode($res, true); } var_dump($res); return 'success'; } //服务号在线咨询页面中转跳转 public function online_consulting() { $param = request()->param(); //$log = new SimpleLog('test'); //$log->write_log($param); $code = empty($param['code']) ? '' : $param['code']; $openid = $this->get_oauth_openid($code); //$log->write_log($openid); $customer_info = profile('','',$openid); $customer_id = $customer_info['customer_id']; $name = $customer_id.'_'.time(); $url = 'https://wx.td.shuhengio.com/public/static/img/'.$customer_id.'/'.$name.'.png'; $data = []; $data['customer_id'] = $customer_id; $data['name'] = $name; $data['nonce'] = rand(100000,999999); $data['time'] = time(); $curl = curl_request('http://wx.td.shuhengio.com/td_api/image_produce/merge_picture','post',$data); //$log->write_log($curl); $redirect_zxzx = "https://ykf-webchat.7moor.com/wapchat.html?accessId=29614f30-4fff-11eb-b9d6-bbf82f53d9cd&fromUrl=http://www.baidu.com&urlTitle=数珩信息科技&language=ZHCN&otherParams={\"peerId\":\"10049041\"}&customField={\"openid\":\"" . $openid . "\",\"img\":\"" . $url . "\"}"; //$log->write_log($redirect_zxzx); header('Location:' . $redirect_zxzx); exit; } //设置公众号菜单 public function set_menu() { $params = request()->param(); $type = empty($params['type']) ? 1 : $params['type']; //服务号 $appid = $this->appid; //$app_secret = $this->app_secret; $s_td_c_id = 1; //订阅号 if ($type == 2) { $appid = $this->dyh_appid; //$app_secret = $this->dyh_app_secret; $s_td_c_id = 4; } $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" . $this->get_token(0, $type); //"https://ykf-webchat.7moor.com/wapchat.html?accessId=29614f30-4fff-11eb-b9d6-bbf82f53d9cd&fromUrl=http://www.baidu.com&urlTitle=AI智能实验室&language=ZHCN&otherParams={\"peerId\":\"10049041\"}" $data = array(); if($type == 1){ //服务号 //会员福利 $redirect = urlencode('http://bc.xgs521.com/index.html?s_td_c_id=' . $s_td_c_id); //个人中心 $redirect2 = urlencode('http://bc.xgs521.com/index2.html?s_td_c_id=' . $s_td_c_id); //在线咨询中转页 $redirect_third = urlencode("http://wx.td.shuhengio.com/td_api/wx_collection/online_consulting"); //先去中转页,在线咨询 $zaixianzixun = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect_third . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; $data["button"] = array( array( 'name' => "会员中心", 'sub_button' => array( array( "type" => "view", "name" => "会员福利", "url" => "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect" ), array( "type" => "view", "name" => "个人中心", "url" => "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect2 . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect" ), array( "type" => "view", "name" => "在线咨询", "url" => $zaixianzixun ) ) ), array( 'name' => "教育资讯", 'sub_button' => array( array( "type"=>"view_limited", "name"=>"2021高考动态", "media_id"=>"MEDIA_ID2" ) ) ), array( 'name' => "学习专区", 'sub_button' => array( array( "type"=>"view_limited", "name"=>"辰读时刻", "media_id"=>"MEDIA_ID2" ), array( "type"=>"view_limited", "name"=>"初中资料", "media_id"=>"MEDIA_ID2" ), array( "type"=>"view_limited", "name"=>"高中资料", "media_id"=>"MEDIA_ID2" ), array( "type"=>"view_limited", "name"=>"语数英测试", "media_id"=>"MEDIA_ID2" ), ) ) ); }else{ //订阅号 //会员福利 $redirect = urlencode('http://bc.xgs521.com/index.html?s_td_c_id=' . $s_td_c_id); //个人中心 $redirect2 = urlencode('http://bc.xgs521.com/index2.html?s_td_c_id=' . $s_td_c_id); //在线咨询中转页 $redirect_third = urlencode("http://wx.td.shuhengio.com/td_api/wx_collection/online_consulting"); //先去中转页,在线咨询 $zaixianzixun = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect_third . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; $data["button"] = array( array( 'name' => "会员中心", 'sub_button' => array( array( "type" => "view", "name" => "会员福利", "url" => "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect" ), array( "type" => "view", "name" => "个人中心", "url" => "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect2 . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect" ), array( "type" => "view", "name" => "在线咨询", "url" => $zaixianzixun ) ) ), array( 'name' => "教育资讯", 'sub_button' => array( array( "type"=>"view_limited", "name"=>"2021高考动态", "media_id"=>"MEDIA_ID2" ) ) ), array( 'name' => "学习专区", 'sub_button' => array( array( "type"=>"view_limited", "name"=>"辰读时刻", "media_id"=>"MEDIA_ID2" ), array( "type"=>"view_limited", "name"=>"初中资料", "media_id"=>"MEDIA_ID2" ), array( "type"=>"view_limited", "name"=>"高中资料", "media_id"=>"MEDIA_ID2" ), array( "type"=>"view_limited", "name"=>"语数英测试", "media_id"=>"MEDIA_ID2" ), ) ) ); } //汉字不转义 $filedata = json_encode($data,
JSON_UNESCAPED_UNICODE
); // var_dump($filedata) ; // include "Http.class.php" ; $res = curl_request($url, 'post', $filedata); $arr = json_decode($res, true); //var_dump($arr) ; if ($arr['errcode'] == '0') { return "菜单创建成功"; } else { return "菜单创建失败"; } } //备份 public function set_menu_backend() { $params = request()->param(); $type = empty($params['type']) ? 1 : $params['type']; //服务号 $appid = $this->appid; //$app_secret = $this->app_secret; $s_td_c_id = 1; //订阅号 if ($type == 2) { $appid = $this->dyh_appid; //$app_secret = $this->dyh_app_secret; $s_td_c_id = 4; } $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" . $this->get_token(0, $type); //"https://ykf-webchat.7moor.com/wapchat.html?accessId=29614f30-4fff-11eb-b9d6-bbf82f53d9cd&fromUrl=http://www.baidu.com&urlTitle=AI智能实验室&language=ZHCN&otherParams={\"peerId\":\"10049041\"}" $data = array(); if($type == 1){ //服务号 $redirect = urlencode('http://bc.xgs521.com/index.html?s_td_c_id=' . $s_td_c_id); $redirect2 = urlencode('http://bc.xgs521.com/index2.html?s_td_c_id=' . $s_td_c_id); $redirect_third = urlencode("http://wx.td.shuhengio.com/td_api/wx_collection/online_consulting"); //先去测试页 $zaixianzixun = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect_third . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; $data["button"] = array( array( 'name' => "数珩", 'sub_button' => array( array( "type" => "view", "name" => "1对1", "url" => "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect" ), array( "type" => "view", "name" => "1对4", "url" => "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $appid . "&redirect_uri=" . $redirect2 . "&response_type=code&scope=snsapi_base&state=1#wechat_redirect" ), array( "type" => "view", "name" => "在线咨询", "url" => $zaixianzixun ), array( "type" => "scancode_push", "name" => "扫码推送", "key" => "saomatuisong" ) ) ), array( 'name' => "原创专栏", 'sub_button' => array( array( "type" => "scancode_waitmsg", "name" => "扫码带提示", "key" => "saomadaitishi" ), array( "type" => "pic_sysphoto", "name" => "系统拍照发图", "key" => "xitongpaizhaofatu" ), array( "type" => "pic_photo_or_album", "name" => "拍照或者相册发图", "key" => "paizhaohuozhexiangce" ) ) ), array( 'name' => "联系我们", 'sub_button' => array( array( "type" => "pic_weixin", "name" => "微信相册发图", "key" => "weixinxiangcefatu" ), array( "type" => "location_select", "name" => "发送位置", "key" => "fasongweizhi" ), ) ) ); }else{ //订阅号 $data["button"] = array( array( 'name' => "数珩", 'sub_button' => array( array( "type" => "click", "name" => "1对1", "key" => "tuijiancai" ), array( "type" => "click", "name" => "1对4", "key" => "tuijiancai4" ), array( "type" => "click", "name" => "在线咨询", "key" => "zaixianzixun" ), array( "type" => "scancode_push", "name" => "扫码推送", "key" => "saomatuisong" ) ) ), array( 'name' => "原创专栏", 'sub_button' => array( array( "type" => "scancode_waitmsg", "name" => "扫码带提示", "key" => "saomadaitishi" ), array( "type" => "pic_sysphoto", "name" => "系统拍照发图", "key" => "xitongpaizhaofatu" ), array( "type" => "pic_photo_or_album", "name" => "拍照或者相册发图", "key" => "paizhaohuozhexiangce" ) ) ), array( 'name' => "联系我们", 'sub_button' => array( array( "type" => "pic_weixin", "name" => "微信相册发图", "key" => "weixinxiangcefatu" ), array( "type" => "location_select", "name" => "发送位置", "key" => "fasongweizhi" ), /* array( "type"=>"media_id", "name"=>"图片", "media_id"=>"MEDIA_ID1" ), array( "type"=>"view_limited", "name"=>"图文消息", "media_id"=>"MEDIA_ID2" )*/ ) ) ); } //汉字不转义 $filedata = json_encode($data,
JSON_UNESCAPED_UNICODE
); // var_dump($filedata) ; // include "Http.class.php" ; $res = curl_request($url, 'post', $filedata); $arr = json_decode($res, true); //var_dump($arr) ; if ($arr['errcode'] == '0') { return "菜单创建成功"; } else { return "菜单创建失败"; } } }
Base.php控制器基类
<?php namespace app\td_api\controller; use app\common\DesUtils; use app\common\RedisCache; use app\common\SimpleLog; use Firebase\JWT\ExpiredException; use Firebase\JWT\JWT; use think\Controller; use think\Exception; use think\Log; use think\Request; use think\Cache; use think\Config; use think\Db; use think\Response;
/** * class WEB_API基类 * Class Base *
@package
app\web_api\controller *
@Auther
mars */
class Base extends Controller { public $user; public $k; protected $des_key = 'ascfesqiokoweirtusdsavxsfgqiyoqwieqserwqpwqfwrpqovrvqmmgqwhrnqnj'; //des密钥 public function __construct(Request $request = null) { parent::
__construct
($request); //动态配置默认返回方式 //Config::set('default_return_type','json'); }
/** * Des 前置操作,默认执行token校验,如果需要排除方法,请在对应控制器初始化函数覆盖该属性。 * eg: 'checkToken' => ['except'=>'app_login,pad_qr,pad_qr_check'] * Auther mars *
@var
array */
protected $beforeActionList = [ 'checkToken','check_params','stripslashes_filter','htmlentities_filter','htmlspecialchars_filter','strip_tags_filter','api_des_decrypt' ];
/** * Des stripslashes() 函数删除由 addslashes() 函数添加的反斜杠。默认地,PHP 对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。 * Auther mars *
@var
array */
public function stripslashes_filter(){ $params = request()->param(); foreach ($params as &$v){ $v = stripslashes($v); } }
/** * Des 把字符转换为 HTML 实体:要把 HTML 实体转换回字符,请使用 html_entity_decode() 函数。 * Auther mars *
@var
array */
public function htmlentities_filter(){ $params = request()->param(); foreach ($params as &$v){ $v = htmlentities($v); } }
/** * Des 将特殊字符转换为 HTML 实体;如需把特殊的 HTML 实体转换回字符,请使用 htmlspecialchars_decode() 函数。 * Auther mars *
@var
array */
public function htmlspecialchars_filter(){ $params = request()->param(); foreach ($params as &$v){ $v = htmlspecialchars($v); } }
/** * Des strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签。 * Auther mars *
@var
array */
public function strip_tags_filter(){ $params = request()->param(); foreach ($params as &$v){ $v = strip_tags($v); } }
/** * Des des解密 * Auther mars *
@var
array */
public function api_des_decrypt(){ $params = request()->param(); if(empty($params['k'])){ return $this->errorResponse('参数不合法'); } //t69y5SBI8xDcQx9I%2BTCwzcPI6t0PvGzjNhxH7cfYU406hFG7HRDuPw%3D%3D $des = new DesUtils(); // dump($des->des_ecb_decrypt($params['k'],$this->des_key));exit; $this->k = json_decode($des->des_ecb_decrypt($params['k'],$this->des_key),true); if(empty($this->k)){ return $this->errorResponse('数据解密失败'); } }
/** * Des des加密 * Auther mars *
@var
array */
public function api_des_encrypt($data,$msg='success',$code=1){ $res = []; $res['data'] = $data; $res['msg'] = $msg; $res['code'] = $code; $des= new DesUtils(); $enc = $des->des_ecb_encrypt(json_encode($res),$this->des_key); //return ['k' => $enc]; Response::
create
(['k' => $enc], 'json')->send(); exit; }
/** * Des 参数校验,防重放 * Auther mars *
@var
array */
public function check_params(){ $params = $this->k; //$log = new SimpleLog('check_params'); //$log->write_log($params); $arr = [0,1]; if(!isset($params['status']) || !in_array($params['status'],$arr)){ return $this->api_des_encrypt('','参数缺失',0); } if(!isset($params['nonce'])){ return $this->api_des_encrypt('','参数缺失!',0); } if(!isset($params['time'])){ return $this->api_des_encrypt('','参数缺失!!',0); } $server_time = time(); if($server_time-$params['time']>60){ return $this->api_des_encrypt(['server_time'=>$server_time],'请求超时',0); } $redis =RedisCache::
get_instance
(); $string = 'beichen:check:params'; $score = $server_time-60; $redis->zRemRangeByScore($string,0,$score); $range = $redis->zRange($string,0,-1); if(in_array($params['nonce'],$range)){ //重放攻击的ip可以记录下来 return $this->api_des_encrypt('','重复的请求',0); }else{ $redis->zAdd($string,$params['nonce']); } }
/** * 创建 token *
@param
array $data 必填 自定义参数数组 *
@param
integer $exp_time 必填 token过期时间 单位:秒 例子:7200=2小时 *
@param
string $scopes 选填 token标识,请求接口的token *
@return
string */
public function createToken($data) { //JWT标准规定的声明,但不是必须填写的; //iss: jwt签发者 //sub: jwt所面向的用户 //aud: 接收jwt的一方 //exp: jwt的过期时间,过期时间必须要大于签发时间 //nbf: 定义在什么时间之前,某个时间点后才能访问 //iat: jwt的签发时间 //jti: jwt的唯一身份标识,主要用来作为一次性token。 include(
ROOT_PATH
.'extend/firebase/php-jwt/src/JWT.php'); try { $time = time(); //当前时间 $token['aud'] = ''; //接收该JWT的一方,可选 $token['iat'] = $time; //签发时间 $token['nbf'] = $time; //(Not Before):某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用 $token['exp'] = $time + 28800; //token过期时间,这里设置8个小时 if(!empty($data['nonce'])){ $token['jti'] = $data['nonce']; //jwt的唯一身份标识,主要用来作为一次性token。 } $token['data'] = $data; $this->user = $token; return JWT::
encode
($token, 'pdvm'); } catch (ExpiredException $e) { //签名不正确 $returndata['code'] = "101";//101=签名不正确 $returndata['msg'] = $e->getMessage(); $returndata['data'] = "";//返回的数据 return json_encode($returndata); //返回信息 } catch (Exception $e) { //其他错误 $returndata['code'] = "199";//199=签名不正确 $returndata['msg'] = $e->getMessage(); $returndata['data'] = "";//返回的数据 return json_encode($returndata); //返回信息 } }
/** * 验证token是否有效,默认验证exp,nbf,iat时间 *
@param
string $jwt 需要验证的token *
@return
string $msg 返回消息 */
function checkToken() { $token = request()->header('token'); $url = input('url'); include(
ROOT_PATH
.'extend/firebase/php-jwt/src/JWT.php'); include(
ROOT_PATH
.'extend/firebase/php-jwt/src/SignatureInvalidException.php'); include(
ROOT_PATH
.'extend/firebase/php-jwt/src/BeforeValidException.php'); include(
ROOT_PATH
.'extend/firebase/php-jwt/src/ExpiredException.php'); if ($token == null) { $returndata['code'] = -1; $returndata['data'] = "token不能为空"; Response::
create
($returndata, 'json')->send(); exit(); } $key = 'pdvm'; try { JWT::
$leeway
= 60;//当前时间减去60,把时间留点余地 $decoded = JWT::
decode
($token, $key, ['HS256']); //HS256方式,这里要和签发的时候对应 $arr = (array)$decoded; $returndata = array(); $returndata['code'] = "1"; $returndata['msg'] = "成功"; $returndata['data'] = $arr;//返回的数据 $this->user = $returndata; } catch (\Firebase\JWT\SignatureInvalidException $e) { //签名不正确 $returndata['code'] = "101";//101=签名不正确 $returndata['msg'] = $e->getMessage(); $returndata['data'] = "签名不正确";//返回的数据 Response::
create
($returndata, 'json')->send(); exit(); } catch (\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用 $returndata['code'] = "102"; $returndata['msg'] = $e->getMessage(); $returndata['data'] = "";//返回的数据 Response::
create
($returndata, 'json')->send(); exit(); } catch (\Firebase\JWT\ExpiredException $e) { // token过期 $returndata['code'] = "-1"; $returndata['msg'] = $e->getMessage(); $returndata['data'] = "token过期";//返回的数据 Response::
create
($returndata, 'json')->send(); exit(); } catch (Exception $e) { //其他错误 $returndata['code'] = "-1"; $returndata['msg'] = $e->getMessage(); $returndata['data'] = "系统错误";//返回的数据 Response::
create
($returndata, 'json')->send(); exit(); } } public function successResponse($msg = 'success', $data = '', array $header = []) { $result = [ 'code' => 1, 'msg' => $msg, 'data' => !empty($data)?$data:null, 'server_time'=>time() ]; return json($result); } public function errorResponse($msg = 'error', $data = '', array $header = []) { $result = [ 'code' => 0, 'msg' => $msg, 'data' => !empty($data)?$data:null, 'server_time'=>time() ]; return json($result); } //数据日志 protected function data_log($arr) { $time = time(); $data_log = []; $data_log['operation_type'] = $arr['operation_type']; $data_log['new_data'] = empty($arr['new_data']) ? '' : json_encode($arr['new_data']); $data_log['old_data'] = empty($arr['old_data']) ? '' : json_encode($arr['old_data']); $data_log['begin_time'] = empty($arr['begin_time']) ? $time : $arr['begin_time']; $data_log['end_time'] = empty($arr['end_time']) ? $time : $arr['end_time']; $data_log['event_type'] = empty($arr['event_type']) ? '' : $arr['event_type']; $data_log['response_msg'] = empty($arr['response_msg']) ? '' : $arr['response_msg']; $data_log['create_time'] = $time; $data_log['operation'] = $arr['operation']; $data_log['sign_id'] = $arr['sign_id']; $data_log['count'] = $arr['count']; $data_log['element_id'] = $arr['element_id']; $data_log['type'] = empty($arr['type']) ? '' : $arr['type']; $td_data_log = Db::name('td_data_log')->insert($data_log); if ($td_data_log) { return 1; } else { return 0; } } }
common.php中curl_request函数
/** * 封装发送http请求 *
@param
string $method 请求方式 get/post *
@param
string $url 请求目标的url *
@param
array $data post发送的数据 *
@param
array $head 请求、响应头信息 array('Content-Type: application/json; charset=utf-8','Content-Length:' . strlen($json_str)) *
@param
bool $ssl 是否为https协议 *
@return
响应主体 */
function curl_request($url, $method = 'get', $data = [], $head = [],$user_agnet=1, $ssl = true) { //curl完成,先开启curl模块 //初始化一个curl资源 $curl = curl_init(); //设置curl选项 curl_setopt($curl,
CURLOPT_URL
, $url);//url //请求的代理信息 if($user_agnet == 1){ $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0 FirePHP/0.7.4'; curl_setopt($curl,
CURLOPT_USERAGENT
, $user_agent); //referer头,请求来源 curl_setopt($curl,
CURLOPT_AUTOREFERER
, true); curl_setopt($curl,
CURLOPT_TIMEOUT
, 30);//设置超时时间 } //SSL相关 if ($ssl) { //禁用后,curl将终止从服务端进行验证; curl_setopt($curl,
CURLOPT_SSL_VERIFYPEER
, false); //检查服务器SSL证书是否存在一个公用名 curl_setopt($curl,
CURLOPT_SSL_VERIFYHOST
, 2); } //判断请求方式post还是get if (strtolower($method) == 'post') {
/**************处理post相关选项******************/
//是否为post请求 ,处理请求数据 curl_setopt($curl,
CURLOPT_POST
, true); curl_setopt($curl,
CURLOPT_POSTFIELDS
, $data); } //是否处理响应头 if (!empty($head)) { curl_setopt($curl,
CURLOPT_HEADER
, true); //类型为json curl_setopt($curl,
CURLOPT_HTTPHEADER
, $head); } else { curl_setopt($curl,
CURLOPT_HEADER
, false); } //是否返回响应结果 curl_setopt($curl,
CURLOPT_RETURNTRANSFER
, true); //发出请求 $response = curl_exec($curl); if (false === $response) { echo '<br>', curl_error($curl), '<br>'; return false; } //关闭curl curl_close($curl); return $response; }
SimpleLog.php日志
<?php
/** * des: 日志记录类 * User: Mars Jane */
namespace app\common; class SimpleLog { const
LOG_FILE_TYPE_DEFAULT
= 1; const
LOG_FILE_TYPE_JSON
= 1; const
LOG_FILE_TYPE_XML
= 2; const
LOG_FILE_TYPE_PHP_ARRAY
= 3; const
LOG_FILE_TYPE_PHP_INI
= 4; const
LOG_FILE_TYPE_TEXT
= 5; private $base_path = ''; private $path = ''; private $file = ''; private $file_name = ''; private $log_file_type = 0; public function __construct($file_name = '',$file_type = self::
LOG_FILE_TYPE_JSON
) { if($file_name) { $this->file_name = trim($file_name); } else { $this->file_name = 'fres'; } $this->create_file($file_type); }
/** * des:写日志 *
@param
mixed $data 日志记录 *
@param
string $capture 日志模块 */
public function write_log($data,$capture='common') { file_put_contents($this->path,'['.date('Y-m-d H:i:s').' <<'.$capture.'>> ]'."\n",
FILE_APPEND
); $this->write($data); file_put_contents($this->path,"\n",
FILE_APPEND
); } protected function create_file($file_type) { // define('WWW_PATH',str_replace('\\','/',realpath(dirname(__FILE__).'/../'))); //定义站点目录 //var_dump(WWW_PATH); $root_path =str_replace('\\','/',
ROOT_PATH
); $this->base_path = $root_path.'runtime/log'; $this->set_logFileType($file_type); $dir = $this->base_path.'/'.date('Y-m-d'); if(!is_dir($dir))@mkdir($dir); $this->path = $dir.'/'.$this->file; //var_dump($this->path ); if(is_dir($dir)) { if(!is_file($this->path)) { @touch($this->path); } } } private function set_logFileType($file_type = self::
LOG_FILE_TYPE_JSON
) { if($file_type == self::
LOG_FILE_TYPE_JSON
) { $this->file = $this->file_name.'.json'; $this->log_file_type = self::
LOG_FILE_TYPE_JSON
; } elseif($file_type == self::
LOG_FILE_TYPE_XML
) { $this->file = $this->file_name.'.xml'; $this->log_file_type = self::
LOG_FILE_TYPE_XML
; } elseif($file_type == self::
LOG_FILE_TYPE_PHP_ARRAY
) { $this->file = $this->file_name.'.plog'; $this->log_file_type = self::
LOG_FILE_TYPE_PHP_ARRAY
; } elseif($file_type == self::
LOG_FILE_TYPE_PHP_INI
) { $this->file = $this->file_name.'.ini'; $this->log_file_type = self::
LOG_FILE_TYPE_PHP_INI
; } elseif($file_type == self::
LOG_FILE_TYPE_TEXT
) { $this->file = $this->file_name.'.txt'; $this->log_file_type = self::
LOG_FILE_TYPE_TEXT
; } else { $this->file = $this->file_name.'.json'; $this->log_file_type = self::
LOG_FILE_TYPE_DEFAULT
; } } protected function write($data) { if(is_null($data)) { return false; } elseif(is_string($data)) { return $this->_write_string($data); } elseif(is_numeric($data)) { return $this->_write_numeric($data); } elseif(is_bool($data)) { return $this->_write_bool($data); } elseif(is_array($data)) { return $this->_write_array($data); } elseif(is_object($data)) { return $this->_write_object($data); } else{ return false; } } private function _write_string($strs) { file_put_contents($this->path,$strs,
FILE_APPEND
); } private function _write_numeric($num) { file_put_contents($this->path,$num,
FILE_APPEND
); } private function _write_bool($bool) { $bool_str = $bool ? 'true' : 'false'; file_put_contents($this->path,$bool_str,
FILE_APPEND
); } private function _write_array($arr) { file_put_contents($this->path,'<?php'."\n",
FILE_APPEND
); file_put_contents($this->path,var_export($arr,true),
FILE_APPEND
); file_put_contents($this->path,';'."\n?>",
FILE_APPEND
); } private function _write_object($obj) { file_put_contents($this->path,'<?php'."\n",
FILE_APPEND
); file_put_contents($this->path,var_export($obj,true),
FILE_APPEND
); file_put_contents($this->path,';'."\n?>",
FILE_APPEND
); } }
php7版本DES加密工具类
<?php namespace app\common; class DesUtils {
/** * des-ecb加密 *
@param
string $data 要被加密的数据 *
@param
string $key 加密密钥(64位的字符串) */
function des_ecb_encrypt($data, $key){ return urlencode(openssl_encrypt ($data, 'des-ecb', $key)); }
/** * des-ecb解密 *
@param
string $data 加密数据 *
@param
string $key 加密密钥 */
function des_ecb_decrypt ($data, $key) { return openssl_decrypt($data, 'des-ecb', $key); } }
Redis基类
<?php namespace app\common\service; //1.创建一个单例类 use think\Config; class RedisCache { private static
$redis
; private function __construct() { echo '我被实例化了!'; } private function __clone() { trigger_error('Clone is not allow',
E_USER_ERROR
); } public static function get_instance() { if (!isset(self::
$redis
)) { self::
$redis
= new \Redis(); $config = Config::
get
('cache.redis'); self::
$redis
->connect($config['host'],$config['port'],$config['timeout']); if(!empty($config['password'])){ self::
$redis
->auth($config['password']); } } return self::
$redis
; } //类中的方法只会有一个实例执行它 public function test() { echo("单例模式设计成功"); } }