最近在做微信的企業服務號,剛開始通過個人的測試平台進行開發,使用了自定義菜單,自定義菜單包含兩個功能: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來通知服務器輸入的信息,但是帶來的問題是自定義菜單沒了!!!要想再次啟用自定義菜單,就必須關掉這個配置!!!這個魚與熊掌不能兼得的問題我沒找到好的配置方式來解決,如果讀者有好的方式希望能夠分享給我,郵箱:cqu2005@163.com
我解決這個問題的辦法是:放棄使用菜單的“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類型標簽獲得所綁定的系統用戶)。