微信支付(統一下單)
思路:
①將appid、mch_id、nonce_str、body、attach、out_trade_no、total_fee、spbill_create_ip、notify_url、trade_type 這些參數以鍵值對的形式拼接起來用MD5進行第一次簽名
②拼接xml:
例如:
<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付測試</attach>
<body>JSAPI支付測試</body>
<mch_id>10000100</mch_id>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url>
<openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>JSAPI</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
,把這個xml 交個 "https://api.mch.weixin.qq.com/pay/unifiedorder" 在微信 生成一個預支付訂單號prepay_id
③將 appid、partner、prepay_id、nonce_str、timestamp、partnerkey、key、package 經過鍵值對的形式拼接,而後MD5加密處理,和第一步加密方式同樣進行第二次簽名
④將appid、partnerid、prepayid、package、noncestr、timestamp、sign 傳給 調起微信支付功能
注:一些用到的方法
一、MD5 將map 拼接成鍵值對的串,而後進行加密
/** * 建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 */ public String createSign(SortedMap<String, String> packageParams) { StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + this.getKey()); String sign = MD5Util.MD5Encode(sb.toString(), this.charset) .toUpperCase(); return sign; }
二、將xml交給 "https://api.mch.weixin.qq.com/pay/unifiedorder" 解析生成 預支付訂單
若是參數提交失敗,會在try裏的 if 中 返回 開發的時候建議打斷點,我是存session ,而後返回錯誤信息msg
/** *description:獲取預支付id *@param urls *@param xmlParam *@return * @author ex_yangxiaoyi * @see */ public static String getPayNo(String url,String xmlParam){ Subject currentUser = SecurityUtils.getSubject(); Session session = currentUser.getSession(); DefaultHttpClient client = new DefaultHttpClient(); client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true); HttpPost httpost= HttpClientConnectionManager.getPostMethod(url); String prepay_id = ""; try { httpost.setEntity(new StringEntity(xmlParam, "UTF-8")); HttpResponse response = httpclient.execute(httpost); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); if(jsonStr.indexOf("FAIL")!=-1){ session.setAttribute("error_msg", jsonStr); return prepay_id; } Map map = doXMLParse(jsonStr); prepay_id = (String) map.get("prepay_id"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return prepay_id; }
public static InputStream String2Inputstream(String str) { return new ByteArrayInputStream(str.getBytes()); }
三、xml解析
/** * 解析xml,返回第一級元素鍵值對。若是第一級元素有子節點,則此節點的值是子節點的xml數據。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws Exception { if(null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); InputStream in = String2Inputstream(strxml); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) { v = e.getTextNormalize(); } else { v = getChildrenText(children); } m.put(k, v); } //關閉流 in.close(); return m; }
/** * 獲取子結點的xml * @param children * @return String */ public static String getChildrenText(List children) { StringBuffer sb = new StringBuffer(); if(!children.isEmpty()) { Iterator it = children.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append("<" + name + ">"); if(!list.isEmpty()) { sb.append(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); }
四、微信回調函數
/** * 微信支付 回調函數 */ @RequestMapping("/notify") @ResponseBody protected void notify(HttpServletRequest request, HttpServletResponse response) throws Exception { PageData pd=new PageData(); logBefore(logger, "微信支付 回調函數"); //把以下代碼貼到的你的處理回調的servlet 或者.do 中便可明白回調操做 logger.info("微信支付回調數據開始"); //示例報文 //String xml = "<xml><appid><![CDATA[wxb4dc385f953b356e]]></appid><bank_type><![CDATA[CCB_CREDIT]]></bank_type><cash_fee><![CDATA[1]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[1228442802]]></mch_id><nonce_str><![CDATA[1002477130]]></nonce_str><openid><![CDATA[o-HREuJzRr3moMvv990VdfnQ8x4k]]></openid><out_trade_no><![CDATA[1000000000051249]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[1269E03E43F2B8C388A414EDAE185CEE]]></sign><time_end><![CDATA[20150324100405]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[1009530574201503240036299496]]></transaction_id></xml>"; String inputLine; String notityXml = ""; String resXml = ""; try { while ((inputLine = request.getReader().readLine()) != null) { notityXml += inputLine; } request.getReader().close(); } catch (Exception e) { e.printStackTrace(); } System.out.println("接收到的報文:" + notityXml); BufferedWriter writer = new BufferedWriter(new FileWriter(new File("c:\\ResultXml.txt"),true)); // writer.write(DateUtil.getTime()+notityXml+"\r\n"); writer.close(); Map m = parseXmlToList2(notityXml); WxPayResult wpr = new WxPayResult(); wpr.setAppid(m.get("appid").toString()); wpr.setBankType(m.get("bank_type").toString()); wpr.setCashFee(m.get("cash_fee").toString()); wpr.setFeeType(m.get("fee_type").toString()); wpr.setIsSubscribe(m.get("is_subscribe").toString()); wpr.setMchId(m.get("mch_id").toString()); wpr.setNonceStr(m.get("nonce_str").toString()); wpr.setOpenid(m.get("openid").toString()); wpr.setOutTradeNo(m.get("out_trade_no").toString()); wpr.setResultCode(m.get("result_code").toString()); wpr.setReturnCode(m.get("return_code").toString()); wpr.setSign(m.get("sign").toString()); wpr.setTimeEnd(m.get("time_end").toString()); wpr.setTotalFee(m.get("total_fee").toString()); wpr.setTradeType(m.get("trade_type").toString()); wpr.setTransactionId(m.get("transaction_id").toString()); if("SUCCESS".equals(wpr.getResultCode())){ //支付成功 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; //修改訂單狀態 pd.put("WX_ORDER", wpr.getOutTradeNo()); wxPayService.editStatusByOutId(pd); //保存訂單信息 pd.put("WX_ORDER_ID", UuidUtil.get32UUID());//微信訂單主鍵ID pd.put("OPEN_ID", wpr.getOpenid());//用戶的OPEN_ID pd.put("MCH_ID", wpr.getMchId());//商戶號 pd.put("NONCE_STR", wpr.getNonceStr());//隨機字符串 pd.put("SIGN", wpr.getSign());//SIGN pd.put("CASH_FEE", wpr.getCashFee());//現金支付金額 pd.put("TOTAL_FEE", wpr.getTotalFee());//總金額 pd.put("BANK_TYPE", wpr.getBankType());//付款銀行 pd.put("TRADE_TYPE", wpr.getTradeType());//交易類型 pd.put("RESULT_CODE", wpr.getResultCode());//業務結果 pd.put("TRANSACTION_ID", wpr.getTotalFee());//微信支付訂單號 pd.put("TIME_END", wpr.getTimeEnd());//微信支付完成時間 wxPayService.saveWxOrder(pd); }else{ resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[報文爲空]]></return_msg>" + "</xml> "; } System.out.println("微信支付回調數據結束"); BufferedOutputStream out = new BufferedOutputStream( response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); }
解析微信通知xml
/** * description: 解析微信通知xml * * @param xml * @return * @author ex_yangxiaoyi * @see */ @SuppressWarnings({ "unused", "rawtypes", "unchecked" }) private static Map parseXmlToList2(String xml) { Map retMap = new HashMap(); try { StringReader read = new StringReader(xml); // 建立新的輸入源SAX 解析器將使用 InputSource 對象來肯定如何讀取 XML 輸入 InputSource source = new InputSource(read); // 建立一個新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 經過輸入源構造一個Document Document doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根節點 List<Element> es = root.getChildren(); if (es != null && es.size() != 0) { for (Element element : es) { retMap.put(element.getName(), element.getValue()); } } } catch (Exception e) { e.printStackTrace(); } return retMap; }