近期公司调完银联,调支付宝,调完支付宝调微信.说实话微信的帮助文档确实是烂,而且有没有技术支持,害的我头发都掉了一桌.不说废话了,看代码.

首先登陆微信的公众平台(微信的服务号不是订阅号),然后选择微信支付-->开发设置,设置好支付回调URL和支付授权目录(授权目录最少精确到二级目录,比如你的需要使用微信支付的目录是:www.weixinpay.com/sp/weixin/pay.do,那么对应的是:www.weixinpay.com/sp/weixin/),设置好后编写代码.

对了,联调支付,一般都需要外网能够访问的URL地址,这里建议使用过ngrok软件,直接在本地联调,使用方式,下载一个ngrok,然后由命令行窗口进入ngrok解压的目录,然后执行:

ngrok.exe -config ngrok.cfg -subdomain weixinpay(可以改成自己喜欢的域名) 8080(可以改成自己喜欢的端口)

首先生成微信二维码(1):

 1 /**
 2       * 生成微信二维码图片
 3       * @throws Exception
 4       */
 5     public void gainQRCode() throws Exception {
 6             try {
 7                 String orderNu = request.getParameter("orderNu");
 8                 String describe = request.getParameter("describe");
 9                 String payCodeType = request.getParameter("payCodeType");
10                 System.out.println("订单编号:"+"\n"+orderNu);
11                 String price = request.getParameter("txnAmt");
12                 if(StringUtils.isBlank(orderNu)){//账户充值,不用签名校验
13                     orderNu = "WE"+StringUtil.getTableId(false);
14                 } else {
15                     String sign = request.getParameter("sign");
16                     String signParam = "orderNu="+orderNu+"&payPrice="+price;
17                     String newSign = DigestUtils.md5Hex(signParam.getBytes("utf-8"));
18                     if(!newSign.equalsIgnoreCase(sign)){
19                         Map param = new HashMap<>();
20                         param.put("statu", "2");
21                         JSONArray jsonProduct = JSONArray.fromObject(param);
22                         System.out.println("json:  "+jsonProduct.toString());
23                         response.getWriter().print(jsonProduct.toString());
24                         return ;
25                     }
26                 }
27                 System.out.println("没有转换的金额:"+"\n"+price);
28                 BigDecimal bigDecimalPrice = new BigDecimal(price);
29                 String pric = bigDecimalPrice.multiply(new BigDecimal(100)).toString().split("\\.")[0];
30                 System.out.println("转换后的金额:"+"\n"+pric);    
31                 String filePostfix = "jpg";
32                 // 二维码图片名称
33                 String codePng = System.currentTimeMillis() + "." + filePostfix;
34                 // 保存路径
35                 // 应用ID
36                 String appid = Config.APPID;
37                 // 商户ID
38                 String mch_id = Config.MCHID;
39                 String key = Config.KEY;
40                 // 生成32位的随机字符串
41                 String nonce_str = RandomStringUtil.generate().toUpperCase();
42                 // 商户产品ID
43                 User user = SessionUtil.getSysUserFormSession(request);
44                 String product_id = "";
45                 if(StringUtils.isNotBlank(payCodeType) && "1".equals(payCodeType)){//账户充值
46                     product_id = "1_"+orderNu+"_"+"账户保证金充值"+"_"+pric+"_"+user.getUniversalid();
47                 } else if (StringUtils.isNotBlank(payCodeType) && "2".equals(payCodeType)){//订单结算
48                     product_id = "2_"+orderNu+"_"+describe+"_"+pric+"_"+user.getUniversalid();
49                 }
50          //上面的可以不关注.关注下面的签名和发送的参数,54行开始
51                 // 时间戳
52                 String time_stamp = System.currentTimeMillis()+"";
53                 String sign = "";//这些参数可以去微信扫码的支付的文档看看具体是什么意思
54                 String temp = "appid=" + appid + "&mch_id=" + mch_id + "&nonce_str=" + nonce_str + "&" + "product_id="
55                         + product_id + "&time_stamp=" + time_stamp;
56                 String signTemp = temp + "&key=" + key;
57                 System.out.println("二维码请求参数:"+"\n"+temp);
58                 sign = Encrypt.e(signTemp).toUpperCase();
59                 String contentUrl = "weixin://wxpay/bizpayurl?" + temp + "&sign=" + sign;
60                 String url = QRCODE_PATH + File.separator  + File.separator + codePng;
61                 File f = new File(url);
62                 if(!f.exists()){
63                     f.mkdirs();
64                 } 
65                 System.out.println("已生成二维码url");
66                 QRImgCode.encode(contentUrl, "二维码", 400, 400, url);
67                 System.out.println("二维码已经生成成功");
68                 Map param = new HashMap<>();
69                 param.put("statu", "1");
70                 param.put("imgName", codePng);
71                 System.out.println("返回参数封装成功");
72                 JSONArray jsonProduct = JSONArray.fromObject(param);
73                 System.out.println("json:  "+jsonProduct.toString());
74                 response.getWriter().print(jsonProduct.toString());
75             } catch (Exception e) {
76                 throw e;
77             }
78         }

生成二维码(2):

 1 /**
 2      * 无中间图片
 3      * @param content
 4      * @param width
 5      * @param height
 6      * @param destImagePath
 7      */
 8     public static void encode(String content,String name,int width, int height, String destImagePath) {
 9         try {
10             System.err.println("进入二维码方法");
11             BitMatrix bitMatrix = genBarcode(content, 190, 184);
12             System.out.println("生成二维码数据");
13             MatrixToImageWriter.writeToFile(bitMatrix, "jpg", new File(destImagePath));
14             System.out.println("二维码图片生成完成");
15         } catch (IOException e) {
16             e.printStackTrace();
17         } catch (WriterException e) {
18             e.printStackTrace();
19         }
20     }

PC端扫码后微信会去调用调用支付回调URL,接下来重要的步骤来了:(险些让我秃顶)

 1 /**
 2      * 微信扫码之后的回调
 3      * @return
 4      */
 5     public void weixinPay() { 
 6         System.out.println("微信支付扫码回调,手动发起统一下单支付");
 7         try {
 8           InputStream inStream = this.request.getInputStream();
 9           ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
10           byte[] buffer = new byte[1024];
11           int len = 0;
12           while ((len = inStream.read(buffer)) != -1) {
13             outSteam.write(buffer, 0, len);
14           }
15           outSteam.close();
16           inStream.close();
17           String re = new String(outSteam.toByteArray(), "utf-8");
18           System.out.println("用户扫码后返回的参数: \n" + re);
19       //转xml 微信传输的是xml
20           Map map = parseXML(re);
21       //扫码后签名验证
22           String sign = gainValidateSign(map,false).toUpperCase();
23           if (!sign.equalsIgnoreCase((String)map.get("sign"))) {
24             System.out.println("扫码后生成的签名不正确");
25             return;
26           }//这些是扫码后的验证
27           System.out.println("扫码后生成的签名正确,  继续进行后续操作");
28 
29           String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
30 
31           String string = (String)map.get("product_id");
32           System.out.println("扫码的product_id参数:\n" + string);
33           String[] para = string.split("_");
34           //这里个方法是生成与支付订单的请求参数,看下面的方法,需要传输的参数可以去查看微信的帮助文档,虽然烂,但是这些还是有的.
35           StringEntity param = genProductArgs(para[0] + "_" + para[1], para[2], para[3]);
36           String result = sendPost(url, param);
37           System.out.println("发送请求得到结果===" + result);
38 
39           Map resultMap = parseXML(result);
40           String signResult = gainSign(resultMap);
41           if (!signResult.equalsIgnoreCase((String)resultMap.get("sign"))) {
42             System.out.println("统一下单接口-->签名不正确");
43             return;
44           }
45           if (StringUtils.isNotBlank(para[0]) && "1".equals(para[0])) {
46                   RechargePrice r = rechargePriceServiceService.findByorderIdRecharge(para[1]);
47                   if(r == null || r.getUniversalid() == null){
48                       RechargePrice rp = new RechargePrice();
49                       rp.setUserId(para[4]);
50                       rp.setCreateDate(DateFormatUtil.strToDate(UtilDate.getDateFormatter()));
51                       rp.setExplain("账户充值");
52                       rp.setFlowAccountNum(StringUtil.getTableId(true));
53                       BigDecimal bigDecimalPrice = new BigDecimal(para[3]);
54                       
55                       double doubleValue = bigDecimalPrice.divide(new BigDecimal("100")).doubleValue();
56                       rp.setPrice(doubleValue+"");
57                       rp.setOrderCode(para[1]);
58                       rp.setTransactionStat("2");
59                       rp.setTransactionType("1");
60                       rp.setPaySource("5");
61                       this.rechargePriceServiceService.saveRechargePrice(rp);
62                   }
63               }
64           ServletOutputStream outputStream = this.response.getOutputStream();
65 
66           String return_code = (String)resultMap.get("return_code");
67           String result_code = (String)resultMap.get("result_code");
68           if (StringUtils.isNotBlank(return_code) && StringUtils.isNotBlank(result_code) && return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")) {
69             String xml = genPay(resultMap);//对于中文乱码主要是上面的那个预支付订单请求,只要上面请求OK,这个请求就可以不用转码
70             System.out.println("统一下单接口后,向微信发送支付其你去的xml"+"\n" + xml);
71             outputStream.write(xml.getBytes());
72             outputStream.flush();
73             outputStream.close();
74           }
75         } catch (Exception e) {
76           e.printStackTrace();
77         }
78     }

字符串转xml:

 1 /**
 2      * 解析xml方法
 3      */
 4     @SuppressWarnings("rawtypes")
 5     public Map<String, String> parseXML(String xml) {
 6         Document doc;
 7         Map<String, String> map = new LinkedHashMap<String, String>();
 8         try {
 9             doc = DocumentHelper.parseText(xml);
10             Element rootElement = doc.getRootElement();
11             Iterator elementIterator = rootElement.elementIterator();
12             while (elementIterator.hasNext()) {
13                 Element recordEle = (Element) elementIterator.next();
14                 String name = recordEle.getName();
15                 String textTrim = recordEle.getTextTrim();
16                 map.put(name, textTrim);
17             }
18         } catch (DocumentException e) {
19             e.printStackTrace();
20         }
21         return map;
22     }
处理xml集合,返回签名:
 1 /**
 2      * 处理xml字符串 返回签名
 3      */
 4     public String gainValidateSign(Map<String, String> map, boolean isUtf8)  {
 5         StringBuffer sb = new StringBuffer();
 6         Set<String> keySet = map.keySet();
 7         List<String> list = new LinkedList<String>();
 8         list.addAll(keySet);
 9         Collections.sort(list);
10         for (String key : list) {
11             if (!key.equals("sign")) {
12                 sb.append(key).append("=").append(map.get(key)).append("&");
13             }
14         }
15         sb.append("key=").append(Config.KEY);
16         String sign = "";
17         if(isUtf8){
18             try {
19                 sign = Encrypt.e(new String(sb.toString().getBytes(), "utf-8"));
20             } catch (UnsupportedEncodingException e) {
21                 e.printStackTrace();
22             }
23         } else {
24             sign = Encrypt.e(sb.toString());
25         }
26         return sign;
27     }

封装请求参数返回xml字符串:

 1 /**
 2      * 把一个参数添加到 一个集合中,按字典顺序,这是为了后面生成 签名方便
 3      * @param attach 
 4      * @param map 
 5      * 
 6      * @return
 7      * @throws Exception
 8      */
 9     private StringEntity genProductArgs(String attach,String body,String price) throws Exception {
10         List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
11         packageParams.add(new BasicNameValuePair("appid", Config.APPID));
12         packageParams.add(new BasicNameValuePair("attach", attach));
13         packageParams.add(new BasicNameValuePair("body", body));
14         packageParams.add(new BasicNameValuePair("mch_id", Config.MCHID));
15         packageParams.add(new BasicNameValuePair("nonce_str", RandomStringUtil.generate().toUpperCase()));
16         packageParams.add(new BasicNameValuePair("notify_url", ""));
17         packageParams.add(new BasicNameValuePair("out_trade_no", com.eryansky.common.utils.StringUtils.getRandomNumbersAndLetters(15).toUpperCase()));
18         packageParams.add(new BasicNameValuePair("spbill_create_ip", request.getRemoteAddr()));
19         packageParams.add(new BasicNameValuePair("total_fee", Integer.parseInt(price)+""));
20         packageParams.add(new BasicNameValuePair("trade_type", Config.trade_type));
21         // 调用genXml()方法获得xml格式的请求数据
22 //        String genXml = null;
23         try {
24             StringEntity stringEntityXml = getStringEntityXml(packageParams);
25             return stringEntityXml;
26         } catch (Exception e) {
27             e.printStackTrace();
28         }
29         return null;
30     }

返回发送的xml数据:(解决中文乱码问题)

 1 /**
 2      * 生成xml文档发送微信生成支付信息
 3      * 
 4      * @param params
 5      * @return
 6      * @throws Exception
 7      */
 8     private StringEntity getStringEntityXml(List<NameValuePair> params) throws Exception {
 9         StringBuilder sb = new StringBuilder();
10         StringBuilder sb2 = new StringBuilder();
11         sb2.append("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><xml>");
12         for (int i = 0; i < params.size(); i++) {
13             // sb是用来计算签名的
14             sb.append(params.get(i).getName());
15             sb.append('=');
16             sb.append(params.get(i).getValue());
17             sb.append('&');
18             // sb2是用来做请求的xml参数
19             sb2.append("<" + params.get(i).getName() + ">");
20             sb2.append(params.get(i).getValue());
21             sb2.append("</" + params.get(i).getName() + ">");
22         }
23         sb.append("key=");
24         sb.append(Config.KEY);
25         System.err.println("生成签名的参数:"+"\n"+sb.toString());
26         String packageSign = null;
27         // 生成签名-->签名和xml字符串都需要转成utf-8格式,中文就不会出现乱码
28         packageSign = DigestUtils.md5Hex(sb.toString().getBytes("utf-8")).toUpperCase();
29 //        packageSign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
30         System.out.println("生成发送统一接口的签名:"+"\n"+packageSign);
31         sb2.append("<sign><![CDATA[");
32         sb2.append(packageSign);
33         sb2.append("]]></sign>");
34         sb2.append("</xml>");
35         System.err.println("生成发送统一接口的xml"+"\n"+sb2.toString());
36         try {
37             StringEntity se = new StringEntity(sb2.toString(), "utf-8");
38             return se;
39         } catch (Exception e) {
40             e.printStackTrace();
41         }
42         return null;
43     }

发送请求:

 1 /**
 2      * 发送请求
 3      * @throws UnsupportedEncodingException 
 4      */
 5     public String sendPost(String url, StringEntity param) throws UnsupportedEncodingException {
 6         String reslt = "";
 7         try {
 8             CloseableHttpClient httpClient = HttpClients.createDefault();
 9             RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(30000).build();
10             HttpPost httpPost = new HttpPost(url);
11             httpPost.addHeader("Content-Type", "text/xml");
12             httpPost.setEntity(param);
13             httpPost.setConfig(requestConfig);
14             HttpResponse response = httpClient.execute(httpPost);
15             HttpEntity entity = response.getEntity();
16             reslt = EntityUtils.toString(entity, "UTF-8");
17         } catch (ParseException | IOException e1) {
18             e1.printStackTrace();
19         }
20         
21         //下面注释掉的直接忽略掉,贴上来主要想说,以前用这种方法中文乱码,而且用utf-8转换一下后直接报签名错误,把签名信息贴到微信的签名验证上面确实可以通过,真的是无语
22 //        System.out.println("进入发送统一下单支付方法");
23 //        PrintWriter out = null;
24 //        BufferedReader in = null;
25 //
26 //        String result = "";
27 //        try {
28 //          URL realUrl = new URL(url);
29 //
30 //          URLConnection conn = realUrl.openConnection();
31 //
32 //          conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
33 //
34 //          conn.setDoOutput(true);
35 //          conn.setDoInput(true);
36 //
37 //          out = new PrintWriter(conn.getOutputStream());
38 //          System.out.println("请求参数========" + param);
39 ////          param = new String(param.getBytes(), "utf-8");
40 //
41 //          out.write(param);
42 //
43 //          out.flush();
44 //
45 //          in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
46 //          String line;
47 //          while ((line = in.readLine()) != null)
48 //          {
49 //            result = result + line;
50 //          }
51 //        } catch (Exception e) {
52 //          e.printStackTrace();
53 //          try
54 //          {
55 //            if (out != null) {
56 //              out.close();
57 //            }
58 //            if (in != null)
59 //              in.close();
60 //          }
61 //          catch (IOException ex) {
62 //            ex.printStackTrace();
63 //          }
64 //        }
65 //        finally
66 //        {
67 //          try
68 //          {
69 //            if (out != null) {
70 //              out.close();
71 //            }
72 //            if (in != null)
73 //              in.close();
74 //          }
75 //          catch (IOException ex) {
76 //            ex.printStackTrace();
77 //          }
78 //        }
79 //        return new String(result.getBytes(), "utf-8");
80         return reslt;
81     }

拼接微信支付信息参数:

 1 /**
 2      * 发送支付信息参数
 3      * @param param
 4      * @return
 5      */
 6     public String genPay(Map<String, String> param) {
 7         System.out.println("向微信发送支付请求的 参数-->"+"\n"+param.toString());
 8         List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
 9         packageParams.add(new BasicNameValuePair("appid", param.get("appid")));
10         packageParams.add(new BasicNameValuePair("mch_id", param.get("mch_id")));
11         packageParams.add(new BasicNameValuePair("nonce_str", RandomStringUtil.generate().toUpperCase()));
12         packageParams.add(new BasicNameValuePair("prepay_id", param.get("prepay_id")));
13         packageParams.add(new BasicNameValuePair("result_code", param.get("result_code")));
14         packageParams.add(new BasicNameValuePair("return_code", param.get("return_code")));
15         if ("FAIL".equalsIgnoreCase(param.get("result_code"))) {
16             packageParams.add(new BasicNameValuePair("return_code", param.get("result_code")));
17             if(StringUtils.isBlank(param.get("err_code_des"))){
18                 packageParams.add(new BasicNameValuePair("err_code_des", "订单时效"));
19             }
20             packageParams.add(new BasicNameValuePair("err_code_des", param.get("err_code_des")));
21         }
22         String genXml = null;
23         try {
24             System.out.println("向微信发送支付请求的xml"+"\n"+packageParams.toString());
25             genXml = genXml(packageParams);
26         } catch (Exception e) {
27             e.printStackTrace();
28         }
29         return genXml;
30     }

生成微信支付xml参数:

 1 /**
 2      * 生成xml文档发送个给微信支付
 3      * 
 4      * @param params
 5      * @return
 6      * @throws Exception
 7      */
 8     private String genXml(List<NameValuePair> params) throws Exception {
 9         StringBuilder sb = new StringBuilder();
10         StringBuilder sb2 = new StringBuilder();
11         sb2.append("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><xml>");
12         for (int i = 0; i < params.size(); i++) {
13             // sb是用来计算签名的
14             sb.append(params.get(i).getName());
15             sb.append('=');
16             sb.append(params.get(i).getValue());
17             sb.append('&');
18             // sb2是用来做请求的xml参数
19             sb2.append("<" + params.get(i).getName() + ">");
20             sb2.append(params.get(i).getValue());
21             sb2.append("</" + params.get(i).getName() + ">");
22         }
23         sb.append("key=");
24         sb.append(Config.KEY);
25         System.err.println("生成签名的参数:"+"\n"+sb.toString());
26         String packageSign = null;
27         // 生成签名
28         packageSign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
29         System.out.println("生成发送统一接口的签名:"+"\n"+packageSign);
30         sb2.append("<sign><![CDATA[");
31         sb2.append(packageSign);
32         sb2.append("]]></sign>");
33         sb2.append("</xml>");
34         System.err.println("生成发送统一接口的xml"+"\n"+sb2.toString());
35         try {
36             return sb2.toString();
37         } catch (Exception e) {
38             e.printStackTrace();
39         }
40         return "";
41     }

支付成功后:

 1 /**
 2         * 微信扫码支付成功后的回调的接口
 3        * @throws IOException 
 4        */
 5       public void weiXinnotify() throws IOException{
 6               InputStream inStream = this.request.getInputStream();
 7              ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
 8             byte[] buffer = new byte[1024];
 9             int len = 0;
10            while ((len = inStream.read(buffer)) != -1) {
11                outSteam.write(buffer, 0, len);
12              }
13              outSteam.close();
14              inStream.close();
15             String re = new String(outSteam.toByteArray(), "utf-8");
16             System.out.println("用户扫码后支付返回的参数: \n" + re);
17             Map parseXML = parseXML(re);
18              System.out.println("转成xml后的map:\n" + parseXML);
19              String string = (String)parseXML.get("attach");
20              //处理完逻辑后记得向微信发送消息,不然微信会隔一段时间会访问
21              PrintWriter writer = response.getWriter();
22              writer.print(re);
23              writer.close();
24       }

 

最后,对于微信的帮助文档,是我目前见过最烂的了,前面联调支付宝和银联都没这样.哎不吐槽了,

本博客是根据:

http://www.cnblogs.com/zyw-205520/p/5495115.html这篇博客,写的很好,他自己写了一个开源项目,

以及和IT好的帮助下完成的,

如果有好的博客可以推荐给我,大家共同学习,谢谢!!

 

 posted on 2016-08-19 14:55  _萨瓦迪卡  阅读(6923)  评论(0编辑  收藏  举报