思路:
①将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 传给 调起微信支付功能
注:一些用到的方法
1、MD5 将map 拼接成键值对的串,然后进行加密
1 /** 2 * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 3 */ 4 public String createSign(SortedMap<String, String> packageParams) { 5 StringBuffer sb = new StringBuffer(); 6 Set es = packageParams.entrySet(); 7 Iterator it = es.iterator(); 8 while (it.hasNext()) { 9 Map.Entry entry = (Map.Entry) it.next(); 10 String k = (String) entry.getKey(); 11 String v = (String) entry.getValue(); 12 if (null != v && !"".equals(v) && !"sign".equals(k) 13 && !"key".equals(k)) { 14 sb.append(k + "=" + v + "&"); 15 } 16 } 17 sb.append("key=" + this.getKey()); 18 String sign = MD5Util.MD5Encode(sb.toString(), this.charset) 19 .toUpperCase(); 20 return sign; 21 22 }
2、将xml交给 "https://api.mch.weixin.qq.com/pay/unifiedorder" 解析生成 预支付订单
如果参数提交失败,会在try里的 if 中 返回 开发的时候建议打断点,我是存session ,然后返回错误信息msg
1 /** 2 *description:获取预支付id 3 *@param urls 4 *@param xmlParam 5 *@return 6 * @author ex_yangxiaoyi 7 * @see 8 */ 9 public static String getPayNo(String url,String xmlParam){ 10 Subject currentUser = SecurityUtils.getSubject(); 11 Session session = currentUser.getSession(); 12 13 DefaultHttpClient client = new DefaultHttpClient(); 14 client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true); 15 HttpPost httpost= HttpClientConnectionManager.getPostMethod(url); 16 String prepay_id = ""; 17 try { 18 httpost.setEntity(new StringEntity(xmlParam, "UTF-8")); 19 HttpResponse response = httpclient.execute(httpost); 20 String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); 21 if(jsonStr.indexOf("FAIL")!=-1){ 22 session.setAttribute("error_msg", jsonStr); 23 return prepay_id; 24 } 25 Map map = doXMLParse(jsonStr); 26 prepay_id = (String) map.get("prepay_id"); 27 } catch (Exception e) { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 } 31 return prepay_id; 32 } 33
1 public static InputStream String2Inputstream(String str) { 2 return new ByteArrayInputStream(str.getBytes()); 3 }
3、xml解析
1 /** 2 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 3 * @param strxml 4 * @return 5 * @throws JDOMException 6 * @throws IOException 7 */ 8 public static Map doXMLParse(String strxml) throws Exception { 9 if(null == strxml || "".equals(strxml)) { 10 return null; 11 } 12 13 Map m = new HashMap(); 14 InputStream in = String2Inputstream(strxml); 15 SAXBuilder builder = new SAXBuilder(); 16 Document doc = builder.build(in); 17 Element root = doc.getRootElement(); 18 List list = root.getChildren(); 19 Iterator it = list.iterator(); 20 while(it.hasNext()) { 21 Element e = (Element) it.next(); 22 String k = e.getName(); 23 String v = ""; 24 List children = e.getChildren(); 25 if(children.isEmpty()) { 26 v = e.getTextNormalize(); 27 } else { 28 v = getChildrenText(children); 29 } 30 31 m.put(k, v); 32 } 33 34 //关闭流 35 in.close(); 36 37 return m; 38 }
1 /** 2 * 获取子结点的xml 3 * @param children 4 * @return String 5 */ 6 public static String getChildrenText(List children) { 7 StringBuffer sb = new StringBuffer(); 8 if(!children.isEmpty()) { 9 Iterator it = children.iterator(); 10 while(it.hasNext()) { 11 Element e = (Element) it.next(); 12 String name = e.getName(); 13 String value = e.getTextNormalize(); 14 List list = e.getChildren(); 15 sb.append("<" + name + ">"); 16 if(!list.isEmpty()) { 17 sb.append(getChildrenText(list)); 18 } 19 sb.append(value); 20 sb.append("</" + name + ">"); 21 } 22 } 23 24 return sb.toString(); 25 }
4、微信回调函数
1 /** 2 * 微信支付 回调函数 3 */ 4 @RequestMapping("/notify") 5 @ResponseBody 6 protected void notify(HttpServletRequest request, 7 HttpServletResponse response) throws Exception { 8 9 PageData pd=new PageData(); 10 logBefore(logger, "微信支付 回调函数"); 11 //把如下代码贴到的你的处理回调的servlet 或者.do 中即可明白回调操作 12 logger.info("微信支付回调数据开始"); 13 //示例报文 14 //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>"; 15 String inputLine; 16 String notityXml = ""; 17 String resXml = ""; 18 19 try { 20 while ((inputLine = request.getReader().readLine()) != null) { 21 notityXml += inputLine; 22 } 23 request.getReader().close(); 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 28 System.out.println("接收到的报文:" + notityXml); 29 30 BufferedWriter writer = new BufferedWriter(new FileWriter(new File("c:\\ResultXml.txt"),true)); 31 // 32 writer.write(DateUtil.getTime()+notityXml+"\r\n"); 33 writer.close(); 34 35 Map m = parseXmlToList2(notityXml); 36 WxPayResult wpr = new WxPayResult(); 37 wpr.setAppid(m.get("appid").toString()); 38 wpr.setBankType(m.get("bank_type").toString()); 39 wpr.setCashFee(m.get("cash_fee").toString()); 40 wpr.setFeeType(m.get("fee_type").toString()); 41 wpr.setIsSubscribe(m.get("is_subscribe").toString()); 42 wpr.setMchId(m.get("mch_id").toString()); 43 wpr.setNonceStr(m.get("nonce_str").toString()); 44 wpr.setOpenid(m.get("openid").toString()); 45 wpr.setOutTradeNo(m.get("out_trade_no").toString()); 46 wpr.setResultCode(m.get("result_code").toString()); 47 wpr.setReturnCode(m.get("return_code").toString()); 48 wpr.setSign(m.get("sign").toString()); 49 wpr.setTimeEnd(m.get("time_end").toString()); 50 wpr.setTotalFee(m.get("total_fee").toString()); 51 wpr.setTradeType(m.get("trade_type").toString()); 52 wpr.setTransactionId(m.get("transaction_id").toString()); 53 54 if("SUCCESS".equals(wpr.getResultCode())){ 55 //支付成功 56 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" 57 + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; 58 //修改订单状态 59 pd.put("WX_ORDER", wpr.getOutTradeNo()); 60 wxPayService.editStatusByOutId(pd); 61 //保存订单信息 62 pd.put("WX_ORDER_ID", UuidUtil.get32UUID());//微信订单主键ID 63 pd.put("OPEN_ID", wpr.getOpenid());//用户的OPEN_ID 64 pd.put("MCH_ID", wpr.getMchId());//商户号 65 pd.put("NONCE_STR", wpr.getNonceStr());//随机字符串 66 pd.put("SIGN", wpr.getSign());//SIGN 67 pd.put("CASH_FEE", wpr.getCashFee());//现金支付金额 68 pd.put("TOTAL_FEE", wpr.getTotalFee());//总金额 69 pd.put("BANK_TYPE", wpr.getBankType());//付款银行 70 pd.put("TRADE_TYPE", wpr.getTradeType());//交易类型 71 pd.put("RESULT_CODE", wpr.getResultCode());//业务结果 72 pd.put("TRANSACTION_ID", wpr.getTotalFee());//微信支付订单号 73 pd.put("TIME_END", wpr.getTimeEnd());//微信支付完成时间 74 wxPayService.saveWxOrder(pd); 75 }else{ 76 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 77 + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; 78 } 79 80 System.out.println("微信支付回调数据结束"); 81 82 BufferedOutputStream out = new BufferedOutputStream( 83 response.getOutputStream()); 84 out.write(resXml.getBytes()); 85 out.flush(); 86 out.close(); 87 88 } 89
解析微信通知xml
1 /** 2 * description: 解析微信通知xml 3 * 4 * @param xml 5 * @return 6 * @author ex_yangxiaoyi 7 * @see 8 */ 9 @SuppressWarnings({ "unused", "rawtypes", "unchecked" }) 10 private static Map parseXmlToList2(String xml) { 11 Map retMap = new HashMap(); 12 try { 13 StringReader read = new StringReader(xml); 14 // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 15 InputSource source = new InputSource(read); 16 // 创建一个新的SAXBuilder 17 SAXBuilder sb = new SAXBuilder(); 18 // 通过输入源构造一个Document 19 Document doc = (Document) sb.build(source); 20 Element root = doc.getRootElement();// 指向根节点 21 List<Element> es = root.getChildren(); 22 if (es != null && es.size() != 0) { 23 for (Element element : es) { 24 retMap.put(element.getName(), element.getValue()); 25 } 26 } 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } 30 return retMap; 31 }