首页 网络安全 安全学院 查看内容

微信公众号开发总结

2016-8-1 16:59 2072 2

摘要: 公司准备在微信公众号上做个东西,所以研究了一周的微信公众号开发,今天在这里做一个总结。 在总结之前,先说一下本人使用的环境,语言是PHP,框架是CI和CI框架下的一个RESTFul框架codeigniter-restserver。一、配 ...

公司准备在微信公众号上做个东西,所以研究了一周的微信公众号开发,今天在这里做一个总结。 在总结之前,先说一下本人使用的环境,语言是PHP,框架是 CI 和CI框架下的一个RESTFul框架 codeigniter-restserver 。

一、配置服务器

在开发之前,我们首先需要在微信公众号下设置服务器配置,这里完全可以参考官方的 接入指南 。 需要说明两点的是:

  1. 我们只能填写一个URL(服务器地址),当微信服务器想要发送消息给我们服务器,只能通过这个URL来进行交互。
  2. 我们在接入的时候,有个验证消息是否来自微信服务器的过程,这个验证过程是GET请求,我们需要输出echostr,验证代码如下:
/**
 * 检查消息是否来自微信
 * @return bool
 */
private function check_from_wx() {
    //检查$_GET中的参数
    param_check($_GET, ['signature', 'timestamp', 'nonce']);

    //获取参数
    $signature = $_GET["signature"];
    $timestamp = $_GET["timestamp"];
    $nonce = $_GET["nonce"];

    $token = WX_APP_TOKEN;
    $tmpArr = array($token, $timestamp, $nonce);
    // use SORT_STRING rule
    sort($tmpArr, SORT_STRING);
    $tmpStr = implode($tmpArr);
    $tmpStr = sha1($tmpStr);

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

/**
 * 配合微信服务器验证是否有效
 */
public function check_get() {
    $echoStr = $this->get("echostr");
    if($this->check_from_wx()) {
       echo $echoStr;
    }
    else {
        echo "验证不通过";
    }
    exit;
}

看到上面的代码,你也许会有疑问,为什么在 check_get 方法中使用 $this->get 获取get参数,而在 check_from_wx 方法中使用的是 $_GET 。这是因为我们后面接收的消息是微信服务器向我们发送POST请求,但是验证的参数却是放在URL后面,所以为了兼容,专门封装了一个 check_from_wx 的私有方法。

到这里,我们算是配置好了,下面我们来看看如何接收和回复用户发来的消息。

二、接收消息和回复消息

接收和回复消息的流程图大概就是这样的:看上面的流程图,我们可以把整个接收和回复消息分成四个步骤:

  • 第一步:用户在微信公众号内发送一条消息,微信客户端将这条消息发送给微信服务器。
  • 第二步:微信服务器将消息以POST方式将消息提交给我们服务器,而这个服务器的地址只有一个,就是我们前面配置服务器填写的URL地址。消息的数据格式是XML格式的。
  • 第三步:我们收到这个消息之后,做出对应的回复,返回对应的XML数据,就算是进行回复了。
  • 第四步:微信服务器将我们服务器的消息返回给微信客户端,这样用户就看到了我们回复的消息了。

看了上面的消息接收和回复流程图,我们下面使用代码进行实现。在配置服务器的时候,我先前填写的URL地址对应的接口是 check ,所以接收消息的PHP代码这么写:

/**
 * 检查消息是否来自微信
 * @return bool
 */
private function check_from_wx() {
    //检查$_GET中的参数
    param_check($_GET, ['signature', 'timestamp', 'nonce']);

    //获取参数
    $signature = $_GET["signature"];
    $timestamp = $_GET["timestamp"];
    $nonce = $_GET["nonce"];

    $token = WX_APP_TOKEN;
    $tmpArr = array($token, $timestamp, $nonce);
    // use SORT_STRING rule
    sort($tmpArr, SORT_STRING);
    $tmpStr = implode($tmpArr);
    $tmpStr = sha1($tmpStr);

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

public function check_post() {
    //检查消息是否来自微信
    if(!$this->check_from_wx()) {
        //非法
        echo "非法操作";
    }

    //获取POST参数
    $param = file_get_contents("php://input");
    //将xml格式中的数据读取成数组
    $param = $this->format->factory($param, 'xml')->to_array();

    //将消息插入by_message表中
    $result = $this->share_model->insert($msg_info, 'by_message');

    //回复空字符串
    echo "";
   }

上面代码值得注意有三个地方,第一个地方是我们检查消息是否来自微信,是获取$_GET 中的参数;第二个地方是我们不能使用 $_POST 和 $this->post 来获取post参数,只能使用 file_get_contents("php://input") 来获取;第三个地方是当我们没有消息回复的时候,回复 success和空字符串 就代表交互成功,否则用户就会看到 该公众号暂时无法提供服务 。

当写好代码之后,我们在微信公众号中发送消息,它就会将消息存入到by_message 表中,我们就可以进行查看。如果出现 该公众号暂时无法提供服务 ,那就可能我们服务器出错误了,可以开启日志功能,使用 log_message 输出日志进行调试。

当接收消息没问题之后,我们就可以进行回复消息了,代码如下:

public function check_post() {
    //检查消息是否来自微信
    if(!$this->check_from_wx()) {
        //非法
        echo "非法操作";
    }

    //获取POST参数
    $param = file_get_contents("php://input");
    //获取xml中的数据
    $param = $this->format->factory($param, 'xml')->to_array();

    //选出参数
    $msg_info = array_choose($param, ['Content', 'ToUserName', 'FromUserName', 'MsgId', 'MsgType']);
    //将消息插入数据库
    $result = $this->share_model->insert($msg_info, 'by_message');

    //获取信息
    $to_user = $param['ToUserName'];
    $from_user = $param['FromUserName'];
    $content = $param['Content'];

    //去掉消息id
    unset($param['MsgId']);

    //回复的内容
    $content = 'http://xxxxxx.com/';
    $response_text = "<xml>
                        <ToUserName><![CDATA[%s]]></ToUserName>
                        <FromUserName><![CDATA[%s]]></FromUserName>
                        <CreateTime>%s</CreateTime>
                        <MsgType><![CDATA[%s]]></MsgType>
                        <Content><![CDATA[%s]]></Content>
                      </xml>";
    $response_text = sprintf($response_text, $from_user, $to_user, time(), $param['MsgType'], $content);
    echo $response_text;
}

配置好之后,我们在微信公众输入字符,它就会回复 http://xxxxxx.com/ 。

上面代码需要注意的是,我们回复消息的时候,返回的数据格式是 xml格式 的,而且对格式有严格要求。我前面使用框架中的 format类 生成xml的数据是无法被微信读取的,所以建议和我上面一样的写法。

还有,上面回复的消息是文本消息,我们还可以回复图片、图文、语音、视频、音乐等消息,其实它们都大体相同,查看官方的文档 被动回复用户消息 这一节,按照对应的格式进行回复就行了。

到这一步的时候,是不是很有成就感?其实,这还是很小的一步,微信开发还有很多的内容,就消息管理这部分来说,它就分接收和发送消息两大类。

其中,接收消息有两种:

  • 第一种:接收普通消息,它是微信用户向公众号发送的消息,我们上面说的就是接收普通消息。
  • 第二种:接收事件消息,它是微信用户在微信公众号里面交互过程中产生的事件消息,例如关注/取消事件、扫描带参数二维码等等。具体可以接收哪些事件消息,就去查询微信的开发文档吧。接收事件消息说起来很高端的样子,其实它和普通消息差不多,整个接收和回复流程和上面一样,只是有的事件消息是不允许我们回复用户的。

而发送消息就有被动 回复消息 、 客服消息 、 群发消息 、 模板消息 四种,其中这四种我又把它分成两小类,被动回复消息算是一类,我们前面实现对用户消息的回复就算是这一类;另外三种我将它们归类为主动发送消息,与被动回复消息不同的是,它会被微信主动推送给用户,流程大概如下图:

了解了流程之后,我们下面来实现模板消息的发送。

三、发送模板消息

模板消息可以定制,而且发送模板消息后,微信会主动推送给用户,这是我们开发很需要的一个功能。(注:模板消息只有认证后的服务号才可以使用)。

首先,我们在微信公众平台的 功能->添加功能插件 处添加这个功能,进入模板消息页面,从模板库中添加一个模板消息,获取到模板ID。当然,我们也可以创建一个符号自己业务的模板消息,进行定制(这个需要申请)。

然后,我们对应着模板详情的数据格式,写一个接口专门用来发送模板消息。

下面是模板详情:

对应上面的模板消息,我们的接口这么写:

/**
     * 发送模板消息
     */
    public function send_template_message_get() {
        //检查参数
        param_check($this->get(), ['uid']);
        $uid = $this->get('uid');

        //wx appid
        $wx_app_id = WX_APP_ID;
        $wx_app_secret = WX_APP_SECRET;

        //get access token
        $token_info = file_get_contents("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$wx_app_id&secret=$wx_app_secret");
        $token_info = json_decode($token_info, true);
        $access_token = $token_info['access_token'];

        //获取用户信息
        $openid = $this->user_model->get_value('openid', $uid);

        //组织参数
        $param = array(
            "touser" => $openid,
            "template_id" => "jt_Rl5X9QWXMiRihrQz67n4riGt3kaPA81Zku0wLm9M",
            "url" => "http://www.beyondwinlaw.com/test/wx/test/home.html",
            "data" => [
                "first" => [
                    "value" => "案件有新进展",
                    "color" => "#173177"
                ],
                "keyword1" => [
                    "value" => "jkfdjafjdsfjdjfs",
                    "color" => "#173177"
                ],
                "keyword2" => [
                    "value" => "jkfdjafjdsfjdjfs",
                    "color" => "#173177"
                ],
                "keyword3" => [
                    "value" => "jkfdjafjdsfjdjfs",
                    "color" => "#173177"
                ],
                "keyword4" => [
                    "value" => "2016-07-12 11:11:11",
                    "color" => "#173177"
                ],
                "keyword5" => [
                    "value" => "jkfdjafjdsfjdjfs",
                    "color" => "#173177"
                ],
                "remark" => [
                    "value" => "点击查看案件进度详情",
                    "color" => "#173177"
                ]
            ]
        );

        //发送请求
        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=$access_token";
        $result = send_post($url, $param);
        answer([
            "result" => $result
        ]);
    }

效果是这样的:

这样,我们就实现了模板消息的发送,至于客服消息、群发接口,原理差不多,因为我没实现过,这里就不多说了。

四、网页授权,获取用户信息

微信采用的是OAuth对开发者进行授权的,具体OAuth授权原理请google查询。在开发之前,我们需要先到公众平台官网中的 开发-接口权限-网页服务-网页授权获取用户基本信息 的配置选项中,修改授权回调域名。

整个授权过程,其实微信官方已经说的很清楚了。我这里简略说一下,算是总结下吧!

首先,我们需要获取access_token,它的时序图和微信开放平台类似,如下:

这整个过程是当用户要登录我们的网站时,我们带上回调地址、AppId、scope等参数跳转到微信授权页面;然后获得用户的同意之后,它会跳转到我们的回调地址,并带上code参数;最后我们通过code、AppId、AppSecret请求接口,获取access_token。

之后,我们通过access_token请求对应的接口,就可以获取用户的基本信息了。

需要注意地方有两个:

  1. 我们的回调地址需要进行encodeURL,否则可能回调地址中url后面的参数会丢失。
  2. 我们的回调地址的域名必须是前面配置的域名。

具体的实现细节,根据官方文档 微信网页授权 和 网站应用微信登录开发指南 的步骤来就行了。

五、JS-SDK的使用

在我们要做的产品中,我们希望能够控制每个页面分享的链接,而JS-SDK就可以做到这一切。 它的使用步骤可以查阅 微信JS-SDK说明文档 这个文档,而且在网页最后还有对应的 DEMO页面 和 示例代码 。

我们的实现是这样的,在前端专门写了一个JS文件 wx_share.js ,这个JS文件中将当前的url传给后台,请求后台的数据对JS-SDK进行配置。代码如下:

var server_url = "http://192.168.30.249/by/wx_api/index.php/share/wx_config";
var url = window.location.href;

//配置微信
$.ajax({
    type: "GET",
    url: server_url,
    data: {
        url: encodeURI(url)
    },
    dataType: "json",
    success: function(data){
        // alert('请求分享配置数据成功');
        wx.config({
            debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: data.data.appId, // 必填,公众号的唯一标识
            timestamp: data.data.timestamp, // 必填,生成签名的时间戳
            nonceStr: data.data.nonceStr, // 必填,生成签名的随机串
            signature: data.data.signature,// 必填,签名,见附录1
            jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
        });
        wx.ready(function() {
            // alert("分享验证完毕");
        });
        wx.error(function() {
            // alert("分享验证失败");
        });

        //获取uid和link_id
        var uid = localStorage.getItem("uid");
        var linkId = localStorage.getItem("link_id");
        var shareUrl = "http://www.baidu.com";
        //存在linkId, 则分享带上link_id等参数
        if(linkId != undefined && linkId != "" && linkId != null) {
          shareUrl = shareUrl + "?super_id=" + uid + "&link_id=" + linkId;
        }
        // alert("分享链接: "+shareUrl);

        wx.onMenuShareAppMessage({
            title: '分享测试', // 分享标题
            desc: '测试一下', // 分享描述
            link: shareUrl, // 分享链接
            imgUrl: 'http://www.beyondwinlaw.com/gw4/images/zhongjie.jpg', // 分享图标
            type: 'link', // 分享类型,music、video或link,不填默认为link
            dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
            success: function () {
                // 用户确认分享后执行的回调函数
                // alert("分享成功");
            },
            cancel: function () {
                // 用户取消分享后执行的回调函数
                // alert("取消分享");
            }
        });
    },
    error: function(data) {
        alert('请求分享配置数据失败');
    }
});

而具体JS-SDK权限签名算法的实现是放在后台的,这个实现我是直接使用官方的 jssdk 的类,只是将它稍微修改了下。 share.php 中 wx_config 接口实现如下:

/**
 * 获取微信配置
 */
public function wx_config_get() {
    //获取参数
    param_check($this->get(), ['url']);
    $url = $this->get('url');
    $url = urldecode($url);

    $jssdk = new JSSDK(WX_APP_ID, WX_APP_SECRET, $url);
    $signPackage = $jssdk->GetSignPackage();
    answer($signPackage);
}

而修改后的JS-SDK代码如下:

                      声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 
                      [邮箱地址] 删除
                    

路过

雷人

握手

鲜花

鸡蛋

返回顶部