首页 » 微信公众平台开发:从零基础到ThinkPHP5高性能框架实践 » 微信公众平台开发:从零基础到ThinkPHP5高性能框架实践全文在线阅读

《微信公众平台开发:从零基础到ThinkPHP5高性能框架实践》20.1.2 使用回调模式

关灯直达底部

企业号在回调企业URL时,会对消息体本身做AES加密,以XML格式POST到企业应用的URL上;企业号在被动响应时,也需要对数据加密,以XML格式返回给微信。企业号的回复支持文本、图片、语音、视频、图文等格式。

假设企业回调URL为http://www.doucube.com/qiyehao/index.php。

请求如下。


http:// www.doucube.com/qiyehao/index.php?msg_signature=cba357c1cfee7db580b8b7be69979c519dd9e2dd&timestamp=1480911337&nonce=953484830  

回调数据格式如下。


<xml>    <ToUserName><![CDATA[wx82e2c31215d9a5a7]]></ToUserName>    <Encrypt><![CDATA[zLP6J6XhqxLmeBioy+dT3QCNlMa6gmEJwI7BXz9+RXRxPns7BvHxnVwHvxGZ8Bk    SntOKIFs9ECpW42SB+aZxk+lp1FTJ+HE+bN4dhCoGN15jWYQmjXD9YdZcjgcTczCJ5Pvxlwwz7pyZnq7n    0wj1rb179g1x78hHigU9TyyMaa6kxzQUoWsfU5h8z9xs1rpWZ/Prj+6ZMg1MGy0ER4SR1hSVtSttUVn7th    yGPZ5+UEWq7ZWzHAOXFUOXwv4nVtRzP+Weu/qrBY+TxZYcRDwdISj7IfNfTh53Yy6+LPLEOShXj602OvJ1l    HVK98D9fumI/9nUZ3C75hvvBBY0HH4tePWwEoNNasb4DKMO6u40iACET+lkrmjuuZP9IuW2aYkLe/ilf3285c    9u/9EYU0o3sNWznxYazNV/lwW/SMdeISlCHwh8CzKQuIMZJdrU3Mfl2gg3IRSY535b0JxSFDw3Ig==]]></Encrypt>    <AgentID><![CDATA[24]]></AgentID></xml>  

上述数据的参数说明如下。

1)msg_encrypt为经过加密的密文。

2)AgentID为接收的应用ID,可在应用的设置页面获取。

3)ToUserName为企业号的CorpID。

企业号对msg_signature进行校验,并解密msg_encrypt,得出msg的原文。明文数据如下。


<xml>    <ToUserName><![CDATA[wx82e2c31215d9a5a7]]></ToUserName>    <FromUserName><![CDATA[fangbei]]></FromUserName>    <CreateTime>1480911337</CreateTime>    <MsgType><![CDATA[event]]></MsgType>    <AgentID>24</AgentID>    <Event><![CDATA[click]]></Event>    <EventKey><![CDATA[COMPANY]]></EventKey></xml>  

根据事件类型,要回复的明文数据如下。


<xml>    <ToUserName><![CDATA[fangbei]]></ToUserName>    <FromUserName><![CDATA[wx82e2c31215d9a5a7]]></FromUserName>    <CreateTime>1480911343</CreateTime>    <MsgType><![CDATA[news]]></MsgType>    <ArticleCount>1</ArticleCount>    <Articles>        <item>            <Title><![CDATA[方倍工作室]]></Title>            <Description><![CDATA]></Description>            <PicUrl><![CDATA[http:// discuz.comli.com/weixin/weather/icon/cartoon.            jpg]]></PicUrl>            <Url><![CDATA[http:// m.cnblogs.com/?u=txw1958]]></Url>        </item>    </Articles></xml>  

将上述数据用同样的加密方法,得到被动响应给微信的数据格式,具体如下。


<xml>    <Encrypt><![CDATA[jvmTnCtYGdari33cQRWgRWdcsLR5Y19nx4txCFlonki3TQQaNlfdc1Svwj    EjKJrXeKofBtC8LIK8gurdR5hfo1BjJ3OqX9WznP2N0Ipnto41dF0hPyNqOw5eBv1BOylly2Rxzhctk    pdS4KPWh70UPjx8vWtMAugkPxZ4REpjEWZoivm2Phq6H0TvLRkNwQlY2D221LjJkDHMskUh7wBeC    yyrw4UJ/Q5vMd4g/k2V8q5kpgHYvvNLoiN8OSMtWCRYAy+qKV1UglegSilxyuvQRX9vb++wH6ejl    ZMbD7L/EeO698202WcqWtBycHPkmuWbx58a4TzjHkKPWtY6GBGoU/KfZAVesCQwUA/ZVo5qtEvgh    4WcUF7u2MYJ72twq0AqdLoD8TtCCSdeK6eoNRKOqm+K0bTrZIt7sR0DhFi8tKrcApU9jaFKR+rKj    b0hsV+M4U16ca1LCrfqQA+AS6MhI6wBEGc2FyFTfqtgroFB18bETuAnahkrtEb2XIDQUnlXiP6Lk    7uuyZDlHaRb7sXPgjGWepOQ7Vdo71CP4sh3RlFp8TbBmA3XMkMUllqjaIlrPfxLsipylY+95xCWX    7rDPPgy5g/6++Sg25XPw0L9ft23LjvuJQWoNABjJVHxjmWbI5bUyYDx/rwzyu/urKWHfrsTmoHvL    fDYp8vrWcfKte0uMGdfJq2vYlAv3ooKTWoh8altTS2YVS6Wc1xqqQG8FMBISqLUjlIMQN3TaWXvE    Y5w5vJ4i1/eHaJeSVhsQGnXW63n0W7gCe0DSLian8DQ32uY7Do3eh2/R6t1VsOUKCnL+oeaRcLzh    nwU+YFIWo7ULiqqPuVFzInN91J6iPKPfw==]]></Encrypt>    <MsgSignature><![CDATA[df908a6dfe95ae615300ae51eb1af6cf8bf3522d]]></MsgSignature>    <TimeStamp>1480911337</TimeStamp>    <Nonce><![CDATA[953484830]]></Nonce></xml>  

上述XML字段解释如下。

1)msg_encrypt为经过加密的密文。

2)MsgSignature为签名。

3)TimeStamp为时间戳,Nonce为随机数,由企业号生成。

使用回调模式的完整代码如下。


  1 require_once("WXBizMsgCrypt.php");  2 $encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";  3 $token = "FangBei";  4 $corpId = "wx82e2c31215d9a5a7";  5   6 class wechatCallbackapiTest extends WXBizMsgCrypt  7 {  8     // 验证URL有效  9     public function valid 10     { 11         $sVerifyMsgSig = $_GET["msg_signature"]; 12         $sVerifyTimeStamp = $_GET["timestamp"]; 13         $sVerifyNonce = $_GET["nonce"]; 14         $sVerifyEchoStr = $_GET["echostr"]; 15  16         $sEchoStr = ""; 17         $errCode = $this->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce,              $sVerifyEchoStr, $sEchoStr); 18         if ($errCode == 0) { 19             // 验证URL成功,将sEchoStr返回 20             echo $sEchoStr; 21         } 22     } 23  24     // 响应消息 25     public function responseMsg 26     { 27         $sReqMsgSig = $_GET['msg_signature']; 28         $sReqTimeStamp = $_GET['timestamp']; 29         $sReqNonce = $_GET['nonce']; 30         $sReqData = $GLOBALS["HTTP_RAW_POST_DATA"]; 31         $sMsg = "";                  // 解析之后的明文 32         $this->logger(" DE /r/n".$sReqData); 33  34         $errCode = $this->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce,             $sReqData, $sMsg); 35         $this->logger(" RR /r/n".$sMsg); 36         $postObj = simplexml_load_string($sMsg, 'SimpleXMLElement', LIBXML_NOCDATA); 37         $RX_TYPE = trim($postObj->MsgType); 38  39         // 消息类型分离 40         switch ($RX_TYPE) 41         { 42             case "event": 43                 $sRespData = $this->receiveEvent($postObj); 44                 break; 45             case "text": 46                 $sRespData = $this->receiveText($postObj); 47                 break; 48             default: 49                 $sRespData = "unknown msg type: ".$RX_TYPE; 50                 break; 51         } 52         $this->logger(" RT /r/n".$sRespData); 53         // 加密 54         $sEncryptMsg = "";         // XML格式的密文 55         $errCode = $this->EncryptMsg($sRespData, $sReqTimeStamp, $sReqNonce, $sEncryptMsg); 56         $this->logger(" EC /r/n".$sEncryptMsg); 57         echo $sEncryptMsg; 58     } 59  60     // 接收事件消息 61     private function receiveEvent($object) 62     { 63         $content = ""; 64         switch ($object->Event) 65         { 66             case "subscribe": 67                 $content = "欢迎关注企业号"; 68                 break; 69             case "enter_agent": 70                 $content = "欢迎进入企业号应用"; 71                 break; 72             default: 73                 $content = "receive a new event: ".$object->Event; 74                 break; 75         } 76  77         $result = $this->transmitText($object, $content); 78         return $result; 79     } 80  81     // 接收文本消息 82     private function receiveText($object) 83     { 84         $keyword = trim($object->Content); 85         $content = time; 86         $result = $this->transmitText($object, $content); 87         return $result; 88     } 89  90     // 回复文本消息 91     private function transmitText($object, $content) 92     { 93         if (!isset($content) || empty($content)){ 94             return ""; 95         } 96  97         $xmlTpl = "<xml> 98                    <ToUserName><![CDATA[%s]]></ToUserName> 99                    <FromUserName><![CDATA[%s]]></FromUserName>100                    <CreateTime>%s</CreateTime>101                    <MsgType><![CDATA[text]]></MsgType>102                    <Content><![CDATA[%s]]></Content>103                    </xml>";104         $result = sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time,             $content);105 106         return $result;107     }108 109     // 日志记录110     public function logger($log_content)111     {112         $max_size = 500000;113         $log_filename = "log.xml";114         if(file_exists($log_filename) and (abs(filesize($log_filename)) > $max_size)){unlink            ($log_filename);}115         file_put_contents($log_filename, date('Y-m-d H:i:s').$log_content."/r/n",         FILE_APPEND);116     }117 }118 119 $wechatObj = new wechatCallbackapiTest($token, $encodingAesKey, $corpId);120 $wechatObj->logger(' http:// '.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].(empty    ($_SERVER['QUERY_STRING'])?"":("?".$_SERVER['QUERY_STRING'])));121 if (!isset($_GET['echostr'])) {122     $wechatObj->responseMsg;123 }else{124     $wechatObj->valid;125 } 

可以看到,企业号的回调模式和其他公众号的加解密方法基本上是一致的。

上述代码中,加解密消息部分在响应消息的函数responseMsg中,该部分解读如下。

第27~30行:解析出获取到的GET参数及微信POST过来的原始XML。

第31~35行:将取到密文写日志,然后进行解密。解密后将明文也写日志。

第36~52行:解析出XML类型为对象,然后根据事件类型分类处理,并得到要回复的XML明文。

第53~57行:将要回复的内容进行加密,并返回给接口。