php微信開發之帶參數二維碼的使用

最近作微信PC端網頁微信相關功能的開發,從一個新手的角度來講,微信公衆號的文檔仍是很差理解的,網上找的帖子大都也都基本上是複製微信公衆平臺上給的文檔,開發微信帶參數二維碼過程當中仍是遇到很多坑的,在此把個人開發過程比較詳細的記錄下,但願對你們有所幫助。php

我本次開發使用的是認證服務號。 ajax

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'); 便可。而後接口即驗證成功了。 數據庫

2 帶參數二維碼的做用
微信的帶參數二維碼有兩種,一種是臨時二維碼,一種是永久二維碼,可是永久二維碼的生成是有個數限制的,我此次要實現的功能是用戶未登陸的狀況下在網站上使用產品,好比得到某商品的詳細報價,可是又不想註冊,然而又想保存這個報價單,這個時候網頁能夠生成一張二維碼,用戶只要用微信掃一掃這個二維碼,官方公衆號就會給這個用戶發送一天圖文消息,圖文消息點開後就是用戶剛剛得到的報價單,並且能夠隨時點擊查看而且分享給朋友進行比價。因此臨時二維碼便可正常使用。
上面是我是怎麼使用的,下面介紹一下整個交互的流程json

當用戶掃描這個二維碼,若是用戶關注了公衆號,用戶會直接進入與公衆號的會話頁面,微信服務器會給咱們在上一步設置的服務器URL中推送一條消息,其中能夠攜帶一個咱們自定義的參數。若是用戶未關注公衆號,則用戶首先會跳轉到公衆號關注頁面,用戶點擊關注後,會直接進入公衆號的會話頁面,微信服務器這時也會給咱們設置的URL推送一個事件消息,攜帶咱們自定義參數,咱們能夠根據這個參數和事件類型作控制下一步動做。api

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()  的具體實現:微信

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//獲取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 app

封裝好這個以後,咱們每次就能夠安心的使用了。微信公衆平臺

.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,微信服務器推送事件的時候會把這個值返回給咱們設置的接口中,而後我會根據這個值去拿相應的訂單數據展現在網頁上,這是後話。 

下面是封裝的生成臨時二維碼的方法: 

?
1
2
3
4
5
6
7
8
9
//建立臨時二維碼
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字符串。下面是這個方法: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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前面會加一個前綴。下面是微信公衆平臺文檔的說明:

用戶未關注時,進行關注後的事件推送

?
1
2
3
4
5
6
7
8
< 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 >
 

用戶已關注時的事件推送

?
1
2
3
4
5
6
7
8
9
< 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便可,由於這是我要展現給用戶看的訂單數據。下面是我寫的接收和處理部分,比較簡單,主要看一下應該怎麼接收微信推送的事件: 

?
1
2
3
4
5
6
7
8
9
10
11
12
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()方法: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
//給用戶發送圖文消息,點擊跳轉到報價頁面
  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格式: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
  "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,無論分享到哪兒,用什麼瀏覽器打開都是能夠訪問的,且展現的也是這個用戶的頭像和暱稱信息,這也是我要實現的一個效果。

相關文章
相關標籤/搜索