企業號在回調企業URL時,會對消息體本身做AES加密,以XML格式POST到企業應用的URL上;企業號在被動響應時,也需要對數據加密,以XML格式返回給微信。企業號的回復支持文本、圖片、語音、視頻、圖文等格式。
假設企業回調URL為http://www.doucube.com/qiyehao/index.php。
請求如下。
http:// www.doucube.com/qiyehao/index.php?msg_signature=cba357c1cfee7db580b8b7be69979c519dd9e2dd×tamp=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行:將要回復的內容進行加密,並返回給接口。