微信实验六、微信PHP后台验证、接收和发送消息及源码下载
(>>>>在公众号中输入文章最后彩蛋即可获取源代码)
开源项目:https://github.com/chenxhjeo,个人博客:http://blog.csdn.net/u013487761
技术QQ群名称:豆豆咨询,群号:625686304
微信公众号名称:豆豆咨询,微信公众号:douAsk
初建日期:2017.04.08
一、实验目的
1、掌握微信开发PHP后台验证、接收和发送消息。
二、实验内容
1、验证代码解析。
2、接收和发送消息解析。
三、实验步骤及过程
只有当微信服务器验证了应用服务器后台PHP的有效性,才能够向应用服务器发送消息和接收消息。每个用户针对每个公众号会产生一个安全的OpenID。公众号主要通过公众号消息会话和公众号内网页来为用户提供服务的,本次实验主要介绍公众号消息会话中的被动回复消息。
被动回复消息:在用户给公众号发消息后,微信服务器会将消息发到开发者预先在开发者中心设置的服务器地址(开发者需要进行消息真实性验证),公众号可以在5秒内做出回复,可以回复一个消息,也可以回复命令告诉微信服务器这条消息暂不回复。
1、验证消息是否来自微信服务器。
1)参数说明
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下所示:
lsignature:微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
ltimestamp:时间戳
lnonce:随机数
lechostr:随机字符串
2)验证过程
开发者通过检验signature对请求进行校验。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
a.将token、timestamp、nonce三个参数进行字典序排序;
b.将三个参数字符串拼接成一个字符串进行sha1加密;
c.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信。
3)函数代码说明
a.checkSignature()函数
//名称:checkSignature()
//功能:验证signature与加密数据(timestamp、nonce、TOKEN)是否一致
//返回:true:验证通过;false:验证失败
privatefunctioncheckSignature()
{
$signature =$_GET["signature"];//从微信服务器得到signature
$timestamp =$_GET["timestamp"];//从微信服务器得到timestamp
$nonce =$_GET["nonce"];//从微信服务器得到nonce
$token =TOKEN;//从微信管理员设置的TOKEN
$tmpArr =array($token, $timestamp, $nonce);//建立数组tmpArr
sort($tmpArr);//将token、timestamp、nonce三个参数进行字典序排序;
$tmpStr =implode($tmpArr);//将数组的内容连接成一个字符串
$tmpStr =sha1($tmpStr);//将字符串进行sha1加密;
if($tmpStr == $signature){//验证字符串,即signature与tmpStr对比
return true;
}else{
return false;
}
}
b.valid()函数
//名称:valid()
//功能:验证微信服务器发送的消息,并发送给$echoStr
//返回:从微信服务器发送过来的echostr
publicfunctionvalid()
{
$echoStr =$_GET["echostr"];//从微信服务器得到echostr
if($this->checkSignature()){
echo$echoStr;
exit;
}
}
c.验证过程
header('Content-type:text');
define("TOKEN","weixin");//与服务器设置的TOKEN要一致
$wechatObj =newwechatCallbackapiTest();
if(!isset($_GET['echostr'])) {//判断echostr是否为空
$wechatObj->responseMsg();
}else{
$wechatObj->valid();
}
验证通过后,用户每次向公众号发送消息、或者产生自定义菜单点击事件时,开发者填写的服务器配置URL将得到微信服务器推送过来的消息和事件,然后开发者可以依据自身业务逻辑进行响应,例如回复消息等。
2、接收消息。
responseMsg()处理接收到的消息,然后根据消息类型分别回发消息,相关函数如下所示。
1)主函数:responseMsg()函数
//名称:responseMsg()
//功能:根据接收的消息类型(文本、事件等),分别回复消息
//返回:返回消息结果
publicfunctionresponseMsg()
{
$postStr =$GLOBALS["HTTP_RAW_POST_DATA"];//用$GLOBALS['HTTP_RAW_POST_DATA']来接收数据
if(!empty($postStr)){
$this->logger("R
".$postStr);//记录读取的信息
$postObj =simplexml_load_string($postStr,'SimpleXMLElement',LIBXML_NOCDATA);//解析xml数据到object
$RX_TYPE =trim($postObj->MsgType);//接收消息类型text/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"voice"://语音
$result = $this->receiveVoice($postObj);
break;
case"video"://视频
$result = $this->receiveVideo($postObj);
break;
case"shortvideo"://小视频
$result = $this->receiveShortVideo($postObj);
break;
case"location"://位置
$result = $this->receiveLocation($postObj);
break;
case"link"://链接
$result = $this->receiveLink($postObj);
break;
}
$this->logger("T
".$result);//记录发送的信息
echo$result;
}else{
echo"";
exit;
}
}
2)其他private函数
a. receiveEvent ()函数
//名称:receiveEvent()
//功能:根据事务类型,构造回发消息
//返回:回发消息
privatefunctionreceiveEvent($object)
{
$content ="";
if($object->Event=="subscribe"){
//1.关注公众号;2.扫描带参数二维码事件(用户未关注时,进行关注后的事件推送)
if(var_dump(property_exists('$object','EventKey'))){
//2.扫描带参数二维码事件(用户未关注时,进行关注后的事件推送)
$content ="未关注时扫描二维码关注事件,".strval($object->EventKey).strval($object->Ticket);
}else{
//1.关注公众号
$content ="欢迎关注豆豆咨询";
}
}else if($object->Event=="SCAN"){
//扫描带参数二维码事件(扫描带参数二维码事件,用户已关注时的事件推送)
$content ="关注时扫描二维码关注事件,".strval($object->EventKey).strval($object->Ticket);
}else if($object->Event=="unsubscribe"){
//取消关注公众号
$content ="取消关注";
}else if($object->Event=="CLICK"){
//自定义菜单:用户点击自定义菜单后,微信会把点击事件推送给开发者,注意,点击菜单弹出子菜单,不会产生上报。
$content ="点击菜单拉取消息时的事件推送:".strval($object->EventKey);
}else if($object->Event=="LOCATION"){
//上报地理位置事件:维度、经度、精度
$content ="上报地理位置:".strval($object->Latitude).",".strval($object->Longitude).",".strval($object->Precision);//
}else if($object->Event=="VIEW"){
//点击菜单跳转链接时的事件推送
$content ="点击菜单跳转链接时的事件推送:".strval($object->EventKey);
}
$result = $this->transmitText($object, $content);
return$result;
}
b. receiveText ()函数
//名称:receiveText()
//功能:接收文本消息
//返回:回发消息
privatefunctionreceiveText($object)
{
$keyword =trim($object->Content);
$content =date("Y-m-d H:i:s",time())."\n技术支持'豆豆咨询'微信公众号(douAsk)和qq群号(625686304):";
$content = $content."\n您发送的消息是".$keyword;
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;
}
c. receiveImage ()函数
//名称:receiveImage()
//功能:接收图片消息
//返回:回发消息
privatefunctionreceiveImage($object)
{
$keyword =trim($object->Content);
$content =date("Y-m-d H:i:s",time())."\n技术支持'豆豆咨询'微信公众号(douAsk)和qq群号(625686304):";
$content = $content."\n您发送的消息PicUrl:".$object->PicUrl."
MediaId:".$object->MediaId;
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;
}
d. receiveVoice ()函数
//名称:receiveVoice()
//功能:接收语音消息
//返回:回发消息
privatefunctionreceiveVoice($object)
{
$keyword =trim($object->Content);
$content =date("Y-m-d H:i:s",time())."\n技术支持'豆豆咨询'微信公众号(douAsk)和qq群号(625686304):";
$content = $content."\n您发送的消息Format:".$object->Format."
MediaId:".$object->MediaId;
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;
}
d. receiveVideo ()函数
//名称:receiveVideo()
//功能:接收视频消息
//返回:回发消息
privatefunctionreceiveVideo($object)
{
$keyword =trim($object->Content);
$content =date("Y-m-d H:i:s",time())."\n技术支持'豆豆咨询'微信公众号(douAsk)和qq群号(625686304):";
$content = $content."\n您发送的消息ThumbMediaId:".$object->ThumbMediaId."
MediaId:".$object->MediaId;
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;
}
e. receiveShortVideo ()函数
//名称:receiveShortVideo()
//功能:接收小视频消息
//返回:回发消息
privatefunctionreceiveShortVideo($object)
{
$keyword =trim($object->Content);
$content =date("Y-m-d H:i:s",time())."\n技术支持'豆豆咨询'微信公众号(douAsk)和qq群号(625686304):";
$content = $content."\n您发送的消息ThumbMediaId:".$object->ThumbMediaId."
MediaId:".$object->MediaId;
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;
}
f. receiveLocation ()函数
//名称:receiveLocation()
//功能:接收地理位置消息
//返回:回发消息
privatefunctionreceiveLocation($object)
{
$keyword =trim($object->Content);
$content =date("Y-m-d H:i:s",time())."\n技术支持'豆豆咨询'微信公众号(douAsk)和qq群号(625686304):";
$content = $content."\n您发送的消息Location_X:".$object->Location_X."
Location_Y:".$object->Location_Y;
$content = $content."\n您发送的消息Scale:".$object->Scale."
Label:".$object->Label;
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;
}
g. receiveLink ()函数
//名称:receiveLink()
//功能:接收链接消息
//返回:回发消息
privatefunctionreceiveLink($object)
{
$keyword =trim($object->Content);
$content =date("Y-m-d H:i:s",time())."\n技术支持'豆豆咨询'微信公众号(douAsk)和qq群号(625686304):";
$content = $content."\n您发送的消息Title:".$object->Title."
Description:".$object->Description;
$content = $content."\n您发送的消息Url:".$object->Url;
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;
}
f. transmitText ()函数
//名称:transmitText()
//功能:发送文本消息
//返回:回发消息
privatefunctiontransmitText($object,$content)
{
$textTpl ="
%s
";
$result =sprintf($textTpl,$object->FromUserName,$object->ToUserName,time(),$content);
return$result;
}
h. transmitText ()函数
//名称:transmitNews()
//功能:回复图文消息
//返回:图文消息
privatefunctiontransmitNews($object,$arr_item)
{
if(!is_array($arr_item))
return;
$itemTpl ="
<![CDATA[%s]]>
";
$item_str ="";
foreach($arr_itemas$item)
$item_str .=sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
$newsTpl ="
%s
%s
$item_str
";
$result =sprintf($newsTpl,$object->FromUserName,$object->ToUserName,time(),count($arr_item));
return$result;
}
i. transmitText ()函数
//名称:transmitMusic()
//功能:回复音乐消息
//返回:音乐消息
privatefunctiontransmitMusic($object,$musicArray)
{
$itemTpl ="
<![CDATA[%s]]>
";
$item_str =sprintf($itemTpl,$musicArray['Title'],$musicArray['Description'],$musicArray['MusicUrl'],$musicArray['HQMusicUrl']);
$textTpl ="
%s
$item_str
";
$result =sprintf($textTpl,$object->FromUserName,$object->ToUserName,time());
return$result;
}
j. transmitText ()函数
//名称:logger()
//功能:记录日志
//返回:
privatefunctionlogger($log_content)
{
if(isset($_SERVER['HTTP_APPNAME'])){//SAE
sae_set_display_errors(false);
sae_debug($log_content);
sae_set_display_errors(true);
}else if($_SERVER['REMOTE_ADDR'] !="127.0.0.1"){//LOCAL
$max_size = 10000;
$log_filename ="log.xml";
if(file_exists($log_filename) and (abs(filesize($log_filename))> $max_size)){unlink($log_filename);}
file_put_contents($log_filename,date('H:i:s')." ".$log_content."\r\n",FILE_APPEND);
}
}
四、技术服务
1、如果有疑问或者需要帮助,请加入QQ群(群名称:豆豆咨询,群号:625686304);或者公众号douAsk,公众号名称为“豆豆咨询”。扫描以下二维码,关注“豆豆咨询”。
关注“豆豆咨询”公众号之后输入彩蛋号即可获取源码,
彩蛋号:1202。