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_tokenfunction 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
//创建临时二维码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() 中有一个在配置文件中的参数给大家看下,其实就是微信接口链接:
<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 我们要做些什么
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,可以利用这个给用户发送消息。
//给用户发送图文消息,点击跳转到报价页面 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参数的限制真心严格。
{ "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,不管分享到哪儿,用什么浏览器打开都是可以访问的,且展示的也是这个用户的头像和昵称信息,这也是我要实现的一个效果。