最近在做微信的企業服務号,剛開始通過個人的測試平台進行開發,使用了自定義菜單,自定義菜單包含兩個功能:1、掃一掃,通過掃描我們賬單的二維碼,綁定賬戶和賬單的關系;2、打開我們系統的賬單查詢頁面,查詢賬戶的賬單資訊。
我的使用環境是:JSP+SpringMVC。
開發之前需要首先配置服務好的JS接口安全域名:“公衆号設定”-“功能設定”-“JS接口安全域名”。
在測試号下測試運作沒問題,實作方式是:掃一掃使用“scancode_waitmsg”類型,通過微信測試平台提供的“接口配置資訊”配置的url回傳掃描出的二維碼資訊,而打開網頁采用自定義菜單的‘view’類型,通過menu配置的url打開網頁。詳細配置菜單如下:
{
"button":
[
{
"type": "scancode_waitmsg",
"name": "掃一掃",
"key": "MENU_BIND_CASE"
},
{
"type": "view",
"name": "我的賬單",
"url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{appid}&redirect_uri=#{url}&response_type=code&scope=snsapi_base#wechat_redirect"
},
]
}
其中#{appid}和#{url}是我自己定制的參數占位符,釋出的時候會通過代碼的替換這兩個占位符。
當釋出到正式服務号時,問題發生了,表現出的現象是:點選掃一掃菜單也能啟動掃描二維碼功能,掃描完成後也能看到通路等待的提示,但是伺服器端拿不到微信端發送的任何二維碼的資訊。經過各種嘗試後發現,服務号裡邊有個設定:“開發”-》“基本配置”-》“伺服器配置”,這裡如果配置後啟用,則通過服務号的輸入框輸入的資訊就能再次調用這個url來通知伺服器輸入的資訊,但是帶來的問題是自定義菜單沒了!!!要想再次啟用自定義菜單,就必須關掉這個配置!!!這個魚與熊掌不能兼得的問題我沒找到好的配置方式來解決,如果讀者有好的方式希望能夠分享給我,郵箱:[email protected]
我解決這個問題的辦法是:放棄使用菜單的“scancode_waitmsg”類型,統一改成“view”類型的菜單,然後通過自定義的界面中調用微信的JS-SDK的方式來完成掃一掃的功能。
{
"button":
[
{
"type": "view",
"name": "掃一掃",
"url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{appid}&redirect_uri=#{url.scan}&response_type=code&scope=snsapi_base#wechat_redirect"
},
{
"type": "view",
"name": "我的賬單",
"url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{appid}&redirect_uri=#{url}&response_type=code&scope=snsapi_base#wechat_redirect"
},
]
}
具體的過程:
,如果你需要傳回送出的結果給使用者,可以通過頁面跳轉的方式傳回給使用者,具體方法是在jsp中添加一個form,通過form送出(不再使用ajax異步送出)的方式完成頁面的跳轉。你也可以不通過頁面提示給使用者,而選擇通過服務号對話框,發送消息給客戶,類似于銀行的信用卡消費提醒,實作這個功能你需要去申請自定義消息模版(防止企業号随意的給客戶發廣告騷擾客戶),具體可以去看微信的幫助文檔。
下面就來具體的講如何實作第二步的JS-SDK調用。
首先、調用JS-SDK是需要通過微信驗證的,建議您首先去檢視微信的幫助文檔;服務端首要要通過access_token獲得jsapi_ticket,擷取的方式參考access_token。以下主要說明一下wx.config()中的四個參數的由來:timestamp、nonceStr、signature、jsApiList;除了jsApiList外,其它的三個參數都是來自伺服器端。
-
- timestamp:伺服器端目前的時間,是一個long類型的數,時間機關精确到秒,如果是java下,通過System.currentTimeMillis()獲得時間精确到毫秒,這時你需要除以1000才能使用;js端和服務端要保持一緻;
- nonceStr:一個随機字元串,你可以每次都固定使用一個,也可以随機一個uuid使用,js和後端的值保持一緻就行;
- singature:一個通過計算獲得的加密字元串,加密算法為sha1,加密的文本為一個包含4部分的字元串(timestamp、noncestr、url、jsapi_ticket),其中noncestr需要注意名字大小寫,與js的大小寫不同!加密過程如下:a、獲得目前的timestamp、noncestr、url、jsapi_ticket,然後将他們的變量名和值連接配接成“name=value”的字元串;(其中url為通過request請求獲得字元串,具體參照執行個體代碼,另外在jsp中通過alert這個屬性也可以拿到url:location.href.split('#')[0])b、對連接配接後的四個字元串進行按照asc碼升序排列,排列完成後将4個字元串通過連接配接符“&”連接配接為一個字元串;c、對連接配接後的字元串通過sha1加密并轉換成16進制的字元串;d、将timestamp、noncestr、url、jsapi_ticket傳回給jsp,傳入給wx.config(),以備微信服務端驗證。
- jsApiList:一個string類型的數組,内容為微信提供的功能識别關鍵字。
然後、修改JSP頁面,完成wx的config配置方法;
最後、驗證通過後系統會調用wx.ready()方法,通過本方法調用我們的掃一掃接口:wx.scanQRCode()完成掃描後通過ajax或者form送出的方式送出資料到指定的處理資料URL上,完成功能。
執行個體代碼如下:
SpringMVC-Controller-字元串加密部分:
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String noncestr = UUID.randomUUID().toString();
String url = request.getScheme() + "://" + request.getServerName() + request.getServletPath();
if (request.getQueryString() != null) {
url += "?" + request.getQueryString();
}
String[] tempArr = { "jsapi_ticket=" + jsapi_ticket, "timestamp=" + timestamp, "noncestr=" + noncestr, "url=" + url };
Arrays.sort(tempArr);
String concatStr = tempArr[0] + "&" + tempArr[1] + "&" + tempArr[2] + "&" + tempArr[3];
String signature = DigestUtils.sha1Hex(concatStr);
JSP-wx.config:
wx.config({
debug: false,
appId: '${appId}',
timestamp: '${timestamp}',
nonceStr: '${noncestr}',
signature: '${signature}',
jsApiList: ['scanQRCode']
});
JSP-wx.ready:
wx.ready(function() {
wx.scanQRCode({
needResult: 1,
scanType: ["qrCode", "barCode"],
success: function(res) {
$('#content').val(res.resultStr);
$('#form1').submit();
}
});
});
SpringMVC-Controller-獲得二維碼資訊部分為一個普通的controller方法,這裡不再描述。
如果總是提示驗證不同的問題,微信也提供了驗證測試接口。
具體開發中你可能發現,整個過程并沒有關于如何獲得目前微信使用者和我們自有網站賬戶的關聯關系邏輯。其實這個問題的關鍵就是生成singature時的url,細心的人會發現這個url中有個code,可以通過這個code到微信伺服器中拿到openid,再通過openid獲得我們系統的userid,由于篇幅原因不再細說,具體可以參考我的另一篇文章(如何通過使用者點選自定義菜單的view類型标簽獲得所綁定的系統使用者)。