微信小程序支付之统一下单(任何框架拿过来直接用,讲的是效率)
本来以前的用的web-java-tool用起来是挺不错的,现成的,不知领导想到什么,要求不用框架重新写一次,用原生的servlet重写接口,也因此web-java-tool的代码很多就不能用
因此,有了这次重新写的经历,话不多说直接上代码
至此用的的东西不多
1.hutool工具包(我这里用的是5.5.2版本的)
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool-all.version}</version> </dependency>
2.直接就是代码了
package com.sjcf.common; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.MessageDigest; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.util.IdUtil; import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; /** * 微信小程序的封装 * * @author luoc * */ public class WchartUtil { /** * 获得token接口调用凭据(access_token) * * @return post */ public static String getToken() { // 拼接url StringBuilder url = new StringBuilder("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"); url.append("&appid=");// appid设置 url.append(Variables.APPID);// secret设置 url.append("&secret=");// code设置 url.append(Variables.APPSECRET); JSONObject result = new JSONObject(HttpUtil.get(url.toString())); System.out.println(result); return result.getStr("access_token"); } /** * 通过openid获取用户信息 * * @param openid * @return */ public static JSONObject getUserInfo(String openid) { StringBuilder url = new StringBuilder("https://api.weixin.qq.com/wxa/getpaidunionid?"); url.append("access_token=");// appid设置 url.append(WchartUtil.getToken());// secret设置 url.append("&openid=");// code设置 url.append(openid); JSONObject result = new JSONObject(HttpUtil.get(url.toString())); System.out.println(result.toString()); return result; } /** * 获取openid * * @param code * @return */ public static JSONObject getOpenid(String code) { StringBuilder url = new StringBuilder("https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code"); url.append("&appid=");// appid设置 url.append(Variables.APPID);// secret设置 url.append("&secret=");// code设置 url.append(Variables.APPSECRET); url.append("&js_code="); url.append(code); String result = HttpUtil.get(url.toString()); System.out.println(result.toString()); return new JSONObject(result); } /** * 生成32位md5码 * * @param key * @return */ public static String md5Password(String key) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; try { byte[] btInput = key.getBytes(); // 获得MD5摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的字节更新摘要 mdInst.update(btInput); // 获得密文 byte[] md = mdInst.digest(); // 把密文转换成十六进制的字符串形式 int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { return null; } } /** * 1老版微信统一下单 * * @throws Exception */ public static JSONObject createOrderOld(String openid, HttpServletRequest request) throws Exception { try { // 生成的随机字符串 String nonce_str = IdUtil.simpleUUID(); // 商品名称 String body = "测试商品名称"; // 获取本机的ip地址 String spbill_create_ip = ServletUtil.getClientIP(request); // 订单号 String orderNo = IdUtil.simpleUUID(); // 支付金额,单位:分,这边需要转成字符串类型,否则后面的签名会失败 String money = "1"; SortedMap<String, Object> packageParams = new TreeMap<String, Object>(); packageParams.put("appid", Variables.APPID); packageParams.put("mch_id", Variables.MCH_ID);//商户号 packageParams.put("nonce_str", nonce_str);// 生成的随机字符串 packageParams.put("body", body);// 商品名称 packageParams.put("out_trade_no", orderNo);// 商户订单号 packageParams.put("total_fee", money);// 支付金额,这边需要转成字符串类型,否则后面的签名会失败 packageParams.put("spbill_create_ip", spbill_create_ip); packageParams.put("notify_url", Variables.NOTIFY_URL); packageParams.put("trade_type", Variables.TRADETYPE); packageParams.put("openid", openid);//微信openid // 除去数组中的空值和签名参数 // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串 // MD5运算生成签名,这里是第一次签名,用于调用统一下单接口 String mysign = createSign(packageParams, Variables.MCH_KEY); System.out.println("===========第一次签名:" + mysign + "=============="); // 拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去 String xml = "<xml>" + "<appid>" + Variables.APPID + "</appid>" + "<body><![CDATA[" + body + "]]></body>" + "<mch_id>" + Variables.MCH_ID + "</mch_id>" + "<nonce_str>" + nonce_str + "</nonce_str>" + "<notify_url>" + Variables.NOTIFY_URL + "</notify_url>" + "<openid>" + openid + "</openid>" + "<out_trade_no>" + orderNo + "</out_trade_no>" + "<spbill_create_ip>" + spbill_create_ip+ "</spbill_create_ip>" + "<total_fee>" + money + "</total_fee>" + "<trade_type>"+ Variables.TRADETYPE + "</trade_type>" + "<sign>" + mysign + "</sign>" + "</xml>"; System.out.println("调试模式_统一下单接口 请求XML数据:" + xml); // 调用统一下单接口,并接受返回的结果 String result = HttpUtil.post(Variables.pay_url, xml); System.out.println(result); JSONObject map = new JSONObject(getMapFromXML(result)); //二次签名 packageParams = new TreeMap<String, Object>(); packageParams.put("appId", Variables.APPID); packageParams.put("nonceStr", map.getStr("nonce_str")); packageParams.put("package", "prepay_id="+map.getStr("prepay_id")); packageParams.put("signType", "MD5"); packageParams.put("timeStamp", DateUtil.date().toString()); packageParams.put("key", Variables.MCH_KEY); String paySign = createSign(packageParams, Variables.MCH_KEY); packageParams.put("paySign", paySign); JSONObject resultJson = new JSONObject(packageParams); return resultJson; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 1微信支付签名算法sign * * @param parameters * @param key 微信小程序商户 key * @return */ public static String createSign(SortedMap<String, Object> parameters, String key) { StringBuffer sb = new StringBuffer(); Set<Entry<String, Object>> es = parameters.entrySet();// 所有参与传参的参数按照accsii排序(升序) Iterator<Entry<String, Object>> it = es.iterator(); while (it.hasNext()) { Entry<String, Object> entry = it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } // 这里是商户那里设置的key sb.append("key=" + key); System.out.println("签名字符串:" + sb.toString()); // System.out.println("签名MD5未变大写:" + MD5Util.MD5Encode(sb.toString(), characterEncoding)); String sign = md5Password(sb.toString()).toUpperCase(); return sign; } /** * XML转换为map * @param strXML * @return Map * @throws Exception */ public static Map<String, Object> getMapFromXML(String strXML) throws Exception { try { Map<String, Object> data = new HashMap<String, Object>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setXIncludeAware(false); documentBuilderFactory.setExpandEntityReferences(false); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { ex.printStackTrace(); } return data; } catch (Exception ex) { throw ex; } } }
3.最后献上截图(1.微信开发工具 2.手机测验截图)
这已经就成功了,如果代码那里有问题的可以留言,我看到了一定回复的
不用重来才叫快;能够积累才叫多