現在,越來越多公司,選擇借微信的勢來發展自己的平台,進入工作沒多久,我也被告知了要對接微信支付的需求。原本以為這樣的對接,跟着文檔走,應該沒多大的難度的,可是后來,我才發現,原來我太天真了。在此,留下印記,說說我在微信支付上面遇到的那些問題。
1、關於微信支付
首先說下微信支付。隨着微信的紅火,微信支付在第三方支付也占了一大塊地盤,越來越多的公司在自己的APP或者網站上集成了微信支付。從微信支付的官網https://pay.weixin.qq.com/index.php/home/login?return_url=/ 可以看出,微信支付主要分為四大塊,公眾號支付、APP支付、掃碼支付(網站)、刷卡支付。工作上,我接觸到了前三種,遇到了各種各樣的問題。
2、關於官方文檔
對於開發者來說,對接這種第三方支付,看其官方文檔尤其重要。開發者可以通過官網,查到對應不同支付模塊的官方文檔,但是,請大家注意,該文檔有待完善,完全照着文檔做,可能實現不了你的功能
3、微信支付流程
微信支付的流程,在微信支付官網上也有所顯示,這里更加泛化的說一下,其實微信支付需要的是集成了微信SDK的客戶端,客戶先通過客戶端瀏覽完成訂單,然后后台首先在業務系統生成了訂單,訂單生成后,業務系統請求微信服務器,進行統一下單。統一下單完成后,微信返回相關信息,后台就可以形成相應的支付二維碼或者是封裝出可以調起微信支付需要的信息。接下來,用戶只要通過掃一掃或者點擊確認支付,便可以調出微信支付。支付成功后,微信會給用戶發送信息,同時也會對業務系統指定的地址發送對應的回調信息,將支付結果告知微信。同時,微信支付信息也可以通過后台直接請求微信支付來進行確認。
4、微信支付相關
首先,微信支付有一個最重要的過程,就是統一下單,簡單的說,開發者需要將業務系統中的訂單信息發送給微信,讓微信后台形成在微信那邊的一個支付訂單。在向微信請求的時候,傳送的數據為xml格式,微信要求xml傳送的數據需要進行一次加密,然后將加密的字符串附加在xml中一起傳輸到服務器端,服務器端驗證通過之后才能進行下訂單操作。具體的算法說明地址https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
以下,是基於java實現的加密算法:
1 /** 2 * 微信支付加密工具,需要加入key 3 */ 4 public static String signature(Map<String, String> map, String key) { 5 Set<String> keySet = map.keySet(); 6 String[] str = new String[map.size()]; 7 StringBuilder tmp = new StringBuilder(); 8 // 進行字典排序 9 str = keySet.toArray(str); 10 Arrays.sort(str); 11 for (int i = 0; i < str.length; i++) { 12 String t = str[i] + "=" + map.get(str[i]) + "&"; 13 tmp.append(t); 14 } 15 if (StringUtils.isNotBlank(key)) { 16 tmp.append("key=" + key); 17 } 18 String tosend = tmp.toString(); 19 MessageDigest md = null; 20 byte[] bytes = null; 21 try { 22 23 md = MessageDigest.getInstance("MD5"); 24 bytes = md.digest(tosend.getBytes("utf-8")); 25 } catch (Exception e) { 26 e.printStackTrace(); 27 } 28 29 String singe = byteToStr(bytes); 30 return singe.toUpperCase(); 31 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調不出支付控件。
圖1
1 //網頁調起的時候 2 String time = Long.toString(System.currentTimeMillis()); 3 back.put("appId", mchappid); 4 back.put("timeStamp", time); 5 back.put("nonceStr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); 6 back.put("package", "prepay_id=" + order.getPrepay_id()); 7 back.put("signType", "MD5"); 8 String sign2 = SignatureUtils.signature(back, wx_key); 9 10 JSONObject jsonObject = new JSONObject(); 11 jsonObject.put("appId", mchappid); 12 jsonObject.put("timeStamp", time); 13 jsonObject.put("nonceStr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); 14 jsonObject.put("package", "prepay_id=" + order.getPrepay_id()); 15 jsonObject.put("signType", "MD5"); 16 jsonObject.put("paySign", sign2); 17 18 result.put("status", "success"); 19 result.put("msg", "下單成功"); 20 result.put("obj", jsonObject); 21 return result;
1 //APP調起的時候,請注意,安卓端不能用駝峰法,所有的key必須使用小寫 2 String time = Long.toString(System.currentTimeMillis()); 3 back.put("appid", app_mchappid); 4 back.put("timestamp", time); 5 back.put("partnerid", app_mchid); 6 back.put("noncestr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); 7 back.put("prepayid", order.getPrepay_id()); 8 back.put("package", "Sign=WXPay"); 9 String sign2 = SignatureUtils.signature(back, wx_key); 10 11 JSONObject jsonObject = new JSONObject(); 12 jsonObject.put("appid", app_mchappid); 13 jsonObject.put("timestamp", time); 14 jsonObject.put("partnerid", app_mchid); 15 jsonObject.put("noncestr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"); 16 jsonObject.put("prepayid", order.getPrepay_id()); 17 //jsonObject.put("package", "Sign=WXPay"); 18 jsonObject.put("sign", sign2); 19 result.put("status", "success"); 20 result.put("msg", "下單成功"); 21 result.put("obj", jsonObject); 22 return result;
如圖,圖1為微信官方文檔中安卓調起支付控件的示例代碼,接下來為web端調起支付控件時候進行加密的算法,最后為解決問題后返回給APP數據時候數據封裝的代碼。問題所在就是在於,它數據的封裝不像官網所說的使用駝峰法,app的時候,需要把所有的字符小寫,,,小寫,,,,,,,。還有,官方說的packageValue是錯的,要用package,就是因為這些錯,加密出來的數據是錯的,所以app端才調不出支付控件。在此,為我那個調到奔潰的同事默哀。
至今為止遇到的問題大致如上,做下筆記,同時希望對同為開發的朋友有用
最后,附上本人在github上的一個基於java的微信支付后台Demo https://github.com/Seanid/wechatPay
