微信支付問題

如今,愈來愈多公司,選擇借微信的勢來發展本身的平臺,進入工做沒多久,我也被告知了要對接微信支付的需求。本來覺得這樣的對接,跟着文檔走,應該沒多大的難度的,但是後來,我才發現,原來我太天真了。在此,留下印記,說說我在微信支付上面遇到的那些問題。 一、關於微信支付 首先說下微信支付。隨着微信的紅火,微信支付在第三方支付也佔了一大塊地盤,愈來愈多的公司在本身的APP或者網站上集成了微信支付。從微信支付的官網https://pay.weixin.qq.com/index.php/home/login?return_url=/ 能夠看出,微信支付主要分爲四大塊,公衆號支付、APP支付、掃碼支付(網站)、刷卡支付。工做上,我接觸到了前三種,遇到了各類各樣的問題。 二、關於官方文檔 對於開發者來講,對接這種第三方支付,看其官方文檔尤爲重要。開發者能夠經過官網,查到對應不一樣支付模塊的官方文檔,可是,請你們注意,該文檔有待完善,徹底照着文檔作,可能實現不了你的功能 三、微信支付流程  php




    微信支付的流程,在微信支付官網上也有所顯示,這裏更加泛化的說一下,其實微信支付須要的是集成了微信SDK的客戶端,客戶先經過客戶端瀏覽完成訂單,而後後臺首先在業務系統生成了訂單,訂單生成後,業務系統請求微信服務器,進行統一下單。統一下單完成後,微信返回相關信息,後臺就能夠造成相應的支付二維碼或者是封裝出能夠調起微信支付須要的信息。接下來,用戶只要經過掃一掃或者點擊確認支付,即可以調出微信支付。支付成功後,微信會給用戶發送信息,同時也會對業務系統指定的地址發送對應的回調信息,將支付結果告知微信。同時,微信支付信息也能夠經過後臺直接請求微信支付來進行確認。 四、微信支付相關 首先,微信支付有一個最重要的過程,就是統一下單,簡單的說,開發者須要將業務系統中的訂單信息發送給微信,讓微信後臺造成在微信那邊的一個支付訂單。在向微信請求的時候,傳送的數據爲xml格式,微信要求xml傳送的數據須要進行一次加密,而後將加密的字符串附加在xml中一塊兒傳輸到服務器端,服務器端驗證經過以後才能進行下訂單操做。具體的算法說明地址https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3 如下,是基於java實現的加密算法:前端

view sourceprint?
01.1 /**
02.2 * 微信支付加密工具,須要加入key
03.3 */
04.4 public static String signature(Map<String, String> map, String key) {
05.5 Set<String> keySet = map.keySet();
06.6 String[] str = new String[map.size()];
07.7 StringBuilder tmp = new StringBuilder();
08.8 // 進行字典排序
09.9 str = keySet.toArray(str);
10.10 Arrays.sort(str);
11.11 for (int i = 0; i < str.length; i++) {
12.12 String t = str[i] + '=' + map.get(str[i]) + '&';
13.13 tmp.append(t);
14.14 }
15.15 if (StringUtils.isNotBlank(key)) {
16.16 tmp.append('key=' + key);
17.17 }
18.18 String tosend = tmp.toString();
19.19 MessageDigest md = null;
20.20 byte[] bytes = null;
21.21 try {
22.22
23.23 md = MessageDigest.getInstance('MD5');
24.24 bytes = md.digest(tosend.getBytes('utf-8'));
25.25 } catch (Exception e) {
26.26 e.printStackTrace();
27.27 }
28.28
29.29 String singe = byteToStr(bytes);
30.30 return singe.toUpperCase();
31.31
32.32 }

     微信支付第一個問題,數據加密的key。這個坑在於不細心,微信支付有不少key,包括咱們微信綁定時候本身輸入的key,還有微信給的隨機字符key,而這裏,在用於加密的key,並非咱們微信公衆號中的 AppSecret,而是在微信支付商戶後臺設置的key,設置的位置爲: key設置路徑:微信商戶平臺(pay.weixin.qq.com)-->帳戶設置-->API安全-->密鑰設置 若是不是使用該Key,哪怕是你的算法寫對了,數據傳到用戶那裏,依然返回的是簽名錯誤。 PS:微信官方給出了一個驗證簽名準確性的工具,該工具地址爲https://pay.weixin.qq.com/wiki/tools/signverify/,AnyWay,正如剛纔說了,若是key設置的不正確,好比說使用了AppSecret ,那麼,你會發現,該工具的出的加密字符串和本身得出的如出一轍,而後當你發送給微信服務端的時候,永遠返回的是錯誤。   微信支付第二個問題,post編碼問題。當組裝好數據後,須要經過POST的形式向微信服務器發送數據。但是,問題來了,微信的數據封裝的徹底正確,key也設置正確了,在官方的驗證工具上驗證出來也是正確的,但是,微信老是提示簽名錯誤。這個問題出如今post請求的編碼問題上,遇到這個問題的狀況是,在封裝數據的時候,xml裏面加入了中文,而後每次請求就會報錯,但是若是中文去掉,下單成功。最後才發現,原來POST的時候,沒有設置編碼,設置成爲UTF8以後就沒事了。但是,返回的簽名錯誤,也真心難排查啊   微信支付第三個問題,js-sdk調起支付控件。這一步時講在微信裏面H5調起支付控件的。須要注意的是要在H5上面調出支付控件,第一件事須要在微信公衆要後臺添加指定域名容許該域名調起控件,不然,是不能調起的。設置的教程在這:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3 。 設置完成以後,接下來是經過js調起,在此吐槽一下,我第一次作的時候,是直接copy的官方的js下來改的,但是。。。。。。。官方的JS上面全角半角的字符混合,致使的別說是他的JS了,就是我本身寫的JS最後都沒調出來。。。。。而後,關於提示,,蘋果版還好,安卓版的微信,若是調不出控件,它一點反應都不會有的。。相對而言,蘋果版會有一個彈框提示,因此後期,只要出現問題,都先用蘋果測測看看出了什麼錯。   微信支付第四個問題,app端數據封裝。可以統一下單了,這樣一來就是對數據封裝返回給前端了,這一部,仍是須要進行簽名,按理來講,前面和前面採用的是一樣的方法,應該問題不大才對。確實,在web端和掃碼支付都沒多大問題,但是,app端問題來了。我在公司剛開始和安卓的同事調這個的時候,原本覺得一個下午能搞定了。但是,卻不如咱們所想。咱們所有采用的是官方給的要求進行封裝的數據,我後臺統一下單完成以後,給到安卓,結果安卓死活調不出支付控件,並且一直都返回-1的結果,該結果,能夠說一點用處都沒有。安卓端的同時調了很久,一直沒有找到解決方案,值得一說的是,它官方給的Demo是能夠調出結果界面,但是也是調不出支付控件的。並且,他的java文件,utf-8和GBK兩種編碼混在一塊兒的。最後說一下,爲啥app調不出支付控件。java


 圖1 git

view sourceprint? 01.1 //網頁調起的時候 02.2 String time = Long.toString(System.currentTimeMillis()); 03.3 back.put('appId', mchappid); 04.4 back.put('timeStamp', time); 05.5 back.put('nonceStr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 06.6 back.put('package', 'prepay_id=' + order.getPrepay_id()); 07.7 back.put('signType', 'MD5'); 08.8 String sign2 = SignatureUtils.signature(back, wx_key); 09.9 10.10 JSONObject jsonObject = new JSONObject(); 11.11 jsonObject.put('appId', mchappid); 12.12 jsonObject.put('timeStamp', time); 13.13 jsonObject.put('nonceStr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 14.14 jsonObject.put('package', 'prepay_id=' + order.getPrepay_id()); 15.15 jsonObject.put('signType', 'MD5'); 16.16 jsonObject.put('paySign', sign2); 17.17 18.18 result.put('status', 'success'); 19.19 result.put('msg', '下單成功'); 20.20 result.put('obj', jsonObject); 21.21 return result; view sourceprint? 01.1 //APP調起的時候,請注意,安卓端不能用駝峯法,全部的key必須使用小寫 02.2 String time = Long.toString(System.currentTimeMillis()); 03.3 back.put('appid', app_mchappid); 04.4 back.put('timestamp', time); 05.5 back.put('partnerid', app_mchid); 06.6 back.put('noncestr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 07.7 back.put('prepayid', order.getPrepay_id()); 08.8 back.put('package', 'Sign=WXPay'); 09.9 String sign2 = SignatureUtils.signature(back, wx_key); 10.10 11.11 JSONObject jsonObject = new JSONObject(); 12.12 jsonObject.put('appid', app_mchappid); 13.13 jsonObject.put('timestamp', time); 14.14 jsonObject.put('partnerid', app_mchid); 15.15 jsonObject.put('noncestr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 16.16 jsonObject.put('prepayid', order.getPrepay_id()); 17.17 //jsonObject.put('package', 'Sign=WXPay'); 18.18 jsonObject.put('sign', sign2); 19.19 result.put('status', 'success'); 20.20 result.put('msg', '下單成功'); 21.21 result.put('obj', jsonObject); 22.22 return result; 如圖,圖1爲微信官方文檔中安卓調起支付控件的示例代碼,接下來爲web端調起支付控件時候進行加密的算法,最後爲解決問題後返回給APP數據時候數據封裝的代碼。問題所在就是在於,它數據的封裝不像官網所說的使用駝峯法,app的時候,須要把全部的字符小寫,,,小寫,,,,,,,。還有,官方說的packageValue是錯的,要用package,就是由於這些錯,加密出來的數據是錯的,因此app端才調不出支付控件。在此,爲我那個調到奔潰的同事默哀。   至今爲止遇到的問題大體如上,作下筆記,同時但願對同爲開發的朋友有用   最後,附上本人在github上的一個基於java的微信支付後臺Demo https://github.com/Seanid/wechatPay