基本原理

这段时间因为工作需要研究了一下微信公众号的开发,发现挺有意思,大致原理就是,申请一个微信公众号,设置URL、TOKEN,用户通过微信服务器,微信服务器在访问第三方服务器,进行一些交互动作。
申请服务器,公众号,还有设置开发中心URL不说了,网上教程太多了。
大致步骤:
用户发送信息给公众号->微信服务器->通过URL转发给第三方服务器->第三方处理逻辑返回信息给微信服务器->微信服务器返回给用户
除了申请公众号,测试号也是可以的
测试号网址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
微信服务器和第三方服务器具体的交互原理:
在提交URL和TOKEN验证时,微信服务器会向URL发送带参数的GET请求,带有signature、timestamp、nonce、echoStr四个参数。
具体看一段代码,在进行分析,以PHP代码为例:

        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);
        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }

$token, $timestamp, $nonce三个参数进行字典序排序,然后将三个参数拼接成一个字符串进行sha1加密,,然后将获得的加密字符串和$signature进行对比,标识该请求来自微信。若确认此次GET请求来自微信服务器,就返回echostr参数内容,则接入生效,否则接入失败。

微信和第三方服务器通信都是用的xml,格式如下

<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
<FuncFlag>0<FuncFlag>
</xml>

第三方服务器返回信息也得封装成这种格式。

完整代码

贴一个完整代码,除了验证,还实现了简易的回复功能。

<?php
define("TOKEN", "denry666");
$wechatObj = new wechatCallbackapiTest();
if (!isset($_GET['echostr'])) {
    $wechatObj->responseMsg();
}else{
    $wechatObj->valid();
}
class wechatCallbackapiTest
{
    public function valid()
    {
        $echoStr = $_GET['echostr'];
        if ($this->checkSignature()) {
            echo $echoStr;
            exit;
        }
    }

    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);

        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }

    public function responseMsg()
    {
       $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        if (!empty($postStr)) {
            $this->logger("接收 ".$postStr);
        //解析XML
           libxml_disable_entity_loader(true);
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $keyword = trim($postObj->Content);
            if ($keyword == '你好') {
                $content = "你好小可爱";
                 $resultStr = $this->transmitText($postObj,$content);
                echo   $resultStr;
            } else if($keyword == '时间') {
                $content = date('Y-m-d H:i:s');
                 $resultStr = $this->transmitText($postObj,$content);
                echo   $resultStr;
            } else {
                 $content = "未识别你说的啥";
                 $resultStr = $this->transmitText($postObj,$content);
                echo   $resultStr;
            }

        } else {
            echo 'success';
            exit;
        }
    }

    private 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><FuncFlag>0<FuncFlag></xml>";
        $result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
         $this->logger("发送 ".$result);
        return $result;
    }


  private function logger($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);
        }
    }

}

除了简易的封装了文本,还有一个logger,会把微信传输,服务器本身发送的xml都写入log.xml文件,这样消息记录,调试都会方便一些。
当然微信公众平台还提供了其他图片视频音频之类的消息,还有自定义菜单很多功能,现在这个代码很简略,后期在进行完善。

参考资料:微信公众平台https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432
方倍工作室博客:http://www.cnblogs.com/txw1958/p/wechat-tutorial.html

最后修改:2019 年 02 月 01 日
如果觉得我的文章对你有用,请随意赞赏