都說微信支付有些坑,都抱怨微信支付的文檔太爛,一會(huì)APPId,一會(huì)商戶id,還有appsecret,支付API秘鑰讓你傻傻分不清楚,還有這里大寫那里小寫,幾種標(biāo)準(zhǔn),讓你眼花繚亂。沒錯(cuò),這就是很多技術(shù)團(tuán)隊(duì)都存在的問題,沒有統(tǒng)一的標(biāo)準(zhǔn)!且團(tuán)隊(duì)越大越嚴(yán)重,即使是在微信這樣的頂尖團(tuán)隊(duì)。然而這些在一番痛苦折騰之后,最后都會(huì)不值一提。這里只詳細(xì)講JSAPI方式的公眾號(hào)支付
簡(jiǎn)要思路圖
配置支付授權(quán)目錄
有點(diǎn)類似授權(quán)回調(diào)安全域名的韻味,需要將支付的頁面路徑添加到授權(quán)目錄里面,否則再頁面調(diào)起支付時(shí)會(huì)報(bào)沒有添加支付目錄的錯(cuò)誤
配置地址: 微信公眾平臺(tái)-微信支付-開發(fā)配置
獲取openid
統(tǒng)一下單
通常情況下是自身系統(tǒng)生成訂單后進(jìn)入支付頁面,用戶點(diǎn)擊支付觸發(fā)一個(gè)請(qǐng)求,將訂單id、openid傳給后臺(tái)微信統(tǒng)一下單接口,后臺(tái)根據(jù)訂單id在自身系統(tǒng)查詢一遍,獲得價(jià)格、描述詳情等信息
一次簽名,發(fā)送xml報(bào)文給微信服務(wù)器
String nonceStr = "5K8264ILTKCH16CQ2502SI8ZNMTM67VS";//暫時(shí)不變 // 加密,這里只列舉必填字段Map<String, String> map = new HashMap<String, String>();map.put("body", body);//商品描述map.put("mch_id", MCHID);//商戶平臺(tái)idmap.put("appid", WX_APPID);//公眾號(hào)idmap.put("nonce_str", nonceStr);//隨機(jī)字符串map.put("notify_url", WX_PAY_CALLBACK);//異步回調(diào)apimap.put("spbill_create_ip", ip);//支付ipmap.put("out_trade_no", orderSn);//商品訂單號(hào)map.put("total_fee", (int) relAmount + "");//真實(shí)金額map.put("trade_type", "JSAPI");//JSAPI、h5調(diào)用map.put("openid", openid);//支付用戶openidString 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("發(fā)送給微信的報(bào)文:" + xml); LOGGER.info("加密后的的簽名字符串:" + sign);// 請(qǐng)求String response = "";try { response = apiService.doPostString(WX_UNIFIEDORDER, xml); } catch (Exception e) { //TODO return null; } LOGGER.info("請(qǐng)求/pay/unifiedorder下單接口后返回?cái)?shù)據(jù):" + response);//處理請(qǐng)求結(jié)果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("微信支付統(tǒng)一下單請(qǐng)求成功,獲得的Prepay_id是:" + order.getPrepay_id()); } else { LOGGER.error("微信支付統(tǒng)一下單請(qǐng)求錯(cuò)誤:" + order.getReturn_msg() + order.getErr_code()); //TODO return null; }
注:
WX_UNIFIEDORDER變量的地址是: https://api.mch.weixin.qq.com/pay/unifiedorder
relAmount 是實(shí)際支付的金額,實(shí)際項(xiàng)目中應(yīng)該是根據(jù)訂單號(hào)在自身系統(tǒng)查詢后獲得
body 是最終顯示在支付憑證里面的【商品詳情】
orderSn 是最終顯示在支付憑證-交易記錄 里面的【商戶單號(hào)】,也就是自身系統(tǒng)的訂單號(hào)
這個(gè)時(shí)候如果你看到下面的日志,恭喜你偉大的第一步已經(jīng)完成了,可以小憩喝杯咖啡再來哈哈
11:46:25.170 INFO com.cramix.portal.service.WechatService 451 createOrder - 請(qǐng)求/pay/unifiedorder下單接口后返回?cái)?shù)據(jù):<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 - 微信支付統(tǒng)一下單請(qǐng)求成功,獲得的Prepay_id是:wx201706291146256b14b6eb620242392023
當(dāng)然,更多的時(shí)候不會(huì)有這么順利,大多數(shù)人都會(huì)遇到簽名錯(cuò)誤的狀態(tài)返回,微信確實(shí)是很喜歡用簽名,簽名錯(cuò)誤這個(gè)四個(gè)字估計(jì)把所有的微信開發(fā)者都折磨了一遍,沒有經(jīng)歷過簽名錯(cuò)誤的程序員不是真正的微信開發(fā)者。
然而,簽名錯(cuò)誤該怎么解決呢?通常情況下注意有三:
發(fā)送給微信的xml報(bào)文格式、字段有誤。body字段如有中文一定要在請(qǐng)求里面加上這句:
HttpPost post = new HttpPost(uri); post.setEntity(new StringEntity(str,"utf-8"));
加密算法是否有誤,參考WxPaySignatureUtils
確認(rèn)商戶平臺(tái)API秘鑰(仔細(xì)閱讀微信支付申請(qǐng)成功后發(fā)過來的郵件,點(diǎn)擊【下載API證書、設(shè)置API密鑰】)
二次簽名
當(dāng)你喝完那杯“甜”的咖啡之后,就要擼接下來的代碼了。將前端需要用的字段進(jìn)行加密校驗(yàn),并返回
核心代碼如下:
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("二次簽名后返回給前端的數(shù)據(jù)是:" + jsonObject.toJSONString());
到此為止,微信支付統(tǒng)一下單環(huán)節(jié)簡(jiǎn)單的后臺(tái)代碼已經(jīng)碼好了,只欠前端喚起支付了!
前端調(diào)起支付
這里有個(gè)巨坑,千萬不要踩,微信公眾平臺(tái)技術(shù)文檔-微信JSSDK里面的微信支付跟這里沒有任何關(guān)系!你只要看商戶平臺(tái)的文檔!,也就是通過WeixinJSBridge對(duì)象去調(diào)用,當(dāng)然只在微信app里有用!
//統(tǒng)一下單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, //公眾號(hào)名稱,由商戶傳入 "timeStamp":timeStamp, //時(shí)間戳,自1970年以來的秒數(shù) "nonceStr":nonceStr, //隨機(jī)串 "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();//刷新頁面 }); } } ); },
授權(quán)獲取openid這里就不多說了,amount為支付金額,我這里是為了方便測(cè)試改為了金額前端傳輸,實(shí)際肯定不是哈!當(dāng)然金額有變之后需要重新走一遍統(tǒng)一下單的流程。
感悟
其實(shí)我們認(rèn)為的所有坑大部分原因都是源自不細(xì)心,盡管文檔等各種會(huì)讓我們多走一些彎路,但是不能帶著情緒去開發(fā),遇到問題首先想自身不足,靜下心來,寫程序本身就需要有足夠的耐心和十分的細(xì)心。去年做微信分享,也是有個(gè)簽名,足足讓我困了三天才搞定,有時(shí)候真的都懷疑人生了,但是最后都解決了,解決的那一瞬間,真的能感動(dòng)自己!這回做微信支付,同樣還是簽名問題,也前前后后搞了差不多一天,當(dāng)然這其中有很大部分原因是吸取了去年的經(jīng)驗(yàn),也就是上面所說。
http://www.cnblogs.com/liliangel/p/7095039.html