php微信开发之自定义菜单完整流程


Posted in PHP onOctober 08, 2016

一、自定义菜单概述

自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。开启自定义菜单后,公众号界面如图所示:

php微信开发之自定义菜单完整流程

二、申请自定义菜单

个人订阅号使用微博认证、企业订阅号通过微信认证;可以申请到自定义菜单资格

服务号默认有菜单权限。

三、获得AppId 和AppSecert

AppId和AppSecret在开发者中心-开发者ID中,可以找到。

php微信开发之自定义菜单完整流程

四、获得Access Token

用appid和appsecert获得access token,接口为

https://api.weixin.qq.com/cgi-bi ... mp;secret=APPSECRET

程序实现如下

$appid = ""; 
$appsecret = ""; 
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appid&secret=$appsecret"; 
$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, $url); 
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); 
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
$output = curl_exec($ch); 
curl_close($ch); 
$jsoninfo = json_decode($output, true); 
$access_token = $jsoninfo["access_token"];

你也可以直接在浏览器地址栏中,拼接出地址,执行后,获得如下数据

{"access_token":"N2L7KXa084WvelONYjkJ_traBMCCvy_UKmpUUzlrQ0EA2yNp3Iz6eSUrRG0bhaR_viswd50vDuPkY5nG43d1gbm-olT2KRMxOsVE08RfeD9lvK9lMguNG9kpIkKGZEjIf8Jv2m9fFhf8bnNa-yQH3g", 

"expires_in":7200} 

参数说明如下

php微信开发之自定义菜单完整流程

其中的
N2L7KXa084WvelONYjkJ_traBMCCvy_UKmpUUzlrQ0EA2yNp3Iz6eSUrRG0bhaR_viswd50vDuPkY5nG43d1gbm-olT2KRMxOsVE08RfeD9lvK9lMguNG9kpIkKGZEjIf8Jv2m9fFhf8bnNa-yQH3g
就是access token。

或者使用官方的接口调试工具,地址为:
https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95&form=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%9B%E5%BB%BA%E6%8E%A5%E5%8F%A3%20/menu/create

使用网页调试工具调试自定义菜单接口

php微信开发之自定义菜单完整流程

点击检查问题得,得到

php微信开发之自定义菜单完整流程

这样也获得了access token

五、组织菜单内容

目前自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代 替。请注意,创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来。建议测试时可以尝试取消关注公众账号后再次关注,则可以看到创 建后的效果。

目前自定义菜单接口可实现两种类型按钮,如下:

click:
用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event        的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
view:
用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值        (即网页链接),达到打开网页的目的,建议与网页授权获取用户基本信息接口结合,获得用户的登入个人信息。

接口调用请求说明

http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bi ... _token=ACCESS_TOKEN

请求示例

{ 
 "button":[ 
  {  
   "type":"click", 
   "name":"今日歌曲", 
   "key":"V1001_TODAY_MUSIC" 
  }, 
  { 
   "type":"click", 
   "name":"歌手简介", 
   "key":"V1001_TODAY_SINGER" 
  }, 
  { 
   "name":"菜单", 
   "sub_button":[ 
   {  
    "type":"view", 
    "name":"搜索", 
    "url":"http://www.soso.com/" 
   }, 
   { 
    "type":"view", 
    "name":"视频", 
    "url":"http://v.qq.com/" 
   }, 
   { 
    "type":"click", 
    "name":"赞一下我们", 
    "key":"V1001_GOOD" 
   }] 
  }] 
}

参数说明

php微信开发之自定义菜单完整流程

返回结果

正确时的返回JSON数据包如下:

{"errcode":0,"errmsg":"ok"}

错误时的返回JSON数据包如下(示例为无效菜单名长度):

{"errcode":40018,"errmsg":"invalid button name size"}

六、提交菜单内容给服务器

菜单的JSON结构为

{"button": [{"name":"天气预报","sub_button":[{"type":"click","name":"北京天气","key":"天气北 京"}, 
{"type":"click","name":"上海天气","key":"天气上海"}, 
{"type":"click","name":" 广州天气","key":"天气广州"},{"type":"click","name":"深圳天气","key":"天气深圳"}, 
{"type":"view","name":"本地天气","url":"http://m.hao123.com/a/tianqi"}]}, 
{"name":"方倍工作室","sub_button":[{"type":"click","name":"公司简 介","key":"company"}, 
{"type":"click","name":"趣味游戏","key":"游戏"}, {"type":"click","name":"讲个笑话","key":"笑话"}]}]}

将以下代码保存为menu.php,并且在浏览器中运行该文件(比如 http://127.0.0.1/menu.php),将直接向微信服务器提交菜单

php 
 
$access_token = ""; 
 
$jsonmenu = '{ 
  "button":[ 
  { 
   "name":"天气预报", 
   "sub_button":[ 
   { 
    "type":"click", 
    "name":"北京天气", 
    "key":"天气北京" 
   }, 
   { 
    "type":"click", 
    "name":"上海天气", 
    "key":"天气上海" 
   }, 
   { 
    "type":"click", 
    "name":"广州天气", 
    "key":"天气广州" 
   }, 
   { 
    "type":"click", 
    "name":"深圳天气", 
    "key":"天气深圳" 
   }, 
   { 
    "type":"view", 
    "name":"本地天气", 
    "url":"http://m.hao123.com/a/tianqi" 
   }] 
  
 
  }, 
  { 
   "name":"瑞雪", 
   "sub_button":[ 
   { 
    "type":"click", 
    "name":"公司简介", 
    "key":"company" 
   }, 
   { 
    "type":"click", 
    "name":"趣味游戏", 
    "key":"游戏" 
   }, 
   { 
    "type":"click", 
    "name":"讲个笑话", 
    "key":"笑话" 
   }] 
  
 
  }] 
}'; 
 
 
$url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=".$access_token; 
$result = https_request($url, $jsonmenu); 
var_dump($result); 
 
function https_request($url,$data = null){ 
 $curl = curl_init(); 
 curl_setopt($curl, CURLOPT_URL, $url); 
 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 
 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); 
 if (!empty($data)){ 
  curl_setopt($curl, CURLOPT_POST, 1); 
  curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 
 } 
 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 
 $output = curl_exec($curl); 
 curl_close($curl); 
 return $output; 
}
?>

或者使用官方的调试接口 使用网页调试工具调试该接口

php微信开发之自定义菜单完整流程

php微信开发之自定义菜单完整流程

提交成功后,重新关注后即可看到菜单。菜单效果类似如下:

php微信开发之自定义菜单完整流程

七、响应菜单点击事件

在消息接口中处理event事件,其中的click代表菜单点击,通过响应菜单结构中的key值回应消息,view事件无须响应,将直接跳转过去

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); 
    $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); 
    $RX_TYPE = trim($postObj->MsgType); 
 
     switch ($RX_TYPE) 
      { 
        case "text": 
          $resultStr = $this->receiveText($postObj); 
          break; 
        case "event": 
         $resultStr = $this->receiveEvent($postObj); 
          break; 
        default: 
          $resultStr = ""; 
         break; 
      } 
      echo $resultStr; 
    }else { 
      echo ""; 
     exit; 
    } 
  } 
 
  private function receiveText($object) 
  { 
    $funcFlag = 0; 
    $contentStr = "你发送的内容为:".$object->Content; 
    $resultStr = $this->transmitText($object, $contentStr, $funcFlag); 
    return $resultStr; 
  } 
   
  private function receiveEvent($object) 
  { 
    $contentStr = ""; 
    switch ($object->Event) 
    { 
      case "subscribe": 
        $contentStr = "欢迎洋洋博客"; 
      case "unsubscribe": 
        break; 
      case "CLICK": 
        switch ($object->EventKey) 
        { 
          case "company": 
            $contentStr[] = array("Title" =>"公司简介",  
            "Description" =>"洋洋的博客",  
            "PicUrl" =>"http://discuz.comli.com/weixin/weather/icon/cartoon.jpg",  
            "Url" =>"weixin://addfriend/pondbaystudio"); 
            break; 
          default: 
            $contentStr[] = array("Title" =>"默认菜单回复",  
            "Description" =>"您正在使用的是<span style="font-family: Arial, Helvetica, sans-serif;">洋洋的博客</span><span style="font-family: Arial, Helvetica, sans-serif;">", </span> 
            "PicUrl" =>"http://discuz.comli.com/weixin/weather/icon/cartoon.jpg",  
            "Url" =>"weixin://addfriend/pondbaystudio"); 
            break; 
        } 
        break; 
      default: 
        break;    
 
    } 
    if (is_array($contentStr)){ 
      $resultStr = $this->transmitNews($object, $contentStr); 
    }else{ 
      $resultStr = $this->transmitText($object, $contentStr); 
   } 
    return $resultStr; 
  } 
 
  private function transmitText($object, $content, $funcFlag = 0) 
  { 
    $textTpl = "<xml> 
<ToUserName><![CDATA[%s]]></ToUserName> 
<FromUserName><![CDATA[%s]]></FromUserName> 
<CreateTime>%s</CreateTime> 
<MsgType><![CDATA[text]]></MsgType> 
<Content><![CDATA[%s]]></Content> 
<FuncFlag>%d</FuncFlag> 
</xml>"; 
    $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content, $funcFlag); 
    return $resultStr; 
  } 
 
  private function transmitNews($object, $arr_item, $funcFlag = 0) 
  { 
    //首条标题28字,其他标题39字 
    if(!is_array($arr_item)) 
      return; 
 
    $itemTpl = "  <item> 
    <Title><![CDATA[%s]]></Title> 
    <Description><![CDATA[%s]]></Description> 
    <PicUrl><![CDATA[%s]]></PicUrl> 
    <Url><![CDATA[%s]]></Url> 
  </item> 
"; 
    $item_str = ""; 
    foreach ($arr_item 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> 
<FuncFlag>%s</FuncFlag> 
</xml>"; 
 
   $resultStr = sprintf($newsTpl, $object->FromUserName, $object->ToUserName, time(), count($arr_item), $funcFlag); 
    return $resultStr; 
  } 
} 
?>

八、菜单中获取OpenID

由于菜单中只能填写固定的url地址,对于想要菜单中获取用户的OpenID的情况,可以使用OAuth2.0授权的方式来实现。

URL中填写的地址为一个固定的回调地址。原理方法可以参考  微信公众平台开发(99) 自定义菜单获取OpenID

<?php 
/* 
  洋洋的博客 
*/ 
 
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()){ 
      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 ); 
 
    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; 
    } 
  } 
} 
?>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
如何在PHP中使用Oracle数据库(1)
Oct 09 PHP
php获取某个目录大小的代码
Sep 10 PHP
PHP number_format() 函数定义和用法
Jun 01 PHP
基于Zend的Config机制的应用分析
May 02 PHP
php类常量的使用详解
Jun 08 PHP
php中如何同时使用session和cookie来保存用户登录信息
Jul 05 PHP
PHP循环结构实例讲解
Feb 10 PHP
Symfony2实现在doctrine中内置数据的方法
Feb 05 PHP
php简单实现数组分页的方法
Apr 30 PHP
Laravel框架中Blade模板的用法示例
Aug 30 PHP
php实现mysql连接池效果实现代码
Jan 25 PHP
php让json_encode不自动转义斜杠“/”的方法
Apr 27 PHP
yii2.0数据库迁移教程【多个数据库同时同步数据】
Oct 08 #PHP
yii2高级应用之自定义组件实现全局使用图片上传功能的方法
Oct 08 #PHP
ThinkPHP发送邮件示例代码
Oct 08 #PHP
Yii2针对游客、用户防范规则和限制的解决方法分析
Oct 08 #PHP
Netbeans 8.2与PHP相关的新特性介绍
Oct 08 #PHP
Laravel中基于Artisan View扩展包创建及删除应用视图文件的方法
Oct 08 #PHP
PHP反射API示例分享
Oct 08 #PHP
You might like
PHP删除HTMl标签的三种解决方法
2013/06/30 PHP
PHP扩展模块Pecl、Pear以及Perl的区别
2014/04/09 PHP
PHP 使用memcached简单示例分享
2015/03/05 PHP
LAMP环境使用Composer安装Laravel的方法
2017/03/25 PHP
PHP fopen函数用法实例讲解
2019/02/15 PHP
JavaScript语言中的Literal Syntax特性分析
2007/03/08 Javascript
JavaScript DOM学习第一章 W3C DOM简介
2010/02/19 Javascript
FileUpload 控件 禁止手动输入或粘贴的实现代码
2010/04/07 Javascript
javascript动画浅析
2012/08/30 Javascript
获取元素距离浏览器周边的位置的方法getBoundingClientRect
2013/04/17 Javascript
PHPMyAdmin导入时提示文件大小超出PHP限制的解决方法
2015/03/30 Javascript
JavaScript中for循环的使用详解
2015/06/03 Javascript
jQuery实现控制文字内容溢出用省略号(…)表示的方法
2016/02/26 Javascript
值得分享的JavaScript实现图片轮播组件
2016/11/21 Javascript
jQuery 出现Cannot read property ‘msie’ of undefined错误的解决方法
2016/11/23 Javascript
微信小程序实现给循环列表添加点击样式实例
2017/04/26 Javascript
微信小程序项目实践之九宫格实现及item跳转功能
2018/07/19 Javascript
vue1.0和vue2.0的watch监听事件写法详解
2018/09/11 Javascript
微信小程序提取公用函数到util.js及使用方法示例
2019/01/10 Javascript
[05:08]第一届“网鱼杯”DOTA2比赛精彩集锦
2014/09/05 DOTA
Django框架下在URLconf中指定视图缓存的方法
2015/07/23 Python
简单学习Python time模块
2016/04/29 Python
编写多线程Python服务器 最适合基础
2018/09/14 Python
PyQt5的安装配置过程,将ui文件转为py文件后显示窗口的实例
2019/06/19 Python
Django 实现图片上传和下载功能
2020/12/31 Python
社区道德讲堂实施方案
2014/03/21 职场文书
公休请假条
2014/04/11 职场文书
餐饮投资计划书
2014/04/25 职场文书
艾滋病宣传活动总结
2014/05/08 职场文书
捐款倡议书格式范文
2014/05/14 职场文书
干部鉴定材料
2014/05/18 职场文书
导航工程专业自荐信
2014/09/02 职场文书
离职信范本
2015/06/23 职场文书
2015年美容师个人工作总结
2015/10/14 职场文书
Python中使用ipython的详细教程
2021/06/22 Python
navicat 连接Ubuntu虚拟机的mysql的操作方法
2022/04/02 MySQL