微信二維碼支付-模式一(PC端,解決中文亂碼問題)


近期公司調完銀聯,調支付寶,調完支付寶調微信.說實話微信的幫助文檔確實是爛,而且有沒有技術支持,害的我頭發都掉了一桌.不說廢話了,看代碼.

首先登陸微信的公眾平台(微信的服務號不是訂閱號),然后選擇微信支付-->開發設置,設置好支付回調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好的幫助下完成的,

如果有好的博客可以推薦給我,大家共同學習,謝謝!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM