都說微信支付有些坑,都抱怨微信支付的文檔太爛,一會APPId,一會商戶id,還有appsecret,支付API祕鑰讓你傻傻分不清楚,還有這裏大寫那裏小寫,幾種標準,讓你眼花繚亂。沒錯,這就是不少技術團隊都存在的問題,沒有統一的標準!且團隊越大越嚴重,即便是在微信這樣的頂尖團隊。然而這些在一番痛苦折騰以後,最後都會不值一提。這裏只詳細講JSAPI方式的公衆號支付html
有點相似受權回調安全域名的韻味,須要將支付的頁面路徑添加到受權目錄裏面,不然再頁面調起支付時會報沒有添加支付目錄的錯誤
前端
配置地址: 微信公衆平臺-微信支付-開發配置java
參考以前的微信受權文章git
一般狀況下是自身系統生成訂單後進入支付頁面,用戶點擊支付觸發一個請求,將訂單id、openid傳給後臺微信統一下單接口,後臺根據訂單id在自身系統查詢一遍,得到價格、描述詳情等信息程序員
String nonceStr = "5K8264ILTKCH16CQ2502SI8ZNMTM67VS";//暫時不變 // 加密,這裏只列舉必填字段 Map<String, String> map = new HashMap<String, String>(); map.put("body", body);//商品描述 map.put("mch_id", MCHID);//商戶平臺id map.put("appid", WX_APPID);//公衆號id map.put("nonce_str", nonceStr);//隨機字符串 map.put("notify_url", WX_PAY_CALLBACK);//異步回調api map.put("spbill_create_ip", ip);//支付ip map.put("out_trade_no", orderSn);//商品訂單號 map.put("total_fee", (int) relAmount + "");//真實金額 map.put("trade_type", "JSAPI");//JSAPI、h5調用 map.put("openid", openid);//支付用戶openid String sign = WxPaySignatureUtils.signature(map, WX_PAY_KEY); String xml = "<xml>" + "<appid>"+ WX_APPID +"</appid>"+ "<body>"+ body +"</body>"+ "<mch_id>"+ MCHID +"</mch_id>"+ "<nonce_str>"+ nonceStr +"</nonce_str>"+ "<notify_url>"+ WX_PAY_CALLBACK +"</notify_url>"+ "<openid>"+ openid +"</openid>"+ "<out_trade_no>"+ orderSn +"</out_trade_no>"+ "<spbill_create_ip>"+ ip +"</spbill_create_ip>"+ "<total_fee>"+ (int) relAmount + "" +"</total_fee>"+ "<trade_type>JSAPI</trade_type>"+ "<sign>"+ sign +"</sign>"+ "</xml>"; LOGGER.info("發送給微信的報文:" + xml); LOGGER.info("加密後的的簽名字符串:" + sign); // 請求 String response = ""; try { response = apiService.doPostString(WX_UNIFIEDORDER, xml); } catch (Exception e) { //TODO return null; } LOGGER.info("請求/pay/unifiedorder下單接口後返回數據:" + response); //處理請求結果 XStream s = new XStream(new DomDriver()); s.alias("xml", WechatOrder.class); WechatOrder order = (WechatOrder) s.fromXML(response); if ("SUCCESS".equals(order.getReturn_code()) && "SUCCESS".equals(order.getResult_code())) { LOGGER.info("微信支付統一下單請求成功,得到的Prepay_id是:" + order.getPrepay_id()); } else { LOGGER.error("微信支付統一下單請求錯誤:" + order.getReturn_msg() + order.getErr_code()); //TODO return null; }
注:github
這個時候若是你看到下面的日誌,恭喜你偉大的第一步已經完成了,能夠小憩喝杯咖啡再來哈哈算法
11:46:25.170 INFO com.cramix.portal.service.WechatService 451 createOrder - 請求/pay/unifiedorder下單接口後返回數據:<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><appid><![CDATA[wx8daca08d2f87216f]]></appid><mch_id><![CDATA[1482800922]]></mch_id><nonce_str><![CDATA[mjIbwmjLNFcD5tAF]]></nonce_str><sign><![CDATA[65EFB7B0BACBA01163765EB28B4E3F31]]></sign><result_code><![CDATA[SUCCESS]]></result_code><prepay_id><![CDATA[wx201706291146256b14b6eb620242392023]]></prepay_id><trade_type><![CDATA[JSAPI]]></trade_type></xml> 11:46:25.179 INFO com.cramix.portal.service.WechatService 459 createOrder - 微信支付統一下單請求成功,得到的Prepay_id是:wx201706291146256b14b6eb620242392023
固然,更多的時候不會有這麼順利,大多數人都會遇到簽名錯誤的狀態返回,微信確實是很喜歡用簽名,簽名錯誤這個四個字估計把全部的微信開發者都折磨了一遍,沒有經歷過簽名錯誤的程序員不是真正的微信開發者。json
然而,簽名錯誤該怎麼解決呢?一般狀況下注意有三:api
HttpPost post = new HttpPost(uri); post.setEntity(new StringEntity(str,"utf-8"));
當你喝完那杯「甜」的咖啡以後,就要擼接下來的代碼了。將前端須要用的字段進行加密校驗,並返回安全
核心代碼以下:
HashMap<String, String> back = new HashMap<String, String>(); String time = Long.toString(System.currentTimeMillis()); back.put("appId", WX_APPID); back.put("timeStamp", time); back.put("nonceStr", nonceStr); back.put("package", "prepay_id=" + order.getPrepay_id()); back.put("signType", "MD5"); String sign2 = WxPaySignatureUtils.signature(back, WX_PAY_KEY); LOGGER.info("二次簽名後返回給前端的簽名證書字符串是:" + sign2); JSONObject jsonObject = new JSONObject(); jsonObject.put("appId", WX_APPID); jsonObject.put("timeStamp", time); jsonObject.put("nonceStr", nonceStr); jsonObject.put("package", "prepay_id=" + order.getPrepay_id()); jsonObject.put("signType", "MD5"); jsonObject.put("paySign", sign2); LOGGER.info("二次簽名後返回給前端的數據是:" + jsonObject.toJSONString());
到此爲止,微信支付統一下單環節簡單的後臺代碼已經碼好了,只欠前端喚起支付了!
這裏有個巨坑,千萬不要踩,微信公衆平臺技術文檔-微信JSSDK裏面的微信支付跟這裏沒有任何關係!你只要看商戶平臺的文檔!,也就是經過WeixinJSBridge對象去調用,固然只在微信app裏有用!
//統一下單 unifiedorder: function(){ var self = this; var userInfo = localStorage.getItem('ssqf_h5_wxUserInfo'); CRAMIX.POST({ baseUrl: URL.BASE + '/api/wechat/pay/h5', data: { 'openid': JSON.parse(userInfo).openid, 'body':'書身祈福支付', 'orderSn': $('#orderSn').val() || 20 , 'amount': $('#money').val() || '0.01' }, success: function(data){ self.options.unifiedorderData = data; } }) }, //支付 pay: function(){ var self = this; var appId = self.options.unifiedorderData.appId; var timeStamp = self.options.unifiedorderData.timeStamp; var nonceStr = self.options.unifiedorderData.nonceStr; var package1 = self.options.unifiedorderData.package; var paySign = self.options.unifiedorderData.paySign; WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":appId, //公衆號名稱,由商戶傳入 "timeStamp":timeStamp, //時間戳,自1970年以來的秒數 "nonceStr":nonceStr, //隨機串 "package":package1, "signType":"MD5", //微信簽名方式: "paySign":paySign //微信簽名 }, function(res){ WeixinJSBridge.log(res.err_msg); if(res.err_msg == "get_brand_wcpay_request:ok"){ window.location.href = '/weixin/pay/pay-success.html';//去支付成功頁面 }else if(res.err_msg == "get_brand_wcpay_request:cancel"){ $.toast("用戶取消", "text"); }else{ $.toast("支付失敗", "forbidden", function() { window.location.reload();//刷新頁面 }); } } ); },
受權獲取openid這裏就很少說了,amount爲支付金額,我這裏是爲了方便測試改成了金額前端傳輸,實際確定不是哈!固然金額有變以後須要從新走一遍統一下單的流程。
其實咱們認爲的全部坑大部分緣由都是源自不細心,儘管文檔等各類會讓咱們多走一些彎路,可是不能帶着情緒去開發,遇到問題首先想自身不足,靜下心來,寫程序自己就須要有足夠的耐心和十分的細心。去年作微信分享,也是有個簽名,足足讓我困了三天才搞定,有時候真的都懷疑人生了,可是最後都解決了,解決的那一瞬間,真的能感動本身!這回作微信支付,一樣仍是簽名問題,也前先後後搞了差很少一天,固然這其中有很大部分緣由是吸收了去年的經驗,也就是上面所說。