微信扫码支付---模式一(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好的帮助下完成的,
如果有好的博客可以推荐给我,大家共同学习,谢谢!!
- 微信扫码支付---模式一(PC端,解决中文乱码)
- PHP在PC端实现微信扫码支付模式二
- 微信扫码支付模式一
- 微信扫码支付--模式一
- 微信扫码支付 模式一
- RadLinux9控制台模式中文乱码解决
- ubuntu文本模式/终端中文乱码解决
- ubuntu文本模式/终端中文乱码解决
- Spring中中文乱码解决一例
- 中文乱码问题的解决一
- Git笔记(一) Git中文乱码解决
- pc端支付宝扫码支付
- 微信扫码支付 模式一 (JSAPI)
- pc(电脑)用java通过adb操作android手机发短信及中文乱码解决
- ubuntu命令模式中文乱码解决+投影仪使用
- Ubuntu命令行模式下中文字符乱码的解决
- 在文本模式下中文乱码简单解决
- ubuntu tty1(文本模式/终端)中文乱码解决
- PHP实现双向链表、栈
- python基础教程学习笔记二
- 判断字符串数组能否首尾相连
- QT 连接 sql server数据库 完整演示
- 解决git error Key has already been taken
- 微信扫码支付---模式一(PC端,解决中文乱码)
- eclipse-maven更新代码后找不到类,报错
- httpCilent请求网络 及 StreamUtils 工具类转换
- static的研究
- java中的static关键字分析
- protobuf-c的使用(一)构建
- c语言要修改或者读取、更改txt的话,真的需要好好看下代码
- mongoDB 入门指南、示例
- python基础教程学习笔记三