php微信开发之带参数二维码的使用


Posted in PHP onAugust 03, 2016

最近做微信PC端网页微信相关功能的开发,从一个新手的角度来说,微信公众号的文档还是不好理解的,网上找的帖子大都也都基本上是复制微信公众平台上给的文档,开发微信带参数二维码过程中还是遇到不少坑的,在此把我的开发过程比较详细的记录下,希望对大家有所帮助。

我本次开发使用的是认证服务号。 

1 接入
首先进入微信公众号 -> 基本配置 
下面是基本配置的页面,在URL中填写服务器地址,这个地址就是接受微信推送事件的一个接口,我是使用thinkPHP框架开发的程序,在其中一个Module(Decoration)的Action目录下新建一个类,比如叫: WechatAction.class.php ,在该Action中新建一个public方法,比如叫: URLRedirect() ,那么在这个URL中填写的就是 http://[IP]:[port]/index.php/Decoration/Wechat/UrlRedirect ,然后填写Token,Token随意填,EncodingAESKey要不要都行,然后点击确认,微信会往这个URL上发送一个get请求,里面包含很多参数,其中大部分都是让我们自己核对这次访问是不是微信服务器请求的,我自己没有验证,他的要求是如果我们核对成功,即原样返回get请求中的一个参数echostr,这里的返回不是return,也不是ajaxReturn,而使用echo,如果用thinkPHP开发的话,直接使用 echo I('echostr'); 即可。然后接口即验证成功了。 

php微信开发之带参数二维码的使用

2 带参数二维码的作用
微信的带参数二维码有两种,一种是临时二维码,一种是永久二维码,但是永久二维码的生成是有个数限制的,我这次要实现的功能是用户未登录的情况下在网站上使用产品,比如获得某商品的详细报价,但是又不想注册,然而又想保存这个报价单,这个时候网页可以生成一张二维码,用户只要用微信扫一扫这个二维码,官方公众号就会给这个用户发送一天图文消息,图文消息点开后就是用户刚刚获得的报价单,而且可以随时点击查看并且分享给朋友进行比价。所以临时二维码即可正常使用。
上面是我是怎么使用的,下面介绍一下整个交互的流程

当用户扫描这个二维码,如果用户关注了公众号,用户会直接进入与公众号的会话页面,微信服务器会给我们在上一步设置的服务器URL中推送一条消息,其中可以携带一个我们自定义的参数。如果用户未关注公众号,则用户首先会跳转到公众号关注页面,用户点击关注后,会直接进入公众号的会话页面,微信服务器这时也会给我们设置的URL推送一个事件消息,携带我们自定义参数,我们可以根据这个参数和事件类型做控制下一步动作。

3 具体开发过程 

3.1 获取access_token
这个access_token是我们程序调用微信接口的凭证,目前的有效期是7200秒,所以我们需要定时更新access_token。
获得方法:
方法 : GET
url :https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
其中的参数APPID和APPSECRET是我们公众号的APPID和APPSECRET,在微信公众号 -> 基本配置中可以查到,调用成功会返回如下JSON数据:
 {"access_token":"ACCESS_TOKEN","expires_in":7200}

其中access_token就是调用接口凭证,expire_in是token有效时间。
我本人是把access_token存在数据库中,同时保存过期时间,然后封装公用函数 getWechatAccessToken() ,每次先检查access_token是否过期,如果过期则重新获取,否则直接使用数据库保存的access_token即可,我忘了在哪儿看加过,这个access_token每天的获取次数应该是有限制的。下面是 getWechatAccessToken()  的具体实现:

//获取access_token
function getWechatAccessToken(){
 $wechatInfo = M('wechat_info')->select();
 $wechatInfo = array_reduce($wechatInfo, create_function('$result, $v', '$result[$v["conf_name"]] = $v;return $result;'));
 $expireTime = $wechatInfo['PUBLIC_WECHAT_ACCESSTOKEN_EXPIRES']['conf_value'];        //前面不用管,是我数据库相应设置

 if (time() < $expireTime){    //access_token未过期
  return $wechatInfo['PUBLIC_WECHAT_ACCESSTOKEN']['conf_value'];
 }else{         //access_token过期,重新获取
  $baseUrl = C('WECHAT_PUBLIC_GET_ACCESS_TOKEN');
  $url = str_replace("##APPSECRET##", $wechatInfo['PUBLIC_WECHAT_APPSECRET']['conf_value'], str_replace("##APPID##", $wechatInfo['PUBLIC_WECHAT_APPID']['conf_value'], $baseUrl));
  $result = file_get_contents($url);
  $result = json_decode($result, true);

  if (array_key_exists('errorcode', $result)){  //失败重试一次
   return false;
  }else{
   M('wechat_info')->where(array('conf_name' => 'PUBLIC_WECHAT_ACCESSTOKEN'))->save(array('conf_value' => $result['access_token']));
   M('wechat_info')->where(array('conf_name' => 'PUBLIC_WECHAT_ACCESSTOKEN_EXPIRES'))->save(array('conf_value' => time()+$result['expires_in']-200));
   return $result['access_token'];
  }
 }
}

C('WECHAT_PUBLIC_GET_ACCESS_TOKEN') = https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

封装好这个之后,我们每次就可以安心的使用了。

.2 创建临时二维码

3.2.1 获取ticket3

请求方式: POST
接口:https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
POST数据: {"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}
接口URL中的TOKEN即我们在3.1中获取的access_token,post数据中expire_seconds是二维码的有效时间,最多为30天,action_name临时二维码的话固定就是QR_SCENE,scene_id即我们自定义参数,是个32位非0整数,我在应用中把它设为订单的ID,微信服务器推送事件的时候会把这个值返回给我们设置的接口中,然后我会根据这个值去拿相应的订单数据展示在网页上,这是后话。 

下面是封装的生成临时二维码的方法: 

//创建临时二维码
function getTemporaryQrcode($orderId){
 $accessToken = getWechatAccessToken();
 $url = str_replace("##TOKEN##", $accessToken, C('WECHAT_PUBLIC_GET_TEMPORARY_TICKET'));
 $qrcode = '{"expire_seconds": 1800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": '.$orderId.'}}}';
 $result = api_notice_increment($url, $qrcode);
 $result = json_decode($result, true);
 return urldecode($result['url']);
}

其中的方法 api_notice_increment() 是我封装的一个POST方法函数,我试过很多POST的方法,可能由于微信接口对POST方法和参数的限制比较严格,这个浪费了好久时间,最后在网上找到了一个可以使用的封装好的POST方法,建议大家先自己试试,如果微信返回错误吗,就用这个吧,起码我测试微信这个接口的时候用postman测试返回的都是错误,而且一定要用JSON字符串,一定要是非常严格的JSON字符串。下面是这个方法: 

function api_notice_increment($url, $data){
 $ch = curl_init();
 $header = "Accept-Charset: utf-8";
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
 curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
 curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
 curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
 curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 $tmpInfo = curl_exec($ch);
 if (curl_errno($ch)) {
  curl_close( $ch );
  return $ch;
 }else{
  curl_close( $ch );
  return $tmpInfo;
 }

}

getTemporaryQrcode() 中有一个在配置文件中的参数给大家看下,其实就是微信接口链接: 
C('WECHAT_PUBLIC_GET_TEMPORARY_TICKET') = https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=##TOKEN##

这个接口的返回值是: 
{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==","expire_seconds":60,"url":"http:\/\/weixin.qq.com\/q\/kZgfwMTm72WWPkovabbI"}

其中ticket是让我们用来进行下一步调用的凭证,expire_seconds是二维码的有效期,url是我们生成的二维码扫描后打开的链接。所以如果我们自己实现了生成二维码的方法,就不用再进行下一步调用,我本人即在这一步就停止了,直接返回url的值,然后利用这个url的值生成二维码存在本地即可。PHP生成二维码可以使用phpqrcode,挺好用的。下一步也大致提一下:

3.2.2 获取二维码地址
请求方式: GET
接口:https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
这个接口的返回值是一张图片,可以直接展示或者下载,我们有具体使用过,所以也不知道应该怎么展示。 

3.3 用户扫描二维码之后发生的事情
 3.3.1 扫描后发生了什么
上面提到了,用户扫描我们生成的临时二维码,如果用户未关注公众号,则首先会跳转到公众号的关注页面,点击关注后,会进入公众号的会话页面,同时会给我们设置的接口推送一个事件。如果用户已经关注了,用户微信会直接跳转到公众号会话页面,然后微信服务器会给我们设置的接口推送一个事件。

用户关注与否微信服务器给我们推送的事件是差不多的,只是新关注用户推送的事件中scene_id前面会加一个前缀。下面是微信公众平台文档的说明:

用户未关注时,进行关注后的事件推送

<xml><ToUserName><![CDATA[toUser]]></ToUserName>//开发者微信号
<FromUserName><![CDATA[FromUser]]></FromUserName> //发送者账号(openid)
<CreateTime>123456789</CreateTime>//消息创建时间(整型)
<MsgType><![CDATA[event]]></MsgType>//消息类型 event
<Event><![CDATA[subscribe]]></Event>//事件类型(subscribe)
<EventKey><![CDATA[qrscene_123123]]></EventKey>//事件KEY值,qrscene_为前缀,后面为二维码参数值
<Ticket><![CDATA[TICKET]]></Ticket> //二维码ticke值,可以用来换取二维码图片
</xml> 
 

用户已关注时的事件推送

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>//开发者微信号
<FromUserName><![CDATA[FromUser]]></FromUserName> //发送者账号(openid)
<CreateTime>123456789</CreateTime> //消息创建时间
<MsgType><![CDATA[event]]></MsgType>     //消息类型event
<Event><![CDATA[SCAN]]></Event> //事件类型 event
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>   //事件key值,是一个32位无符号整数,即创建二维码时的二维码scene_id
<Ticket><![CDATA[TICKET]]></Ticket>      //二维码的ticke,可以用来换取二维码图片
</xml>

3.3.2 我们要做些什么

我们需要在自己填写的URL接口中接收这个事件,然后拿到我们需要的东西做我们想干的事儿。因为我要实现的功能比较简单,只需要拿到scene_id即可,因为这是我要展示给用户看的订单数据。下面是我写的接收和处理部分,比较简单,主要看一下应该怎么接收微信推送的事件: 

public function urlRedirect(){
  $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
  $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
  $fromUsername = (string)$postObj->FromUserName;
  $EventKey = trim((string)$postObj->EventKey);
  $keyArray = explode("_", $EventKey);
  if (count($keyArray) == 1){   //已关注者扫描
   $this->sendMessage($fromUsername, $EventKey);
  }else{ //未关注者关注后推送事件
   $this->sendMessage($fromUsername, $keyArray[1]);
  }
 }

我没有使用其他参数,只是根据不同的推送事件拿到我想要的订单ID,然后这时候其实相当于你在这里用公众号的客服在跟扫码的这个用户对话,上段代码中调用的sendMessage()是使用客户账号给扫码用户发送一个图文消息,因为我在拿scen_id的同时也拿到了用户的openid,可以利用这个给用户发送消息。

下面是sendMessage()方法: 

//给用户发送图文消息,点击跳转到报价页面
 public function sendMessage($openid,$orderId){
  $url = str_replace('##TOKEN##', getWechatAccessToken(), C('WECHAT_SEND_MESSAGE'));
  $redirectUrl = str_replace("##ORDERID##", $orderId, str_replace("##OPENID##", $openid, C('WECHAT_REDIRECT_URL_PRE')));
  $orderInfo = M('order')->where(array('orderid' => $orderId))->field(array('totalMoney', 'savedMoney', 'roomarea'))->find();
  $description = str_replace("##ROOMAREA##", intval($orderInfo['roomarea'] * 1.25), C('WECHAT_MESSAGE_BRIEF'));
  $description = str_replace("##TOTALBUDGET##", $orderInfo['totalMoney'], $description);
  $description = str_replace("##MARKETBUDGET##", $orderInfo['totalMoney']+$orderInfo['savedMoney'], $description);
  $description = str_replace("##SAVEMONEY##", $orderInfo['savedMoney'], $description);
  $dataStr = '{"touser":"' . $openid . '","msgtype":"news","news":{"articles":[{"title":"' . C('WECHAT_MESSAGE_TITLE') .
   '","description":"' . $description . '","url":"' . $redirectUrl . '","picurl":"' . C('WECHAT_MESSAGE_PICURL') . '""}]}}';
  api_notice_increment($url, $dataStr);
 }

其中 C('WECHAT_SEND_MESSAGE') = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=##TOKEN##' 至于下面的一大段str_replace,就是在组给用户发送的文字而已,需要注意$dataStr的格式,这里面要求JSON字符串比较严格,必须所有的字符串都用双引号括起来。微信接口对POST参数的限制真心严格。

下面是微信公众平台开发者文档中要求发送图文消息的POST data格式: 

{
 "touser":"OPENID",
 "msgtype":"news",
 "news":{
  "articles": [
   {
    "title":"Happy Day",
    "description":"Is Really A Happy Day",
    "url":"URL",
    "picurl":"PIC_URL"
   },
   {
    "title":"Happy Day",
    "description":"Is Really A Happy Day",
    "url":"URL",
    "picurl":"PIC_URL"
   }
   ]
 }
}

其中url是用户点击这个消息之后打开的地址,这个时候我就组了一个自己网站的地址,是一个get请求地址,里面携带参数是用户的openid和订单id,这样用户点击开图文消息就可以看到自己刚才下单的内容了,因为需要在网页上展示用户的微信头像和昵称,所以我把openid也放到参数里,在页面加载前先拿到用户的个人信息和订单数据,再展示网页。这样流程:用户未登录下单 -> 生成微信二维码 -> 用户扫码关注公众号 -> 查看订单详细信息 就完成了。而且因为这个图文消息打开后的链接携带的参数是这个用户的额openid和其下单的订单ID,不管分享到哪儿,用什么浏览器打开都是可以访问的,且展示的也是这个用户的头像和昵称信息,这也是我要实现的一个效果。

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

PHP 相关文章推荐
Discuz!下Memcache缓存实现方法
May 28 PHP
php下通过curl抓取yahoo boss 搜索结果的实现代码
Jun 10 PHP
php 定义404页面的实现代码
Nov 19 PHP
关于PHP session 存储方式的详细介绍
Jun 25 PHP
常见php数据文件缓存类汇总
Dec 05 PHP
PHP中使用xmlreader读取xml数据示例
Dec 29 PHP
yii用户注册表单验证实例
Dec 26 PHP
CodeIgniter针对数据库的连接、配置及使用方法
Mar 03 PHP
PHP+Ajax验证码验证用户登录
Jul 20 PHP
探究Laravel使用env函数读取环境变量为null的问题
Dec 06 PHP
php转换上传word文件为PDF的方法【基于COM组件】
Jun 10 PHP
Yii实现微信公众号场景二维码的方法实例
Aug 30 PHP
PHP支付系统设计与典型案例分享
Aug 02 #PHP
php微信支付接口开发程序
Aug 02 #PHP
比较完整的微信开发php代码
Aug 02 #PHP
微信利用PHP创建自定义菜单的方法
Aug 01 #PHP
php微信浏览器分享设置以及回调详解
Aug 01 #PHP
PHP+Apache+Mysql环境搭建教程
Aug 01 #PHP
Thinkphp和Bootstrap结合打造个性的分页样式(推荐)
Aug 01 #PHP
You might like
精通php的十大要点(上)
2009/02/04 PHP
thinkphp在模型中自动完成session赋值示例代码
2014/09/09 PHP
ThinkPHP模板之变量输出、自定义函数与判断语句用法
2014/11/01 PHP
WordPress网站性能优化指南
2015/11/18 PHP
WordPress开发中用于获取近期文章的PHP函数使用解析
2016/01/05 PHP
基于PHP常用文件函数和目录函数整理
2017/08/17 PHP
PHP递归实现汉诺塔问题的方法示例
2017/11/25 PHP
PHP简单实现图片格式转换(jpg转png,gif转png等)
2019/10/30 PHP
Gambit vs ForZe BO3 第二场 2.13
2021/03/10 DOTA
用js获取电脑信息(是使用与IE浏览器)
2013/01/15 Javascript
jQuery中setTimeout的几种使用方法小结
2013/04/07 Javascript
jquery 实现二级/三级/多级联动菜单的思路及代码
2013/04/08 Javascript
js 高效去除数组重复元素示例代码
2013/12/19 Javascript
可以浮动某个物体的jquery控件用法实例
2015/07/24 Javascript
jfinal与bootstrap的登录跳转实战演习
2015/09/22 Javascript
JavaScript中利用各种循环进行遍历的方式总结
2015/11/10 Javascript
Javascript缓存API
2016/06/14 Javascript
微信小程序 video组件详解
2016/10/25 Javascript
Javascript基于jQuery UI实现选中区域拖拽效果
2016/11/25 Javascript
js 轮播效果实例分享
2016/12/28 Javascript
JS复制对应id的内容到粘贴板(Ctrl+C效果)
2017/01/23 Javascript
基于angular-utils-ui-breadcrumbs使用心得(分享)
2017/11/03 Javascript
Koa2微信公众号开发之消息管理
2018/05/16 Javascript
解决Nuxt使用axios跨域问题
2020/07/06 Javascript
浅谈vue中使用编辑器vue-quill-editor踩过的坑
2020/08/03 Javascript
python批量提取word内信息
2015/08/09 Python
Python识别快递条形码及Tesseract-OCR使用详解
2019/07/15 Python
django自定义非主键自增字段类型详解(auto increment field)
2020/03/30 Python
DRF框架API版本管理实现方法解析
2020/08/21 Python
DOUGLAS荷兰:购买香水和化妆品
2020/10/24 全球购物
英国顶尖手表珠宝品牌独家授权经销商:HS Johnson
2020/10/28 全球购物
管理科学大学生求职信
2013/11/13 职场文书
小学评语大全
2014/04/22 职场文书
股东授权委托书
2014/10/15 职场文书
新教师个人总结
2015/02/06 职场文书
一篇文章弄懂MySQL查询语句的执行过程
2021/05/07 MySQL