我们将使用微信公众账号方倍工作室作为讲解的例子,二维码见底部。
本系列教程将引导你完成如下任务:
创建新浪云计算平台应用启用微信公众平台开发模式基础接口消息及事件微信公众平台PHP SDK微信公众平台开发模式原理开发天气预报功能
第一章 申请服务器资源
创建新浪云计算应用
申请账号
我们使用SAE新浪云计算平台作为服务器资源,并且申请PHP环境+MySQL数据库作为程序运行环境。
申请地址:http://sae.sina.com.cn/ ,使用新浪微博账号可以直接登录SAE,登录后SAE将赠送500个免费云豆。
创建新应用
登录后点击顶部【我的首页】
点击下侧的创建新应用,这时会弹出提示, 禁止放置违法违规内容,点击继续创建,弹出如下窗口。
选择一个未使用的appid,如果老是已经被使用不知道该什么好,就填写你的QQ号或者手机号吧。
填写二级域名AppID、应用名称、验证码,开发语言选择PHP,应用类型选择web应用。然后点击创建应用
应用创建成功。并自动跳转到应用列表中,可以看到已经有刚才创建的CCTV-7
创建版本
选择CCTV-7右侧的应用管理下面的代码管理,
跳转到代码管理
点击右侧的
版本号默认为1,点击创建,成功后如下图所示:
到这里,就成功创建了一个域名URL为 http://cctv7.sinaapp.com/ 的应用了。
上传代码
将以下代码复制下来,另存为index.php。必须使用专业的开发编辑软件操作,例如Notepad++,不要使用Windows自带的记事本等。
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
if (isset($_GET['echostr'])) {
$wechatObj->valid();
}else{
$wechatObj->responseMsg();
}
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);
$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)){
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$fromUsername = $postObj->FromUserName;
$toUsername = $postObj->ToUserName;
$keyword = trim($postObj->Content);
$time = time();
$textTpl = "
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;
}
}
}
?>
然后将index.php文件压缩成ZIP格式,注意不能用RAR格式
这样会生成一个index.zip的文件。或者直接下载方倍已经压缩好的zip文件 点此下载
在代码管理界面中,选择操作按钮。
选择上传代码包。
点击上传文件,选择刚才压缩好的index.zip文件,点击上传,上传成功后如下所示,如果上传有问题,请在Chrome浏览器下重试一下。
点击操作按钮下的代码编辑,
我们可以看到index.php已经上传成功,双击可以查看编辑里面的代码
新浪云应用的创建就成功了。
第二章 启用开发模式
微信公众平台开发模式
高级功能
微信公众平台地址:https://mp.weixin.qq.com
登录微信公众平台后台,选择高级功能,进入后就看到两种模式
我们需要先关闭编辑模式。点击编辑模式的进入
滑动关闭
开发模式
进入开发模式里面
点击成为开发者
弹出URL和Token填写框
此处的URL为上篇中介绍的云应用的域名,而Token在index.php中定义为weixin。提交后提示你已成为开发者。
再滑动右上角启用按钮。
恭喜,你成功启用开发模式。
自动回复
在上面的例子中,实现了一个发送“?”就能回复当前时间的功能。
效果如下:
至此,你的微信公众平台账号已经实现自动回复了。
第三章 基础接口消息及事件
所有账号在申请之后,都将获得基础接口的权限,基础接口中将包括接收用户消息,向用户回复消息,接受事件推送等三种服务。
接收用户消息
目前普通用户能向公众账号推送五种格式的消息:文本(包括表情)、语音、图片、视频、位置、链接。
下面就这五种分别详解如下:
1. 文本(包括表情)
发送文本及表情
2. 图片
发送图片
3. 语音
发送语音
4. 视频
发送视频
5. 位置
发送位置
6. 链接
发送链接
向用户回复消息
目前普通公众账号能向用户推送六种格式的消息:文本、图文、音乐、图片、语音、视频。其中图文消息包括单条图文消息和多条图文消息,展示方式有一点点不同。
下面就这几种分别详解如下:【图片、语音、视频由于需要用到和高级接口相关的media_id,在本教程中暂不讨论。】
1. 文本消息格式
回复文本
2. 图文消息格式
2.1 单条图文消息
回复单条图文
2.2 多图文消息
回复多图文
3. 音乐消息
回复音乐消息
接收事件推送
目前用户在关注和取消关注,以及点击菜单的时候会自动向公众平台发送事件推送消息:
1. 关注事件
第四章 微信公众平台PHP SDK
方倍工作室开发了微信公众平台的PHPSDK,集成了目前所有消息及事件的接收及发送,代码如下:
define("TOKEN", "weixin");
$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("R ".$postStr);
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$RX_TYPE = trim($postObj->MsgType);
switch ($RX_TYPE)
{
case "event":
$result = $this->receiveEvent($postObj);
break;
case "text":
$result = $this->receiveText($postObj);
break;
case "image":
$result = $this->receiveImage($postObj);
break;
case "location":
$result = $this->receiveLocation($postObj);
break;
case "voice":
$result = $this->receiveVoice($postObj);
break;
case "video":
$result = $this->receiveVideo($postObj);
break;
case "link":
$result = $this->receiveLink($postObj);
break;
default:
$result = "unknown msg type: ".$RX_TYPE;
break;
}
$this->logger("T ".$result);
echo $result;
}else {
echo "";
exit;
}
}
//接收事件消息
private function receiveEvent($object)
{
$content = "";
switch ($object->Event)
{
case "subscribe":
$content = "欢迎关注方倍工作室 ";
$content .= (!empty($object->EventKey))?("\n来自二维码场景 ".str_replace("qrscene_","",$object->EventKey)):"";
break;
case "unsubscribe":
$content = "取消关注";
break;
case "SCAN":
$content = "扫描场景 ".$object->EventKey;
break;
case "CLICK":
switch ($object->EventKey)
{
case "COMPANY":
$content = "方倍工作室提供互联网相关产品与服务。";
break;
default:
$content = "点击菜单:".$object->EventKey;
break;
}
break;
case "LOCATION":
$content = "上传位置:纬度 ".$object->Latitude.";经度 ".$object->Longitude;
break;
case "VIEW":
$content = "跳转链接 ".$object->EventKey;
break;
default:
$content = "receive a new event: ".$object->Event;
break;
}
$result = $this->transmitText($object, $content);
return $result;
}
//接收文本消息
private function receiveText($object)
{
switch ($object->Content)
{
case "文本":
$content = "这是个文本消息";
break;
case "图文":
case "单图文":
$content = array();
$content[] = array("Title"=>"单图文标题", "Description"=>"单图文内容", "PicUrl"=>"http://discuz.comli.com/weixin/weather/icon/cartoon.jpg", "Url" =>"http://m.cnblogs.com/?u=txw1958");
break;
case "多图文":
$content = array();
$content[] = array("Title"=>"多图文1标题", "Description"=>"", "PicUrl"=>"http://discuz.comli.com/weixin/weather/icon/cartoon.jpg", "Url" =>"http://m.cnblogs.com/?u=txw1958");
$content[] = array("Title"=>"多图文2标题", "Description"=>"", "PicUrl"=>"http://d.hiphotos.bdimg.com/wisegame/pic/item/f3529822720e0cf3ac9f1ada0846f21fbe09aaa3.jpg", "Url" =>"http://m.cnblogs.com/?u=txw1958");
$content[] = array("Title"=>"多图文3标题", "Description"=>"", "PicUrl"=>"http://g.hiphotos.bdimg.com/wisegame/pic/item/18cb0a46f21fbe090d338acc6a600c338644adfd.jpg", "Url" =>"http://m.cnblogs.com/?u=txw1958");
break;
case "音乐":
$content = array("Title"=>"最炫民族风", "Description"=>"歌手:凤凰传奇", "MusicUrl"=>"http://121.199.4.61/music/zxmzf.mp3", "HQMusicUrl"=>"http://121.199.4.61/music/zxmzf.mp3");
break;
default:
$content = date("Y-m-d H:i:s",time());
break;
}
if(is_array($content)){
if (isset($content[0]['PicUrl'])){
$result = $this->transmitNews($object, $content);
}else if (isset($content['MusicUrl'])){
$result = $this->transmitMusic($object, $content);
}
}else{
$result = $this->transmitText($object, $content);
}
return $result;
}
//接收图片消息
private function receiveImage($object)
{
$content = array("MediaId"=>$object->MediaId);
$result = $this->transmitImage($object, $content);
return $result;
}
//接收位置消息
private function receiveLocation($object)
{
$content = "你发送的是位置,纬度为:".$object->Location_X.";经度为:".$object->Location_Y.";缩放级别为:".$object->Scale.";位置为:".$object->Label;
$result = $this->transmitText($object, $content);
return $result;
}
//接收语音消息
private function receiveVoice($object)
{
if (isset($object->Recognition) && !empty($object->Recognition)){
$content = "你刚才说的是:".$object->Recognition;
$result = $this->transmitText($object, $content);
}else{
$content = array("MediaId"=>$object->MediaId);
$result = $this->transmitVoice($object, $content);
}
return $result;
}
//接收视频消息
private function receiveVideo($object)
{
$content = array("MediaId"=>$object->MediaId, "ThumbMediaId"=>$object->ThumbMediaId, "Title"=>"", "Description"=>"");
$result = $this->transmitVideo($object, $content);
return $result;
}
//接收链接消息
private function receiveLink($object)
{
$content = "你发送的是链接,标题为:".$object->Title.";内容为:".$object->Description.";链接地址为:".$object->Url;
$result = $this->transmitText($object, $content);
return $result;
}
//回复文本消息
private function transmitText($object, $content)
{
$textTpl = "
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
return $result;
}
//回复图片消息
private function transmitImage($object, $imageArray)
{
$itemTpl = "
$item_str = sprintf($itemTpl, $imageArray['MediaId']);
$textTpl = "
$item_str
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time());
return $result;
}
//回复语音消息
private function transmitVoice($object, $voiceArray)
{
$itemTpl = "
$item_str = sprintf($itemTpl, $voiceArray['MediaId']);
$textTpl = "
$item_str
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time());
return $result;
}
//回复视频消息
private function transmitVideo($object, $videoArray)
{
$itemTpl = "";
$item_str = sprintf($itemTpl, $videoArray['MediaId'], $videoArray['ThumbMediaId'], $videoArray['Title'], $videoArray['Description']);
$textTpl = "
$item_str
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time());
return $result;
}
//回复图文消息
private function transmitNews($object, $newsArray)
{
if(!is_array($newsArray)){
return;
}
$itemTpl = "
";
$item_str = "";
foreach ($newsArray as $item){
$item_str .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
}
$newsTpl = "
$item_str
$result = sprintf($newsTpl, $object->FromUserName, $object->ToUserName, time(), count($newsArray));
return $result;
}
//回复音乐消息
private function transmitMusic($object, $musicArray)
{
$itemTpl = "
$item_str = sprintf($itemTpl, $musicArray['Title'], $musicArray['Description'], $musicArray['MusicUrl'], $musicArray['HQMusicUrl']);
$textTpl = "
$item_str
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time());
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);
}
}
}
?>
在公众账号中回复以下文字,你将得到和上一章一样的回复内容。
还可以尝试以下操作,体验一下其他消息
发送一张图片给公众账号
发送一段语音给公众账号
发送一段视频给公众账号
发送位置信息给公众账号
发送收藏中的链接给公众账号第五章 微信公众平台开发模式原理分析
在体验了上一节的各种功能之后,我们只是知其然,这一节里面,将介绍在上面的基础上介绍微信公众平台收发消息机制及原理,这是知其所以然。
开发模式成为开发者时的消息校验原理
在开发者首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,并且带上四个参数(signature、timestamp、nonce、echostr),开发者通过对签名(即signature)的效验,来判断此条消息的真实性。
此后,每次开发者接收用户消息的时候,微信也都会带上前面三个参数(signature、timestamp、nonce)访问开发者设置的URL,开发者依然通过对签名的效验判断此条消息的真实性。效验方式与首次提交验证申请一致。
参数 | 描述 |
---|---|
signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
加密/校验流程如下:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信启用接口是由代码中的checkSignature()函数来实现校验的。如果对这一原理难以理解,可以暂时不用深究,继续看下面。
成为开发者后消息收发时的原理
再来看下这个图,当用户发送一个“?”时,系统回复了一个时间
这一原理的消息流程图如下所示。
从上图可以看出,用户在发送一个?后,微信服务器将组装一个消息发送给我们自己的服务器,自己的服务器然后回复一个时间,并且将该时间也按一定的规则组装,回复给公众账号,公众账号再回复给用户,在这个收发过程中,发送方和接收方进行了调换(ToUserName和FromUserName值互换),收发都是以xml格式在后台进行传输的,
所以掌握各种消息类型的收发就是进行微信公众平台开发的基础!
下面对前面所述的各种消息类型讲解其XML数据包的格式。
各种收发消息的XML数据包分析
接收消息
1. 文本(包括表情)
发送文本及表情
文字后台格式:
表情后台格式
XML格式讲解
可以看出,文本和表情的消息类型均为文本
2. 图片
发送图片
后台格式:
XML格式讲解
3. 语音
发送语音
后台格式:
XML格式讲解
附:AMR接口简介
全称Adaptive Multi-Rate,主要用于移动设备的音频,压缩比比较大,但相对其他的压缩格式质量比较差,由于多用于人声,通话,效果还是很不错的。
4. 视频
发送视频
后台格式:
XML格式讲解
5. 位置
发送位置
后台格式:
XML格式讲解
6. 链接
发送链接
后台格式:
XML格式讲解
发送消息
只介绍三种格式的消息:文本、图文、音乐。其中图文消息包括单条图文消息和多条图文消息,展示方式有一点点不同。
1. 文本消息格式
回复文本
后台格式:
XML格式讲解
2. 图文消息格式
2.1 单条图文消息
回复单条图文
后台格式:
2.2 多图文消息
回复多图文
后台数据格式
XML格式讲解
3. 音乐消息
回复音乐消息
后台格式:
XML格式讲解
事件消息类型
目前用户在关注和取消关注,以及点击菜单的时候会自动向公众平台发送事件推送消息:
1. 关注事件
2. 取消关注事件
3. 菜单点击事件
XML格式讲解
第六章 开发天气预报功能
这一章里,我们来快速开发天气预报功能、我们使用方倍工作室的相应接口来实现。下面代码实现了这样该功能。
define("TOKEN", "weixin");
$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("R ".$postStr);
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$RX_TYPE = trim($postObj->MsgType);
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)
{
$content = "";
switch ($object->Event)
{
case "subscribe":
$content = "欢迎关注方倍工作室 ";
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)
{
$textTpl = "
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
return $result;
}
private function transmitNews($object, $newsArray)
{
if(!is_array($newsArray)){
return;
}
$itemTpl = "
";
$item_str = "";
foreach ($newsArray as $item){
$item_str .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
}
$newsTpl = "
$item_str
$result = sprintf($newsTpl, $object->FromUserName, $object->ToUserName, time(), count($newsArray));
return $result;
}
private function logger($log_content)
{
}
}
?>
在公众账号中使用的命令如下:
1.发送城市名称,如“深圳”,可以查询该城市的天气
在你的公众账号输入相应的命令,实现效果类似如下所示:
第七章 小结
总的来说,通过本教程,你得到了以下收获:
1. 你通过本教程得到了一个免费的云计算空间2. 你成功启用了开发模式,并且实现了时间的自动回复3. 你非常快速地就体验了各种消息接收及发送,比方倍当年自己摸索所用的时间短了很多4. 你了解了微信公众平台开发的原理,并且熟悉了各种消息及发送是怎么一回事5. 你使用方倍工作室的接口,成功的开发了你的第一个微信公众平台功能——天气预报。
接下来该做什么呢?你可以学习开发一些基础的常用功能,推荐:方倍工作室 编写,机械工业出版社 出版的《微信公众平台开发最佳实践》,里面包含很多php开发技巧、数据库使用、及近30项微信公众平台实用功能或技术 。