微信扫码支付---模式一(PC端,解决中文乱码)

来源:互联网 发布:展板制作软件 编辑:程序博客网 时间:2024/04/29 18:35

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

首先登陆微信的公众平台(微信的服务号不是订阅号),然后选择微信支付–>开发设置,设置好支付回调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):

  /**        * 生成微信二维码图片        * @throws Exception        */      public void gainQRCode() throws Exception {              try {                  String orderNu = request.getParameter("orderNu");                  String describe = request.getParameter("describe");                  String payCodeType = request.getParameter("payCodeType");                 System.out.println("订单编号:"+"\n"+orderNu);                 String price = request.getParameter("txnAmt");                 if(StringUtils.isBlank(orderNu)){//账户充值,不用签名校验                     orderNu = "WE"+StringUtil.getTableId(false);                 } else {                     String sign = request.getParameter("sign");                     String signParam = "orderNu="+orderNu+"&payPrice="+price;                     String newSign = DigestUtils.md5Hex(signParam.getBytes("utf-8"));                     if(!newSign.equalsIgnoreCase(sign)){                         Map param = new HashMap<>();                         param.put("statu", "2");                         JSONArray jsonProduct = JSONArray.fromObject(param);                         System.out.println("json:  "+jsonProduct.toString());                         response.getWriter().print(jsonProduct.toString());                         return ;                     }                 }                 System.out.println("没有转换的金额:"+"\n"+price);                 BigDecimal bigDecimalPrice = new BigDecimal(price);                 String pric = bigDecimalPrice.multiply(new BigDecimal(100)).toString().split("\\.")[0];                 System.out.println("转换后的金额:"+"\n"+pric);                     String filePostfix = "jpg";                 // 二维码图片名称                 String codePng = System.currentTimeMillis() + "." + filePostfix;                 // 保存路径                 // 应用ID                 String appid = Config.APPID;                 // 商户ID                 String mch_id = Config.MCHID;                 String key = Config.KEY;                 // 生成32位的随机字符串                 String nonce_str = RandomStringUtil.generate().toUpperCase();                 // 商户产品ID                 User user = SessionUtil.getSysUserFormSession(request);                 String product_id = "";                 if(StringUtils.isNotBlank(payCodeType) && "1".equals(payCodeType)){//账户充值                     product_id = "1_"+orderNu+"_"+"账户保证金充值"+"_"+pric+"_"+user.getUniversalid();                 } else if (StringUtils.isNotBlank(payCodeType) && "2".equals(payCodeType)){//订单结算                     product_id = "2_"+orderNu+"_"+describe+"_"+pric+"_"+user.getUniversalid();                 }          //上面的可以不关注.关注下面的签名和发送的参数,54行开始                 // 时间戳                 String time_stamp = System.currentTimeMillis()+"";                 String sign = "";//这些参数可以去微信扫码的支付的文档看看具体是什么意思                 String temp = "appid=" + appid + "&mch_id=" + mch_id + "&nonce_str=" + nonce_str + "&" + "product_id="                         + product_id + "&time_stamp=" + time_stamp;                 String signTemp = temp + "&key=" + key;                 System.out.println("二维码请求参数:"+"\n"+temp);                 sign = Encrypt.e(signTemp).toUpperCase();                 String contentUrl = "weixin://wxpay/bizpayurl?" + temp + "&sign=" + sign;                 String url = QRCODE_PATH + File.separator  + File.separator + codePng;                 File f = new File(url);                 if(!f.exists()){                     f.mkdirs();                 }                  System.out.println("已生成二维码url");                 QRImgCode.encode(contentUrl, "二维码", 400, 400, url);                 System.out.println("二维码已经生成成功");                 Map param = new HashMap<>();                 param.put("statu", "1");                 param.put("imgName", codePng);                 System.out.println("返回参数封装成功");                 JSONArray jsonProduct = JSONArray.fromObject(param);                 System.out.println("json:  "+jsonProduct.toString());                 response.getWriter().print(jsonProduct.toString());             } catch (Exception e) {                 throw e;             }         }

生成二维码(2):

  /**       * 无中间图片       * @param content       * @param width       * @param height       * @param destImagePath       */      public static void encode(String content,String name,int width, int height, String destImagePath) {          try {             System.err.println("进入二维码方法");             BitMatrix bitMatrix = genBarcode(content, 190, 184);             System.out.println("生成二维码数据");             MatrixToImageWriter.writeToFile(bitMatrix, "jpg", new File(destImagePath));             System.out.println("二维码图片生成完成");         } catch (IOException e) {             e.printStackTrace();         } catch (WriterException e) {             e.printStackTrace();         }     }

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

  /**       * 微信扫码之后的回调       * @return       */      public void weixinPay() {           System.out.println("微信支付扫码回调,手动发起统一下单支付");          try {            InputStream inStream = this.request.getInputStream();            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();           byte[] buffer = new byte[1024];           int len = 0;           while ((len = inStream.read(buffer)) != -1) {             outSteam.write(buffer, 0, len);           }           outSteam.close();           inStream.close();           String re = new String(outSteam.toByteArray(), "utf-8");           System.out.println("用户扫码后返回的参数: \n" + re);       //转xml 微信传输的是xml          Map map = parseXML(re);       //扫码后签名验证           String sign = gainValidateSign(map,false).toUpperCase();           if (!sign.equalsIgnoreCase((String)map.get("sign"))) {             System.out.println("扫码后生成的签名不正确");             return;           }//这些是扫码后的验证           System.out.println("扫码后生成的签名正确,  继续进行后续操作");           String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";           String string = (String)map.get("product_id");           System.out.println("扫码的product_id参数:\n" + string);           String[] para = string.split("_");           //这里个方法是生成与支付订单的请求参数,看下面的方法,需要传输的参数可以去查看微信的帮助文档,虽然烂,但是这些还是有的.           StringEntity param = genProductArgs(para[0] + "_" + para[1], para[2], para[3]);           String result = sendPost(url, param);           System.out.println("发送请求得到结果===" + result);           Map resultMap = parseXML(result);           String signResult = gainSign(resultMap);           if (!signResult.equalsIgnoreCase((String)resultMap.get("sign"))) {             System.out.println("统一下单接口-->签名不正确");             return;           }           if (StringUtils.isNotBlank(para[0]) && "1".equals(para[0])) {                   RechargePrice r = rechargePriceServiceService.findByorderIdRecharge(para[1]);                   if(r == null || r.getUniversalid() == null){                       RechargePrice rp = new RechargePrice();                       rp.setUserId(para[4]);                       rp.setCreateDate(DateFormatUtil.strToDate(UtilDate.getDateFormatter()));                       rp.setExplain("账户充值");                       rp.setFlowAccountNum(StringUtil.getTableId(true));                       BigDecimal bigDecimalPrice = new BigDecimal(para[3]);                       double doubleValue = bigDecimalPrice.divide(new BigDecimal("100")).doubleValue();                       rp.setPrice(doubleValue+"");                       rp.setOrderCode(para[1]);                       rp.setTransactionStat("2");                       rp.setTransactionType("1");                       rp.setPaySource("5");                       this.rechargePriceServiceService.saveRechargePrice(rp);                   }               }           ServletOutputStream outputStream = this.response.getOutputStream();           String return_code = (String)resultMap.get("return_code");           String result_code = (String)resultMap.get("result_code");           if (StringUtils.isNotBlank(return_code) && StringUtils.isNotBlank(result_code) && return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")) {             String xml = genPay(resultMap);//对于中文乱码主要是上面的那个预支付订单请求,只要上面请求OK,这个请求就可以不用转码             System.out.println("统一下单接口后,向微信发送支付其你去的xml"+"\n" + xml);             outputStream.write(xml.getBytes());             outputStream.flush();             outputStream.close();           }         } catch (Exception e) {           e.printStackTrace();         }     }

字符串转xml:

  /**       * 解析xml方法       */      @SuppressWarnings("rawtypes")      public Map<String, String> parseXML(String xml) {          Document doc;          Map<String, String> map = new LinkedHashMap<String, String>();          try {              doc = DocumentHelper.parseText(xml);             Element rootElement = doc.getRootElement();             Iterator elementIterator = rootElement.elementIterator();             while (elementIterator.hasNext()) {                 Element recordEle = (Element) elementIterator.next();                 String name = recordEle.getName();                 String textTrim = recordEle.getTextTrim();                 map.put(name, textTrim);             }         } catch (DocumentException e) {             e.printStackTrace();         }         return map;     }

处理xml集合,返回签名:

  /**       * 处理xml字符串 返回签名       */      public String gainValidateSign(Map<String, String> map, boolean isUtf8)  {          StringBuffer sb = new StringBuffer();          Set<String> keySet = map.keySet();          List<String> list = new LinkedList<String>();          list.addAll(keySet);          Collections.sort(list);         for (String key : list) {             if (!key.equals("sign")) {                 sb.append(key).append("=").append(map.get(key)).append("&");             }         }         sb.append("key=").append(Config.KEY);         String sign = "";         if(isUtf8){             try {                 sign = Encrypt.e(new String(sb.toString().getBytes(), "utf-8"));             } catch (UnsupportedEncodingException e) {                 e.printStackTrace();             }         } else {             sign = Encrypt.e(sb.toString());         }         return sign;     }

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

  /**       * 把一个参数添加到 一个集合中,按字典顺序,这是为了后面生成 签名方便       * @param attach        * @param map        *        * @return       * @throws Exception       */      private StringEntity genProductArgs(String attach,String body,String price) throws Exception {         List<NameValuePair> packageParams = new LinkedList<NameValuePair>();         packageParams.add(new BasicNameValuePair("appid", Config.APPID));         packageParams.add(new BasicNameValuePair("attach", attach));         packageParams.add(new BasicNameValuePair("body", body));         packageParams.add(new BasicNameValuePair("mch_id", Config.MCHID));         packageParams.add(new BasicNameValuePair("nonce_str", RandomStringUtil.generate().toUpperCase()));         packageParams.add(new BasicNameValuePair("notify_url", ""));         packageParams.add(new BasicNameValuePair("out_trade_no", com.eryansky.common.utils.StringUtils.getRandomNumbersAndLetters(15).toUpperCase()));         packageParams.add(new BasicNameValuePair("spbill_create_ip", request.getRemoteAddr()));         packageParams.add(new BasicNameValuePair("total_fee", Integer.parseInt(price)+""));         packageParams.add(new BasicNameValuePair("trade_type", Config.trade_type));         // 调用genXml()方法获得xml格式的请求数据 //        String genXml = null;         try {             StringEntity stringEntityXml = getStringEntityXml(packageParams);             return stringEntityXml;         } catch (Exception e) {             e.printStackTrace();         }         return null;     }

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

  /**       * 生成xml文档发送微信生成支付信息       *        * @param params       * @return       * @throws Exception       */     private StringEntity getStringEntityXml(List<NameValuePair> params) throws Exception {          StringBuilder sb = new StringBuilder();         StringBuilder sb2 = new StringBuilder();         sb2.append("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><xml>");         for (int i = 0; i < params.size(); i++) {             // sb是用来计算签名的             sb.append(params.get(i).getName());             sb.append('=');             sb.append(params.get(i).getValue());             sb.append('&');             // sb2是用来做请求的xml参数             sb2.append("<" + params.get(i).getName() + ">");             sb2.append(params.get(i).getValue());             sb2.append("</" + params.get(i).getName() + ">");         }         sb.append("key=");         sb.append(Config.KEY);         System.err.println("生成签名的参数:"+"\n"+sb.toString());         String packageSign = null;         // 生成签名-->签名和xml字符串都需要转成utf-8格式,中文就不会出现乱码         packageSign = DigestUtils.md5Hex(sb.toString().getBytes("utf-8")).toUpperCase(); //        packageSign = DigestUtils.md5Hex(sb.toString()).toUpperCase();         System.out.println("生成发送统一接口的签名:"+"\n"+packageSign);         sb2.append("<sign><![CDATA[");         sb2.append(packageSign);         sb2.append("]]></sign>");         sb2.append("</xml>");         System.err.println("生成发送统一接口的xml"+"\n"+sb2.toString());         try {             StringEntity se = new StringEntity(sb2.toString(), "utf-8");             return se;         } catch (Exception e) {             e.printStackTrace();         }         return null;     }

发送请求:

  /**       * 发送请求       * @throws UnsupportedEncodingException        */      public String sendPost(String url, StringEntity param) throws UnsupportedEncodingException {          String reslt = "";          try {              CloseableHttpClient httpClient = HttpClients.createDefault();              RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(30000).build();             HttpPost httpPost = new HttpPost(url);             httpPost.addHeader("Content-Type", "text/xml");             httpPost.setEntity(param);             httpPost.setConfig(requestConfig);             HttpResponse response = httpClient.execute(httpPost);             HttpEntity entity = response.getEntity();             reslt = EntityUtils.toString(entity, "UTF-8");        } catch (ParseException | IOException e1) {             e1.printStackTrace();         }         **//下面注释掉的直接忽略掉,贴上来主要想说,以前用这种方法中文乱码,而且用utf-8转换一下后直接报签名错误,把签名信息贴到微信的签名验证上面确实可以通过,真的是无语** //        System.out.println("进入发送统一下单支付方法"); //        PrintWriter out = null; //        BufferedReader in = null; // //        String result = ""; //        try { //          URL realUrl = new URL(url); // //          URLConnection conn = realUrl.openConnection(); ////          conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // //          conn.setDoOutput(true); //          conn.setDoInput(true);// //          out = new PrintWriter(conn.getOutputStream()); //          System.out.println("请求参数========" + param); ////          param = new String(param.getBytes(), "utf-8"); // //          out.write(param); // //          out.flush(); // //          in = new BufferedReader(new InputStreamReader(conn.getInputStream())); //          String line; //          while ((line = in.readLine()) != null) //          { //            result = result + line; //          } //        } catch (Exception e) { //          e.printStackTrace(); //          try //          { //            if (out != null) { //              out.close(); //            }//            if (in != null) //              in.close(); //          } //          catch (IOException ex) { //            ex.printStackTrace(); //          } //        } //        finally //        { //          try //          { //            if (out != null) { //              out.close(); //            }//            if (in != null) //              in.close(); //          } //          catch (IOException ex) { //            ex.printStackTrace(); //          } //        } //        return new String(result.getBytes(), "utf-8");         return reslt;     }

拼接微信支付信息参数:

  /**       * 发送支付信息参数       * @param param       * @return       */      public String genPay(Map<String, String> param) {          System.out.println("向微信发送支付请求的 参数-->"+"\n"+param.toString());          List<NameValuePair> packageParams = new LinkedList<NameValuePair>();          packageParams.add(new BasicNameValuePair("appid", param.get("appid")));         packageParams.add(new BasicNameValuePair("mch_id", param.get("mch_id")));         packageParams.add(new BasicNameValuePair("nonce_str", RandomStringUtil.generate().toUpperCase()));         packageParams.add(new BasicNameValuePair("prepay_id", param.get("prepay_id")));         packageParams.add(new BasicNameValuePair("result_code", param.get("result_code")));         packageParams.add(new BasicNameValuePair("return_code", param.get("return_code")));         if ("FAIL".equalsIgnoreCase(param.get("result_code"))) {             packageParams.add(new BasicNameValuePair("return_code", param.get("result_code")));             if(StringUtils.isBlank(param.get("err_code_des"))){                 packageParams.add(new BasicNameValuePair("err_code_des", "订单时效"));             }             packageParams.add(new BasicNameValuePair("err_code_des", param.get("err_code_des")));         }         String genXml = null;         try {             System.out.println("向微信发送支付请求的xml"+"\n"+packageParams.toString());             genXml = genXml(packageParams);         } catch (Exception e) {             e.printStackTrace();         }         return genXml;     }

生成微信支付xml参数:

  /**       * 生成xml文档发送个给微信支付       *        * @param params       * @return       * @throws Exception       */      private String genXml(List<NameValuePair> params) throws Exception {          StringBuilder sb = new StringBuilder();         StringBuilder sb2 = new StringBuilder();         sb2.append("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><xml>");         for (int i = 0; i < params.size(); i++) {             // sb是用来计算签名的             sb.append(params.get(i).getName());             sb.append('=');             sb.append(params.get(i).getValue());             sb.append('&');             // sb2是用来做请求的xml参数             sb2.append("<" + params.get(i).getName() + ">");             sb2.append(params.get(i).getValue());             sb2.append("</" + params.get(i).getName() + ">");         }         sb.append("key=");         sb.append(Config.KEY);         System.err.println("生成签名的参数:"+"\n"+sb.toString());         String packageSign = null;         // 生成签名         packageSign = DigestUtils.md5Hex(sb.toString()).toUpperCase();         System.out.println("生成发送统一接口的签名:"+"\n"+packageSign);         sb2.append("<sign><![CDATA[");        sb2.append(packageSign);         sb2.append("]]></sign>");         sb2.append("</xml>");         System.err.println("生成发送统一接口的xml"+"\n"+sb2.toString());         try {             return sb2.toString();         } catch (Exception e) {             e.printStackTrace();         }         return "";     }

支付成功后:

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

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

本博客是根据:

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

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

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

0 0
原创粉丝点击