微信公众平台开发

Coding Alan 9年前 (2015-09-09) 6382次浏览 0个评论 扫描二维码

Alan最近开始转战国内市场,个人感觉国内外的互联网环境还是有蛮大差别的,不论是独立站的运营还社交营销环境都有较大差别。鉴于微信在国内的火爆程度,谨以此帖研究一下微信公众平台开发的相关知识。

本帖以开发上的研究为主,所以对于如何申请就不再赘述,读者可以访问http://mp.weixin.qq.com按步骤进行申请。本文中将采用名为Alanhou的订阅号来进行测试。

第一步 提交验证接口

进行微信公众平台开发要求开发者拥有自己的主机,Alan将使用http://wechat.alanhou.org这个域名进行测试,登入微信公众账号后台,在左侧导航中找到开发者中心,在服务器配置版块点击修改配置,填写URL, Token和EncodingAESKey(可随机生成)并在消息加解密方式项下选择兼容模式

微信公众平台开发

此时在所提交的URL文件中加入如下代码(请根据实际情况替换$token值),然后点击提交按钮,会弹出一个提示窗口:

确定提交该配置?
提交后,新配置将立即生效。

点击确定按钮显示提交成功即表示接口验证成功,若提示Token验证失败可重复提交或检查下代码是否存在错误

<?php
	//1.将timestamp,nonce,token按字典序排序
	$timestamp = $_GET['timestamp'];
	$nonce	   = $_GET['nonce'];
	$token     = '随意在后台中指定';
	$signature = $_GET['signature'];
	$array     = array($timestamp, $nonce, $token);

	//2.将排序后的三个参数拼接之后用sha1加密
	$tmpstr = implode('', $array); //join
	$tmpstr = sha1($tmpstr);

	//3.将加密后的字符串与signature进行对比,判断该请求是否来自微信
	if($tmpstr == $signature){
		echo $_GET['echostr'];
		exit;
	}


注意:微信公众号接口只支持80接口

今天发现方倍工作室的一套教程讲得比较全面,所以参考其代码重新进行了下接口验证,方法同上,代码文件命名为index.php,所以在URL部分可以填写http://wechat.alanhou.org或http://wechat.alanhou.org/index.php,效果是一样的。index.php代码如下:

<?php
 //定义TOKEN常量,根据实际在后台中填写的Token进行修改
 define("TOKEN", "填写后添加的Token");
 //实例化下面添加的测试类
 $wechatObj = new wechatCallbackapiTest();
 //首次进行验证申请时,会返回echostr
 if(isset($_GET["echostr"])){
    $wechatObj->valid();
  }else{
    $wechatObj->responseMsg();
  }

  class wechatCallbackapiTest{
    //验证签名
    public function valid(){
      $echoStr = $_GET["echostr"];
      if($this->checkSignature()){
        header("content-type:text");
        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); //进行加密
      //将加密后的字符串与signature进行对比,判断该请求是否来自微信
      if($tmpStr == $signature){
        return true;
      }else{
        return false;
      }
    }
    //响应消息
    public function responseMsg(){
      $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

      if(!empty($postStr)){
        $postObj = simplexml_load_string($postStr, "SimpleXMLElement", LIBXML_NOCDATA);
        $fromUsername = $postObj->FromUserName;
        $toUsername = $postObj->ToUserName;
        $keyword = trim($postObj->Content);
        $time = time();
        $textTpl = "<xml>
              <ToUserName><![CDATA[%s]]></ToUserName>
              <FromUserName><![CDATA[%s]]></FromUserName>
              <CreateTime>%s</CreateTime>
              <MsgType><![CDATA[%s]]></MsgType>
              <Content><![CDATA[%s]]></Content>
              <FuncFlag>0<FuncFlag>
              </xml>";
        //回复中文或英文的问号,返回当前时间
        if($keyword == "?" || $keyword == "?"){
          $msgType = "text";
          $contentStr = date("Y-m-d H:i:s", time());
          $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
          echo $resultStr;
        }
      }else{
        echo "";
        exit;
      }
    }
  }

以上代码在responseMsg方法中定义了回复中英文问号返回当前时间的功能,启用服务器设置,测试效果如下(注:这里返回的时间没有进行过本地化处理,为GMT标准时间):

微信公众平台开发

接收消息后台XML格式详解:

ToUserName 消息接收方微信号,一般为公众平台账号微信号
FromUserName 消息发送方微信号
CreateTime 消息创建时间

MsgType 消息类型;文本消息为text
Content 消息内容

MsgType 消息类型;图片消息为image,
PicUrl 图片链接地址,可以用HTTP GET获取

MsgType 消息类型;语音消息为voice
MediaId 媒体ID
Format 语音格式,如amr

MsgType 消息类型;视频消息为video
MediaId 媒体ID
ThumbMediaId 媒体缩略ID

MsgType 消息类型,地理位置为location
Location_X 地理位置纬度
Location_Y 地理位置经度
Scale 地图缩放大小
Label 地理位置信息

MsgType 消息类型,链接为link
Title 图文消息标题
Description 图文消息描述
Url 点击图文消息跳转链接

MsgId 消息ID号

回复消息后台XML格式详解

FromUserName 消息发送方
ToUserName 消息接收方
CreateTime 消息创建时间

MsgType 消息类型,文本消息必须填写text
Content 消息内容,大小限制在2048字节,字段为空为不合法请求

MsgType 消息类型,图文消息必须填写news
Content 消息内容,图文消息可填空
ArticleCount 图文消息个数,限制为10条以内
Articles 多条图文消息信息,默认第一个item为大图
Title 图文消息标题
Description 图文消息描述
PicUrl 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80
Url 点击图文消息跳转链接

MsgType 消息类型,此处为music
Title 音乐标题
Description 音乐描述
MusicUrl 音乐链接
HQMusicUrl 高质量音乐链接,WIFI环境优先使用该链接播放音乐

MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)、CLICK(自定义菜单点击事件)
EventKey 事件KEY值,与自定义菜单接口中KEY值对应

FuncFlag 星标字段

事件XML格式详解

ToUserName 接收方微信号
FromUserName 发送方微信号,若为普通用户,则是一个OpenID
CreateTime 消息创建时间
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)、CLICK(自定义菜单点击事件)
EventKey 事件KEY值,与自定义菜单接口中KEY值对应

天气预报功能

实现这一功能将使用到方倍工作室的天气预报接口,http://apix.sinaapp.com/weather/

这个接口的参数appkey为公众号原始id,参数city为城市名。例如,查询深圳的天气预报时,将city值做urlencode,最终访问的url为:

http://apix.sinaapp.com/weather/?appkey=trialuser&city=%E6%B7%B1%E5%9C%B3

<?php
 //定义TOKEN常量,根据实际在后台中填写的Token进行修改
 define("TOKEN", "填写后台添加的Token");
 //实例化下面添加的测试类
 $wechatObj = new wechatCallbackapiTest();
 if(!isset($_GET["echostr"])){
   $wechatObj->responseMsg();
  }else{
    $wechatObj->valid();
  }

  class wechatCallbackapiTest{
    //验证签名
    public function valid(){
      $echoStr = $_GET["echostr"];
      $signature = $_GET["signature"];
      $timestamp = $_GET["timestamp"];
      $nonce = $_GET["nonce"];
      $token = TOKEN;
    
      $tmpArr = array($token, $timestamp, $nonce);
      sort($tmpArr); //字典排序
      $tmpStr = implode($tmpArr); //拼接排序后的数组
      $tmpStr = sha1($tmpStr); //进行加密
      //将加密后的字符串与signature进行对比,判断该请求是否来自微信
      if($tmpStr == $signature){
        echo $echoStr;
        exit;
      }
    }
    //响应消息
    public function responseMsg(){
      $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

      if(!empty($postStr)){
        $this->logger("R ".$postStr);
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $RX_TYPE = trim($postObj->MsgType);

        $result = "";
        switch($RX_TYPE){
          case "event":
            $result = $this->receiveEvent($postObj);
            break;
          case "text":
            $result = $this->receiveText($postObj);
            break;
        }
        $this->logger("T ".$result);
        echo $result;
      }else{
        echo "";
        exit;
      }
    }

    private function receiveEvent($object){
      switch($object->Event){
        case "subscribe":
          $content = "感谢关注Alanhou官方微信";
          break;
      }
      $result = $this->transmitText($object, $content);
      return $result;
    }

    private function receiveText($object){
      $keyword = trim($object->Content);
      $url = "http://apix.sinaapp.com/weather/?appkey=".$object->ToUserName."&city=".urlencode($keyword);
      $output = file_get_contents($url);
      $content = json_decode($output, true);

      $result = $this->transmitNews($object, $content);
      return $result;
    }

    private function transmitText($object, $content){
      if(!isset($content) || empty($content)){
        return "";
      }
      $textTpl ="<xml>
        <ToUserName><![CDATA[%s]]></ToUserName>
        <FromUserName><![CDATA[%s]]></FromUserName>
        <CreateTime>%s</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[%s]]></Content>
      </xml>";
      $result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
      return $result;
    }

    private function transmitNews($object, $newsArray){
      if(!is_array($newsArray)){
        return "";
      }
      $itemTpl = "<item>
          <Title><![CDATA[%s]]></Title>
          <Description><![CDATA[%s]]></Description>
          <PicUrl><![CDATA[%s]]></PicUrl>
          <Url><![CDATA[%s]]></Url>
        </item>";
        $item_str = "";
        foreach($newsArray as $item){
          $item_str .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
        }
        $newsTpl = "<xml>
          <ToUserName><![CDATA[%s]]></ToUserName>
          <FromUserName><![CDATA[%s]]></FromUserName>
          <CreateTime>%s</CreateTime>
          <MsgType><![CDATA[news]]></MsgType>
          <Content><![CDATA[]]></Content>
          <ArticleCount>%s</ArticleCount>
          <Articles>$item_str</Articles>
          </xml>";
        $result = sprintf($newsTpl, $object->FromUserName, $object->ToUserName, time(), count($newsArray));
        return $result;
    }

    private function logger($log_content){
      
    }
  }

实现效果如下:

微信公众平台开发

参考链接:http://www.cnblogs.com/txw1958/p/wechat-tutorial.html

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址