微信开发,快速入门

2016-09-03 02:12:16 PHP 3952 0

开发前提

先申请一个公众号中的订阅号

配置 token
配置 access_token
配置 appId

微信签名,如,示例中的 check_signature 方法。只在配置公众号时调用,平时请不要调用它了
验证微信公众号的地址,如,http://wechat.hlzblog.top/

Route::get('/', 'WechatController@check_signature'); // 验证前,路由指向这个方法
Route::any('/', 'WechatController@index'); // 验证后,用于回复用户

WechatController.php

控制器示例

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Business\WechatMsgBusiness;
use App\Business\WechatLogicBusiness;
use App\Helpers\CurlRequest;
class WechatController extends Controller{
  // 微信签名,签名才用。平时请不要调用它了
  public function check_signature(){
    $token = env('wechat_token');
    // 微信服务器,发来三个字符串,用于生成签名
    $signature = $_GET['signature'];  // 微信服务器 用同样算法 生成的签名
    $timestamp = $_GET['timestamp'];
    $nonce = $_GET['nonce'];
    // 签名算法 => 这一块,是用来让自己知道,微信发来的请求是不是你当前正在调试的公众号的请求
        // 比如:你现在有 A、B两个公众号,你正在调试A,但是你地址填的B,微信公众号就以为B就是你了,所以就出错了。
    $tmpArr = array($token, $timestamp, $nonce);
      // use SORT_STRING rule
    sort($tmpArr, SORT_STRING);
    $tmpStr = implode( $tmpArr );
    $tmpStr = sha1( $tmpStr ); // 这个是就是你生成的签名
      // 对比 签名是否相同
    if( $tmpStr == $signature ){
      // 微信服务器,发来的第四个字符串,用于告诉微信服务器,你验证成功
      $echostr = $_GET['echostr']; // 反正必须输出这个字符串
      return $echostr;
    }else{
      return '测试失败';
    }
  }
  //+++++++++++++++++++++++++++++++++++++++++++++++++++
  //  正式开发,使用的部分
  //+++++++++++++++++++++++++++++++++++++++++++++++++++
  protected $fromUsername ; // 发送方帐号
  protected $toUsername ; // 开发者微信号
  protected $CreateTime ; // 消息创建时间 
  protected $MsgType ; // 消息类型
  protected $Event; // 事件的具体名称 ,选填
  protected $EventKey; // 事件类型
  protected $Content ; // 消息内容 ,选填
  // 统一微信请求入口
  public function index(){
    // ~~~~~~~~~~~~~~~~~~~~~~~微信请求我们服务器时,的必要操作~~~~~~~~~~~~~~~~~~~~~~~
     //get post data, May be due to the different environments
    $wecaht_request_xml = file_get_contents('php://input'); // 允许读取 POST 的原始数据
    //extract post data
    if (!empty($wecaht_request_xml)){
     /* 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);
      loader(true);
      $postObj = simplexml_load_string($wecaht_request_xml, 'SimpleXMLElement', LIBXML_NOCDATA);
      $this->fromUsername = $postObj->FromUserName[0];
      $this->toUsername = $postObj->ToUserName[0]; // 公众号
      $this->CreateTime = $postObj->CreateTime[0]; // 发送过来的消息
      $this->MsgType = $postObj->MsgType[0]; // 消息类型
      if( isset($postObj->Content[0]) ){
        $this->Content = $postObj->Content[0];
      }
      if( isset($postObj->Event[0]) ){
        $this->Event = $postObj->Event[0];
        if( isset($postObj->EventKey[0]) ){
          $this->EventKey = $postObj->EventKey[0];
        }
      }
      $this->handle_requset();
    }
  }
  /**
  * 微信事件处理
  * @return XML
  */
  private function handle_requset(){
    $result = array(); // 用于接收处理后的数据的
    switch($this->MsgType){
      // 接收普通消息
      case 'text': // 文本消息
        $result = WechatMsgBusiness::text(  trim($this->Content)  );
        break;
      case 'image': // 图片消息
        break;
      case 'voice': // 语音消息
        break;
      case 'video': // 视频消息
        break;
      case 'shortvideo': // 小视频消息
        break;
      case 'location': // 地理位置消息
        break;
      case 'link': // 链接消息
        break;
      // 接收事件推送
      case 'event': // 接收事件推送
        if( $this->Event =='CLICK' ){
          // 关于我们
          if( $this->EventKey=='About_button' ){
            $result = WechatMsgBusiness::text('About_button');
          }
        }
        break;
      default: // 默认回复
        $result = WechatMsgBusiness::text('');
        break;
    }
    return $this->handle_response($result['event'],  $result['arr']);
  }
  /**
  * 选择回复用户的事件
  * @param String event 回复的事件类型
  * @param Array  vars 回复的数组信息
  * @return XML 用于传输微信服务器的的 XML 数据
  */
  private function handle_response($event, $vars){
    $vars['ToUserName'] = $this->fromUsername;
    $vars['FromUserName'] = $this->toUsername;
    $vars['CreateTime'] = $this->CreateTime;
    // 回复事件
    switch ($event) {
      case 'text':
        $xml = view('wechat.tpl_reply_text', $vars);
        break;
      case 'news':
        $vars['ArticleCount'] = count( $vars['news'] ); // 计算图文数目
        $xml = view('wechat.tpl_pic_content', $vars);
        break;
      default:
        # code...
        break;
    }
    echo $xml;
    exit();
  }
  // 微信登陆入口
  public function to_oauth(){
    $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?';
    $get['appid'] = env('wechat_appid');
    $get['redirect_uri'] = env('wechat_redirect_uri');
    $get['response_type'] = 'code';
    $get['scope'] = 'snsapi_userinfo';
    $url .= http_build_query($get).'#wechat_redirect'; // 文档里说,这里必须加上这个hash值
    header("Location:".$url);
    exit();
  }
  // 获取用户授权信息
  public function get_user_info(){
    // 获取临时 access_token
    $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?';
    $get['appid'] = env('wechat_appid');
    $get['secret'] = env('wechat_appkey');
    $get['code'] = $_GET['code']; // 由回调页面传来
    $get['grant_type'] = 'authorization_code';
    $url .= http_build_query($get);
    $res = json_decode( CurlRequest::run($url) );
    // 这个判断主要用于调试
    if( isset($res->errcode) ){
      return $res->errmsg;
    }
    // 拉取授权信息
    $url = 'https://api.weixin.qq.com/sns/userinfo?';
    $get = [];
    $get['access_token'] = $res->access_token;
    $get['openid'] = $res->openid;
    $get['lang'] = 'zh_CN ';
    $url .= http_build_query($get);
    $user_info = json_decode( CurlRequest::run($url) );
    // 微信登录逻辑
    $wechat = new WechatLogicBusiness();
    $wechat->login($user_info);
    $redirect_url = '/user';
    echo '<script>window.location.href="'.$redirect_url.'"</script>';
    exit();
  }
}

WechatMsgBusiness.php

回复逻辑示例

<?php
// 微信回复相关
namespace App\Business;
class WechatMsgBusiness{
  public function __construct(){}
  /**
  * 文本消息
  * @return Array
  */
  public static function text($text){
    $arr =  array(); // 初始化数据
    switch ($text) {
      case '活动':
        // 返回类型
        $event = 'news';
        // 第一条
        $info = array();
        $info['Title'] = '云天河博客活动';
        $info['Description'] = '留言随机送配对女友';
        $info['PicUrl'] = '图文的图片链接';
        $info['Url'] = '点击图文信息,跳转到的对应url链接'
        $arr['news'][] = $info;
        // 第 n 条 [最多5条]
        $info['Title'] = '同上';
        $info['Description'] = '同上';
        $info['PicUrl'] = '同上';
        $info['Url'] = '同上'
        $arr['news'][] = $info;
        break;
      default:
        // 返回类型
        $event = 'text';
        $str =  "您好,欢迎关注云天河Blog!\n\n'";
        $str .= "输入1,查看进行中的活动";
        $arr['Content'] = $str;
        break;
    }
    return [
      'event' => $event,
      'arr'   => $arr
    ];
  }
}

WechatLogicBusiness.php

一些微信内部处理的逻辑,比如,这里的登陆逻辑

<?php
// 微信一些业务的逻辑
namespace App\Business;
use App\User;
class WechatLogicBusiness{
  public function __construct(){}
  /**
  * 微信登录逻辑
  * @param Object  用户信息
  * @return String 随机md5值[redis中的变量名],
  */
  public function login($user_info){
    $vars['openid'] = $user_info->openid; // openid 以公众号为单位,如,公众号A 申请了公众号支付功能,则可以其对应的openid来填写订单信息
    $vars['nickname'] = $user_info->nickname;   // 昵称
    $vars['headpic'] = $user_info->headimgurl;  // 头像
    // $vars['wxid'] = $user_info->unionid;   // unionid 以公司为单位,如,一个企业可以有很多种微信号,如,订阅号、公众号1、公众号2..,为方便管理,我们需要一个公共身份,企业内的业务人员都能识别这个用户
    // 已经有相关信息了?
    $user = new User;
    $res_1 = $user->where('wxid', $vars['wxid'])
                  ->get();
      // true 如果有用户
    if( count($res_1) ){
      $uid = $res_1[0]->id;
      $vars = []; // 清空
      $vars['uid'] = $uid;
      $_SESSION['uid'] = $uid;
    }else{
      $_SESSION['wx_user'] = $vars;
    }
  }
}

CurlRequest.php

<?php
namespace App\Helpers;
class CurlRequest {
  /** 
  * POST或GET请求,并返回数据
  * @param  String      url     访问地址
  * @param  Array|JSON  data    用于POST的数据
  * @param  Array       header  HTTP头请求
  * @return String  返回数据
  */
  public static function run($url, $data = null, $header = null ){
    //请求 URL,返回该 URL 的内容   
    $ch = curl_init(); // 初始化curl  
    curl_setopt($ch, CURLOPT_URL, $url); // 设置访问的 URL  
    curl_setopt($ch, CURLOPT_HEADER, 0); // 放弃 URL 的头信息
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回字符串,而不直接输出
    // Add Headers?
    if( $header ){
      curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    }
    // Https ?
    if( preg_match('/^https/', $url) ){
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不做服务器的验证  
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);  // 做服务器的证书验证
    }
    // POST method?
    if( $data ){
      curl_setopt($ch, CURLOPT_POST, true); // 设置为 POST 请求  
      curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // 设置POST的请求数据  
    }
    $content = curl_exec($ch); // 开始访问指定URL  
    curl_close($ch); // 关闭 cURL 释放资源 
    return $content;
  }
}

示例模板文件

tpl_pic_content.blade.php

回复图文消息

<xml>
  <ToUserName><![CDATA[{{$ToUserName}}]]></ToUserName>
  <FromUserName><![CDATA[{{$FromUserName}}]]></FromUserName>
  <CreateTime>{{$CreateTime}}</CreateTime>
  <MsgType><![CDATA[news]]></MsgType>
  <ArticleCount>{{$ArticleCount}}</ArticleCount>
  <Articles>
@foreach ($news  as $k => $v)
  <item>
  <Title><![CDATA[{{$v['Title']}}]]></Title>
  <Description><![CDATA[{{$v['Description']}}]]></Description>
  <PicUrl><![CDATA[{{$v['PicUrl']}}]]></PicUrl>
  <Url><![CDATA[{{$v['Url']}}]]></Url>
  </item>
@endforeach
  </Articles>
</xml>
tpl_reply_text.blade.php

回复文本消息

<xml>
  <ToUserName><![CDATA[{{$ToUserName}}]]></ToUserName>
  <FromUserName><![CDATA[{{$FromUserName}}]]></FromUserName>
  <CreateTime>{{$CreateTime}}</CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <Content><![CDATA[{!! $Content !!}]]></Content>
</xml>

地理位置消息

如果用户发了地理位置给公众号

公众号发给我们服务器的XML如下
<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[fromUser]]></FromUserName>
  <CreateTime>1351776360</CreateTime>
  <MsgType><![CDATA[location]]></MsgType>
  <Location_X>23.134521</Location_X>
  <Location_Y>113.358803</Location_Y>
  <Scale>20</Scale>
  <Label><![CDATA[位置信息]]></Label>
  <MsgId>1234567890123456</MsgId>
</xml>
去百度API

找对应接口:web端 URI API

找到如下地址

http://api.map.baidu.com/place/search?query=海底捞&location=31.204055632862,121.41117785465&radius=1000&region=上海&output=html&src=yourCompanyName|yourAppName

实际上,使用以下链接中的参数即可

http://api.map.baidu.com/place/search?query=海底捞&location=31.204055632862,121.41117785465&radius=1000&output=html
参数介绍

参数介绍

微信被动回复消息 php原生案例

链接:http://pan.baidu.com/s/1qYmyD7u 密码:v0x9

注:若无特殊说明,文章均为云天河原创,请尊重作者劳动成果,转载前请一定要注明出处