這兩天作微信支付開發。碰到大坑。糾結死我了。好不容作完。前端
後臺java:直接上代碼:注意區分先後端的變量大小寫。。。java
@RequestMapping(value = "/index") public Model index(@RequestParam(value = "openid", required = true) String openid ,Model model,HttpServletRequest request) throws Exception{ logger.info("************openid***********爲:"+openid); //獲取prepayid Map<String ,String > map=new HashMap<String,String>(); WeiXinConfig wcf=weiXinBaseService.getWeiXinConfig(); String nonceStr=UUID.randomUUID().toString().substring(0, 32); oauthService.shareFactory(request); String appid=wcf.getAppid(); long timestamp = System.currentTimeMillis() / 1000; map.put("appid", appid ); map.put("mch_id", WebConfig.get("pay.mch_id")); map.put("nonce_str",nonceStr); map.put("body", WebConfig.get("pay.body")); map.put("out_trade_no", payWxUtil.orderNum()); map.put("total_fee", WebConfig.get("pay.price")); map.put("spbill_create_ip",request.getRemoteAddr()); map.put("notify_url", WebConfig.get("hostAddress")+request.getContextPath()+"/babyShow/payInfo/info"); map.put("trade_type", "JSAPI"); map.put("openid", openid); String paySign=SignUtil.getPayCustomSign(map,WebConfig.get("pay.key")); map.put("sign",paySign); String xml= CommonUtil.ArrayToXml(map); String prepayid= payWxUtil.getPrepayid(xml); logger.info("prepareid*****************************="+prepayid); //封裝h5頁面調用參數 Map<String ,String > signMap=new HashMap<String ,String >(); signMap.put("appId", appid); logger.info("appId="+appid); signMap.put("timeStamp", timestamp+""); logger.info("timeStamp="+timestamp); signMap.put("package", "prepay_id="+prepayid); logger.info("package="+"prepay_id="+prepayid); signMap.put("signType", "MD5"); logger.info("singType="+"MD5"); signMap.put("nonceStr", nonceStr); logger.info("nonceStr="+nonceStr); model.addAttribute("paytimestamp", timestamp); model.addAttribute("paypackage", "prepay_id="+prepayid); model.addAttribute("paynonceStr", nonceStr); model.addAttribute("paysignType", "MD5"); String paySign2=SignUtil.getPayCustomSign(signMap,WebConfig.get("pay.key")); model.addAttribute("paySign",paySign2 ); logger.info("paySign="+paySign2); return model; }
以上代碼獲取openid須要根據網頁受權來獲取。這裏就很少講了。主要講講獲取prepayid和生成h5頁面所需參數,git
這裏面比較麻煩的就是簽名的獲取json
查看方法SignUtil.getPayCustomSign(signMap,WebConfig.get("pay.key"))後端
代碼以下api
/** * 獲取支付所需簽名 * @param ticket * @param timeStamp * @param card_id * @param code * @return * @throws Exception */ public static String getPayCustomSign(Map<String, String> bizObj,String key) throws Exception { String bizString = CommonUtil.FormatBizQueryParaMap(bizObj, false); logger.info(bizString); return MD5SignUtil.sign(bizString, key); }
其中CommonUtil.FormatBizQueryParaMap是用來作字典排序的。有參考了網上的例子。就沒單獨作。微信
public static String FormatBizQueryParaMap(Map<String, String> paraMap, boolean urlencode) throws Exception { String buff = ""; try { List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>( paraMap.entrySet()); Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() { public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) { return (o1.getKey()).toString().compareTo( o2.getKey()); } }); for (int i = 0; i < infoIds.size(); i++) { Map.Entry<String, String> item = infoIds.get(i); //System.out.println(item.getKey()); if (item.getKey() != "") { String key = item.getKey(); String val = item.getValue(); if (urlencode) { val = URLEncoder.encode(val, "utf-8"); } buff += key + "=" + val + "&"; } } if (buff.isEmpty() == false) { buff = buff.substring(0, buff.length() - 1); } } catch (Exception e) { throw new Exception(e.getMessage()); } return buff; }
其中MD5SignUtil.sign(bizString, key)方法以下網絡
public static String sign(String content, String key) throws Exception{ String signStr = ""; signStr = content + "&key=" + key; return MD5Util.MD5(signStr).toUpperCase(); }
其中MD5Util.MD5(signStr)方法以下
public final static String MD5(String s) { char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; try { byte[] btInput = s.getBytes(); MessageDigest mdInst = MessageDigest.getInstance("MD5"); mdInst.update(btInput); byte[] md = mdInst.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } }
CommonUtil.ArrayToXml(map)方法以下
public static String ArrayToXml(Map<String, String> arr) { String xml = "<xml>"; Iterator<Entry<String, String>> iter = arr.entrySet().iterator(); while (iter.hasNext()) { Entry<String, String> entry = iter.next(); String key = entry.getKey(); String val = entry.getValue(); if (IsNumeric(val)) { xml += "<" + key + ">" + val + "</" + key + ">"; } else xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">"; } xml += "</xml>"; return xml; }
public static boolean IsNumeric(String str) { if (str.matches("\\d *")) { return true; } else { return false; } }
發送請求到微信獲取prepayid代碼以下app
public static String URL="https://api.mch.weixin.qq.com/pay/unifiedorder"; @SuppressWarnings("deprecation") public JSONObject getPrepayJson(String xml){ HttpClient httpClient = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) ); InputStream is = null; PostMethod method=null; try { String url =URL; method = HttpClientUtils.postMethod(url); method.setRequestBody(xml); httpClient.executeMethod(method); //讀取響應 is = method.getResponseBodyAsStream(); JSONObject o =Xml2JsonUtil.xml2JSON(is); return o; } catch (Exception e) { e.printStackTrace(); }finally{ if(method!=null){ method.releaseConnection(); } if(is!=null){ try { is.close(); } catch (IOException e1) { e1.printStackTrace(); } } } return null; } public String getPrepayid(String xml){ try { JSONObject jo=getPrepayJson(xml); JSONObject element=jo.getJSONObject("xml"); String prepayid=((JSONArray)element.get("prepay_id")).get(0).toString(); return prepayid; } catch (Exception e) { e.printStackTrace(); } return null; } public String orderNum(){ String chars = "0123456789"; String order = System.currentTimeMillis()+""; String res = ""; for (int i = 0; i < 19; i++) { Random rd = new Random(); res += chars.charAt(rd.nextInt(chars.length() - 1)); } order+=res; return order; }
xml轉json方法Xml2JsonUtil.xml2JSON(is);。摘自網絡dom
/** * 轉換一個xml格式的字符串到json格式 * * @param xml * xml格式的字符串 * @return 成功返回json 格式的字符串;失敗反回null */ @SuppressWarnings("unchecked") public static JSONObject xml2JSON(InputStream is) { JSONObject obj = new JSONObject(); try { SAXReader sb = new SAXReader(); Document doc = sb.read(is); Element root = doc.getRootElement(); obj.put(root.getName(), iterateElement(root)); return obj; } catch (Exception e) { log.error("傳入XML後轉換JSON出現錯誤===== Xml2JsonUtil-->xml2JSON============>>",e); return null; } } /** * 一個迭代方法 * * @param element * : org.jdom.Element * @return java.util.Map 實例 */ @SuppressWarnings("unchecked") private static Map iterateElement(Element element) { List jiedian = element.elements() ; Element et = null; Map obj = new HashMap(); List list = null; for (int i = 0; i < jiedian.size(); i++) { list = new LinkedList(); et = (Element) jiedian.get(i); if (et.getTextTrim().equals("")) { if (et.elements().size() == 0) continue; if (obj.containsKey(et.getName())) { list = (List) obj.get(et.getName()); } list.add(iterateElement(et)); obj.put(et.getName(), list); } else { if (obj.containsKey(et.getName())) { list = (List) obj.get(et.getName()); } list.add(et.getTextTrim()); obj.put(et.getName(), list); } } return obj; }
服務端基本這麼多
前端被微信和我本身挖了大坑,整整搞了兩天,擦
先是看的微信文檔jssdk文檔(開始埋坑。。)
頁面引入js
加入
wx.config({ debug: false, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。 appId: '${appid}', // 必填,公衆號的惟一標識 timestamp: '${timestamp}', // 必填,生成簽名的時間戳 nonceStr: '${nonceStr}', // 必填,生成簽名的隨機串 signature: '${signature}',// 必填,簽名,見附錄1 jsApiList: [ 'chooseWXPay' ] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2 });
嗯。配置好了。若是傳入jsconfig的參數。。。
而後看到jssdk裏面有發送一個微信支付請求?
嗯加進去
function pay(){ wx.chooseWXPay({ timestamp: ${paytimestamp}, // 支付簽名時間戳,注意微信jssdk中的全部使用timestamp字段均爲小寫。但最新版的支付後臺生成簽名使用的timeStamp字段名需大寫其中的S字符 nonceStr: '${paynonceStr}', // 支付簽名隨機串,不長於 32 位 package: '${paypackage}', // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=***) signType: '${paysignType}', // 簽名方式,默認爲'SHA1',使用新版支付需傳入'MD5' paySign: '${paySign}', // 支付簽名 success: function (res) { alert("支付成功"); // 支付成功後的回調函數 } }); }
而後發佈,執行調試。。尼瑪。怎麼樣都支付不了。一下子報訂單信息錯誤、一下子報簽名錯誤。。。找了N多資料均不對
後來想啊。大不了用商戶平臺提供的文檔寫接口什麼的。因而加入了代碼
function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId" : "${appid}", //公衆號名稱,由商戶傳入 "timeStamp":"${paytimestamp}", //時間戳,自1970年以來的秒數 "nonceStr" : "${paynonceStr}", //隨機串 "package" : "${paypackage}", "signType" : "${paysignType}", //微信簽名方式: "paySign" : "${paySign}" //微信簽名 }, function(res){ alert(res.err_msg); // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返 } ); } function pay2(){ if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); } }
發佈調試。。。仍是不對。。兩個按鈕pay()和pay2()均不對。pay方法仍是一下子訂單信息錯誤一下子簽名錯誤。pay2()方法則一直報簽名錯誤。。。尼瑪。吧經歷放後臺簽名。。怎麼改都不行。簽名明明對的啊。能夠獲取prepayid的。爲毛封裝h5參數時就出錯了呢。。。折騰啊。。。搞了N久。。就尼瑪不對。。。後來一個小錯誤pay方法js報錯了。。結果pay2方法確能夠執行了。。。吧pay的js報錯去了以後pay2又不能用了。。我擦。。哥有預感。。難道兩種方式衝突了??因而我把config和pay方法刪除了再試試。。。尼瑪果真能夠了。不能加入config和pay。。。果真能夠了。。。我擦。折騰的。。微信啃爹啊。第一種方式沒法用有沒有,誤導人啊。。。正確結果就是商品平臺的文檔使用方法js以下:
function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId" : "${appid}", //公衆號名稱,由商戶傳入 "timeStamp":"${paytimestamp}", //時間戳,自1970年以來的秒數 "nonceStr" : "${paynonceStr}", //隨機串 "package" : "${paypackage}", "signType" : "${paysignType}", //微信簽名方式: "paySign" : "${paySign}" //微信簽名 }, function(res){ alert(res.err_msg); // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返 } ); } function pay(){ if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); } }
至此。終於把微信支付搞定了。留着博客。防止後人走彎路(config裏面不能加入chooseWXPay方法。。切記)