1、准备
首先你得有一个微信公众号,一个服务器
没有服务器可以用新浪云服务 https://sae.sina.com.cn
选择 云应用SAE -> 添加新应用
代码上传:SAE支持Git、SVN、代码打包上传三种提交方式
例如:git方式:
在你应用的git代码目录里,添加一个新的git远程仓库 sae
$ git remote add sae https://git.sinacloud.com/appname
编辑代码并将代码部署到 sae 的版本1。
$ git add .
$ git commit -am “bababa”
$ git push sae master:1
代码上传后,自动生成 http://1.appname.applinzi.com/
这就是第一个应用的服务地址
2、公众号配置和初始
开发手册:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
初始代码:
三个必要函数:
public function valid()
private function checkSignature()
public function responseMsg()
参数配置
http://mp.weixin.qq.com/mpres/htmledition/res/wx_sample.20140819.zipwx_sample.php
define("TOKEN", "weixin");//可将‘weixin'改为自定义的token
$wechatObj = new wechatCallbackapiTest();//实例化一个微信接口类,包含了验证函数、消息和事件处理函数
$wechatObj->valid();//验证请求是否来源于微信
使用新浪sae时,需要在代码前加上:header('Content-type:text');
不加的话可能会出现“验证token失败”的错误
token验证函数
微信发送过来的验证token请求
class wechatCallbackapiTest
{
    public function valid()//验证合法性函数
    {
        $echoStr = $_GET["echostr"];
        //valid signature , option
        if($this->checkSignature()){
            echo $echoStr;
            exit;
        }
    }
    private function checkSignature()//签名验证函数
    {
        // you must define TOKEN by yourself
        if (!defined("TOKEN")) {
            throw new Exception('TOKEN is not defined!');
        }
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        $token = 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 responseMsg()
    {
    //处理消息
    }
}
将token、timestamp、nonce三个参数作为字符串进行排序,
将三个参数字符串拼接成一个字符串进行sha1加密,
开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
公众号配置:
开发->基本配置->修改配置->启用
3、功能开发
消息处理函数 public function responseMsg()
public function responseMsg()
    {
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
          //extract post data
        if (!empty($postStr)){
                /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
                   the best way is to check the validity of xml by yourself */
                libxml_disable_entity_loader(true);
                  $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(!empty( $keyword ))
                {
                      $msgType = "text";
                    $contentStr = "Welcome to wechat world!";
                    $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                    echo $resultStr;
                }else{
                    echo "Input something...";
                }
        }else {
            echo "";
            exit;
        }
    }
微信发送过来,和返回的消息都是xml$postStr: 接收到的消息
post过来的数据不是PHP能够识别的,用 $GLOBALS[‘HTTP_RAW_POST_DATA’]来接收$fromUsername: 发送方帐号$toUsername: 开发者微信号$keyword: 用户发送给该公众号的微信消息
$msgType: 
- text、image、voice、video、shortvideo、location、link :接收的消息类型/发送的消息类型
 - event:接收到的为事件消息(关注/取关,扫描带二维码、上报地理位置、自定义菜单)
 
手册:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140454&token=&lang=zh_CN
在实际代码中,都是先判断消息类型,然后选择该进入哪一个处理函数
简化后的函数 responseMsg()
public function responseMsg()
    {
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        if (!empty($postStr)){
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $RX_TYPE = trim($postObj->MsgType);
        switch ($RX_TYPE)
        {
            case "text":
                 $resultStr = $this->receiveText($postObj);
                 break;
            case "event":
                 $resultStr = $this->receiveEvent($postObj);
                 break;
        }
        echo $resultStr;
        }else {
            echo "";
            exit;
        }
    }
private function receiveText($object){...}
private function receiveEvent($object){...}