是在一个子项目完成的,
依赖:
org.springframework.boot spring-boot-starter-web org.apache.httpcomponents httpclient 4.5.3 org.slf4j slf4j-api 1.7.21 org.slf4j slf4j-simple 1.7.21 com.google.zxing core 3.1.0 com.google.zxing javase 3.1.0 org.springframework.boot spring-boot-starter-thymeleaf
下面是工具类:
WXPayConstants工具类
package com.sdk.pay.wechat; import org.apache.http.client.HttpClient; public class WXPayConstants { //商户密钥 public static final String APP_KEY = "sbNCm1JnevqI36LrEaxFwcaT0hkGxFnC"; //商户id public static final String MCH_ID = "1497984412"; //APPid public static final String APP_ID = "wx632c8f211f8122c6"; public static final String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //回调地址要写自己的 28k6010n77.wicp.vip这个是做了内网穿透 public static final String NOTIFY_URL = "28k6010n77.wicp.vip/wxcallback.html"; public enum SignType { MD5, HMACSHA256 } public static final String DOMAIN_API = "api.mch.weixin.qq.com"; public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com"; public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com"; public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com"; public static final String FAIL = "FAIL"; public static final String SUCCESS = "SUCCESS"; public static final String HMACSHA256 = "HMAC-SHA256"; public static final String MD5 = "MD5"; public static final String FIELD_SIGN = "sign"; public static final String FIELD_SIGN_TYPE = "sign_type"; public static final String WXPAYSDK_VERSION = "WXPaySDK/3.0.9"; public static final String USER_AGENT = WXPAYSDK_VERSION + " (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") + ") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion(); public static final String MICROPAY_URL_SUFFIX = "/pay/micropay"; public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder"; public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery"; public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse"; public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder"; public static final String REFUND_URL_SUFFIX = "/secapi/pay/refund"; public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery"; public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill"; public static final String REPORT_URL_SUFFIX = "/payitil/report"; public static final String SHORTURL_URL_SUFFIX = "/tools/shorturl"; public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid"; // sandbox public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay"; public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder"; public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery"; public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse"; public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder"; public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund"; public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery"; public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill"; public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report"; public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl"; public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid"; }
WXPayUtil工具类
package com.sdk.pay.wechat; import com.sdk.pay.wechat.WXPayConstants.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.StringWriter; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.*; public class WXPayUtil { private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final Random RANDOM = new SecureRandom(); /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */ public static MapxmlToMap(String strXML) throws Exception { try { Map data = new HashMap (); DocumentBuilder documentBuilder = WXPayXmlUtil.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) { // do nothing } return data; } catch (Exception ex) { WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML); throw ex; } } /** * 将Map转换为XML格式的字符串 * * @param data Map类型数据 * @return XML格式的字符串 * @throws Exception */ public static String mapToXml(Map data) throws Exception { org.w3c.dom.Document document = WXPayXmlUtil.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key: data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { } return output; } /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @return 含有sign字段的XML */ public static String generateSignedXml(final Map data, String key) throws Exception { return generateSignedXml(data, key, SignType.MD5); } /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名类型 * @return 含有sign字段的XML */ public static String generateSignedXml(final Map data, String key, SignType signType) throws Exception { String sign = generateSignature(data, key, signType); data.put(WXPayConstants.FIELD_SIGN, sign); return mapToXml(data); } /** * 判断签名是否正确 * * @param xmlStr XML格式数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(String xmlStr, String key) throws Exception { Map data = xmlToMap(xmlStr); if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) { return false; } String sign = data.get(WXPayConstants.FIELD_SIGN); return generateSignature(data, key).equals(sign); } /** * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。 * * @param data Map类型数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(Map data, String key) throws Exception { return isSignatureValid(data, key, SignType.MD5); } /** * 判断签名是否正确,必须包含sign字段,否则返回false。 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名方式 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(Map data, String key, SignType signType) throws Exception { if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) { return false; } String sign = data.get(WXPayConstants.FIELD_SIGN); return generateSignature(data, key, signType).equals(sign); } /** * 生成签名 * * @param data 待签名数据 * @param key API密钥 * @return 签名 */ public static String generateSignature(final Map data, String key) throws Exception { return generateSignature(data, key, SignType.MD5); } /** * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 * * @param data 待签名数据 * @param key API密钥 * @param signType 签名方式 * @return 签名 */ public static String generateSignature(final Map data, String key, SignType signType) throws Exception { Set keySet = data.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (k.equals(WXPayConstants.FIELD_SIGN)) { continue; } if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名 sb.append(k).append("=").append(data.get(k).trim()).append("&"); } sb.append("key=").append(key); if (SignType.MD5.equals(signType)) { return MD5(sb.toString()).toUpperCase(); } else if (SignType.HMACSHA256.equals(signType)) { return HMACSHA256(sb.toString(), key); } else { throw new Exception(String.format("Invalid sign_type: %s", signType)); } } /** * 获取随机字符串 Nonce Str * * @return String 随机字符串 */ public static String generateNonceStr() { char[] nonceChars = new char[32]; for (int index = 0; index < nonceChars.length; ++index) { nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); } return new String(nonceChars); } /** * 生成 MD5 * * @param data 待处理数据 * @return MD5结果 */ public static String MD5(String data) throws Exception { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] array = md.digest(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 生成 HMACSHA256 * @param data 待处理数据 * @param key 密钥 * @return 加密结果 * @throws Exception */ public static String HMACSHA256(String data, String key) throws Exception { Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); sha256_HMAC.init(secret_key); byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 日志 * @return */ public static Logger getLogger() { Logger logger = LoggerFactory.getLogger("wxpay java sdk"); return logger; } /** * 获取当前时间戳,单位秒 * @return */ public static long getCurrentTimestamp() { return System.currentTimeMillis()/1000; } /** * 获取当前时间戳,单位毫秒 * @return */ public static long getCurrentTimestampMs() { return System.currentTimeMillis(); } }
WXPayXmlUtil工具类
package com.sdk.pay.wechat; import org.w3c.dom.Document; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; public final class WXPayXmlUtil { public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); documentBuilderFactory.setXIncludeAware(false); documentBuilderFactory.setExpandEntityReferences(false); return documentBuilderFactory.newDocumentBuilder(); } public static Document newDocument() throws ParserConfigurationException { return newDocumentBuilder().newDocument(); } }
HttpsHandler工具类
package com.sdk.pay.util; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; public class HttpsHandler { public static void trustAllHttpsCertificates() throws Exception { TrustManager[] tm_array=new TrustManager[1]; TrustManager tm=new myTrustManager(); tm_array[0]=tm; SSLContext sc=SSLContext.getInstance("SSL"); sc.init(null, tm_array, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } public static class myTrustManager implements TrustManager, X509TrustManager { public X509Certificate[] getAcceptedIssuers() { return null; } public boolean isServerTrusted(X509Certificate[] certs) { return true; } public boolean isClientTrusted(X509Certificate[] certs) { return true; } public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } } }
HTTPUtil工具类
package com.sdk.pay.util; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class HTTPUtil { public static String httpPost(String urlStr, String params, String charSet) { HttpURLConnection httpConn = null; try { byte[] data = params.getBytes(charSet); URL url = new URL(urlStr); httpConn = (HttpURLConnection) url.openConnection(); httpConn.setRequestMethod("POST"); httpConn.setRequestProperty("ContentType", "application/x-www-form-urlencoded"); httpConn.setRequestProperty("Content-Length", String.valueOf(data.length)); httpConn.setDoInput(true); httpConn.setDoOutput(true); // System.setProperty("sun.net.client.defaultConnectTimeout", "30000");//jdk1.4换成这个,连接超时 // System.setProperty("sun.net.client.defaultReadTimeout", "30000"); //jdk1.4换成这个,读操作超时 httpConn.setConnectTimeout(30000);// jdk 1.5换成这个,连接超时 httpConn.setReadTimeout(30000);// jdk 1.5换成这个,读操作超时 httpConn.connect(); OutputStream os1 = httpConn.getOutputStream(); os1.write(data); os1.flush(); os1.close(); return getResponseResult(httpConn, urlStr, charSet); } catch (Exception ex) { if (ex instanceof java.net.ConnectException || ex instanceof java.net.SocketTimeoutException) { NetUtil.clearDNSCache(); } } finally { if (null != httpConn) { httpConn.disconnect(); } } return null; } public static String httpsPost(String urlStr, String params, String charSet) { HttpsURLConnection httpsConn = null; try { byte[] data = params.getBytes(charSet); URL url = new URL(urlStr); HttpsHandler.trustAllHttpsCertificates(); HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String arg0, SSLSession arg1) { return true; } }; HttpsURLConnection.setDefaultHostnameVerifier(hv); httpsConn = (HttpsURLConnection) url.openConnection(); httpsConn.setRequestMethod("POST"); httpsConn.setRequestProperty("ContentType", "application/x-www-form-urlencoded"); httpsConn.setRequestProperty("Content-Length", String.valueOf(data.length)); httpsConn.setDoInput(true); httpsConn.setDoOutput(true); httpsConn.setConnectTimeout(30000);// jdk 1.5换成这个,连接超时 httpsConn.setReadTimeout(30000);// jdk 1.5换成这个,读操作超时 httpsConn.connect(); OutputStream os1 = httpsConn.getOutputStream(); os1.write(data); os1.flush(); os1.close(); return getResponseResult(httpsConn, urlStr, charSet); } catch (Exception ex) { if (ex instanceof java.net.ConnectException || ex instanceof java.net.SocketTimeoutException) { NetUtil.clearDNSCache(); } } finally { if (null != httpsConn) { httpsConn.disconnect(); } } return null; } public static String httpPostJson(String urlStr, String params, String charSet) { HttpURLConnection httpConn = null; try { httpConn = (HttpURLConnection) ((new URL(urlStr).openConnection())); httpConn.setDoOutput(true); httpConn.setRequestProperty("Content-Type", "application/json"); httpConn.setRequestProperty("Accept", "application/json"); httpConn.setRequestMethod("POST"); httpConn.connect(); byte[] outputBytes = params.getBytes(charSet); OutputStream os = httpConn.getOutputStream(); os.write(outputBytes); os.close(); return getResponseResult(httpConn, urlStr, charSet); } catch (Exception e) { e.printStackTrace(); } finally { if (null != httpConn) { httpConn.disconnect(); } } return null; } /** * 发送json字符 * * @param urlStr * @param params * @param charSet * @param header * @param headerVal * @return */ public static String httpPostJson(String urlStr, String params, String charSet, String header, String headerVal) { HttpURLConnection httpConn = null; try { httpConn = (HttpURLConnection) ((new URL(urlStr).openConnection())); httpConn.setDoOutput(true); httpConn.setRequestProperty("Content-Type", "application/json"); httpConn.setRequestProperty("Accept", "application/json"); if (header != null && header.trim().length() > 0) { httpConn.setRequestProperty(header, headerVal); } httpConn.setRequestMethod("POST"); httpConn.connect(); byte[] outputBytes = params.getBytes(charSet); OutputStream os = httpConn.getOutputStream(); os.write(outputBytes); os.close(); return getResponseResult(httpConn, urlStr, charSet); } catch (Exception e) { e.printStackTrace(); } finally { if (null != httpConn) { httpConn.disconnect(); } } return null; } // /** // * post过去的参数分成不同的域发送过去 // * @param urlStr // * @param params // * @param charSet // * @return // */ // public static String httpPost(String urlStr, Mapparams, String charSet) { // HttpURLConnection httpConn=null; // try { // String paramstring= ParameterUtil.mapToUrlString(params); // URL url=new URL(urlStr); // httpConn=(HttpURLConnection)url.openConnection(); // httpConn.setRequestMethod("POST"); // httpConn.setRequestProperty("ContentType", "application/x-www-form-urlencoded"); // httpConn.setRequestProperty("Content-Length", String.valueOf(paramstring.length())); // httpConn.setDoInput(true); // httpConn.setDoOutput(true); // // System.setProperty("sun.net.client.defaultConnectTimeout", "30000");//jdk1.4换成这个,连接超时 // // System.setProperty("sun.net.client.defaultReadTimeout", "30000"); //jdk1.4换成这个,读操作超时 // httpConn.setConnectTimeout(30000);// jdk 1.5换成这个,连接超时 // httpConn.setReadTimeout(30000);// jdk 1.5换成这个,读操作超时 // httpConn.connect(); // OutputStream os1=httpConn.getOutputStream(); // Iteratorit=params.keySet().iterator(); // int size=params.keySet().size(); // int i=1; // while(it.hasNext()){ // String andStr="&"; // if(i==size){ // andStr=""; // } // String key=it.next(); // String val=params.get(key); // byte[] data=(key+"="+URLEncoder.encode(val, "utf-8")+ andStr).getBytes(); // os1.write(data); // } // os1.flush(); // os1.close(); // return getResponseResult(httpConn, urlStr, charSet); // } catch(Exception ex) { // if(ex instanceof java.net.ConnectException || ex instanceof java.net.SocketTimeoutException) { // com.sdk.pay.util.NetUtil.clearDNSCache(); // } // logger.error(urlStr, ex); // } finally { // if(null != httpConn) { // httpConn.disconnect(); // } // } // return null; // } public static String httpGet(String urlStr, String params, String charSet) { HttpURLConnection httpConn = null; try { if (null != params && params.length() > 0) { if (urlStr.indexOf("?") == -1) { urlStr += "?" + params; } else { urlStr += "&" + params; } } URL url = new URL(urlStr); httpConn = (HttpURLConnection) url.openConnection(); httpConn.setRequestMethod("GET"); httpConn.setRequestProperty("ContentType", "application/x-www-form-urlencoded"); httpConn.setDoInput(true); httpConn.setDoOutput(true); // System.setProperty("sun.net.client.defaultConnectTimeout", "30000");//jdk1.4换成这个,连接超时 // System.setProperty("sun.net.client.defaultReadTimeout", "30000"); //jdk1.4换成这个,读操作超时 httpConn.setConnectTimeout(30000);// jdk 1.5换成这个,连接超时 httpConn.setReadTimeout(30000);// jdk 1.5换成这个,读操作超时 httpConn.connect(); return getResponseResult(httpConn, urlStr, charSet); } catch (Exception ex) { if (ex instanceof java.net.ConnectException || ex instanceof java.net.SocketTimeoutException) { NetUtil.clearDNSCache(); } ex.printStackTrace(); } finally { if (null != httpConn) { httpConn.disconnect(); } } return null; } public static String httpsGet(String urlStr, String params, String charSet) { HttpsURLConnection httpsConn = null; try { if (null != params && params.length() > 0) { if (urlStr.indexOf("?") == -1) { urlStr += "?" + params; } else { urlStr += "&" + params; } } byte[] data = params.getBytes(charSet); URL url = new URL(urlStr); HttpsHandler.trustAllHttpsCertificates(); HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String arg0, SSLSession arg1) { return true; } }; HttpsURLConnection.setDefaultHostnameVerifier(hv); httpsConn = (HttpsURLConnection) url.openConnection(); httpsConn.setRequestMethod("GET"); httpsConn.setRequestProperty("ContentType", "application/x-www-form-urlencoded"); httpsConn.setRequestProperty("Content-Length", String.valueOf(data.length)); httpsConn.setDoInput(true); httpsConn.setDoOutput(true); httpsConn.setConnectTimeout(30000);// jdk 1.5换成这个,连接超时 httpsConn.setReadTimeout(30000);// jdk 1.5换成这个,读操作超时 httpsConn.connect(); OutputStream os1 = httpsConn.getOutputStream(); os1.write(data); os1.flush(); os1.close(); return getResponseResult(httpsConn, urlStr, charSet); } catch (Exception ex) { if (ex instanceof java.net.ConnectException || ex instanceof java.net.SocketTimeoutException) { NetUtil.clearDNSCache(); } ex.printStackTrace(); } finally { if (null != httpsConn) { httpsConn.disconnect(); } } return null; } private static String getResponseResult(HttpURLConnection httpConn, String urlStr, String charSet) throws IOException { String res = null; // 获得响应状态 int responseCode = httpConn.getResponseCode(); if (HttpURLConnection.HTTP_OK == responseCode) { byte[] buffer = new byte[1024]; int len = -1; InputStream is = httpConn.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); while ((len = is.read(buffer)) != -1) { bos.write(buffer, 0, len); } res = bos.toString(charSet); is.close(); bos.close(); System.out.println(urlStr + " Response Code:" + responseCode + " content:" + res); } else { System.out.println(urlStr + " Response Code:" + responseCode); } return res; } }
NetUtil工具类
package com.sdk.pay.util; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Map; public class NetUtil { @SuppressWarnings("unchecked") public static void clearDNSCache() { try { Class clazz=java.net.InetAddress.class; final Field cacheField=clazz.getDeclaredField("addressCache"); cacheField.setAccessible(true); final Object o=cacheField.get(clazz); Class clazz2=o.getClass(); // final Field cachePolicyField = clazz2.getDeclaredField("policy"); final Field cacheMapField=clazz2.getDeclaredField("cache"); cacheMapField.setAccessible(true); final Map cacheMap=(Map)cacheMapField.get(o); // cachePolicyField.setAccessible(true); AccessController.doPrivileged(new PrivilegedAction
QRCodeUtil工具类
package com.sdk.pay.util; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import java.awt.image.BufferedImage; import java.util.Hashtable; public class QRCodeUtil { private static final String CHARSET = "utf-8"; private static final String FORMAT_NAME = "JPG"; // 二维码尺寸 private static final int QRCODE_SIZE = 300; // LOGO宽度 private static final int WIDTH = 60; // LOGO高度 private static final int HEIGHT = 60; public static BufferedImage createImage(String content) throws Exception { Hashtablehints = new Hashtable (); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.CHARACTER_SET, CHARSET); hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints); int width = bitMatrix.getWidth(); int height = bitMatrix.getHeight(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); } } return image; } }
WchatController Controller层
package com.sdk.pay.controller; import com.sdk.pay.util.HTTPUtil; import com.sdk.pay.util.QRCodeUtil; import com.sdk.pay.wechat.WXPayConstants; import com.sdk.pay.wechat.WXPayUtil; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; @Controller public class WchatController { @RequestMapping("wechat.html") public String wechat(Integer amount, Model model) { if (amount != null) { Mapdata = new HashMap<>(); data.put("appid", WXPayConstants.APP_ID); data.put("mch_id", WXPayConstants.MCH_ID); //随机字符串 这里用时间戳代替 data.put("nonce_str", System.currentTimeMillis() + ""); //商品描述 data.put("body", "年货"); //商户订单号 商户系统内部订单号,要求32个字符内, // 只能是数字、大小写字母_-|* 且在同一个商户号下唯一。 // 这里用时间戳代替 data.put("out_trade_no", System.currentTimeMillis() + ""); //标价金额 订单总金额,单位为分,详见支付金额 data.put("total_fee", amount.toString()); //终端IP 支持IPV4和IPV6两种格式的IP地址。用户的客户端IP data.put("spbill_create_ip", "10.9.45.62");//从request中获取 //通知地址 //异步接收微信支付结果通知的回调地址, // 通知url必须为外网可访问的url,不能携带参数。自己测试需要做内网穿透 data.put("notify_url", WXPayConstants.NOTIFY_URL); //交易类型 NATIVE -Native支付 data.put("trade_type", "NATIVE "); try { //封装好的生成XML的工具类 把数据封装到map里面就可以生成了 String xml = WXPayUtil.generateSignedXml(data, WXPayConstants.APP_KEY); System.out.println(xml); String res = HTTPUtil.httpPost(WXPayConstants.URL, xml, "utf-8"); Map result = WXPayUtil.xmlToMap(res); model.addAttribute("code_url", result.get("code_url")); } catch (Exception e) { e.printStackTrace(); } } return "wechat"; } @RequestMapping("code.html") public void code(HttpServletResponse response, String code) { if (code == null) { return; } try { BufferedImage img = QRCodeUtil.createImage(code); ImageIO.write(img, "JPEG", response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); } } String OK = " "; String ERROR = " "; @RequestMapping("wxcallback.html") public void callback(HttpServletRequest request, HttpServletResponse response) { String line = null; try { BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); StringBuilder builder = new StringBuilder(); while ((line = reader.readLine()) != null) { builder.append(line); } String xml = builder.toString();//微信通知结果 System.out.println(xml); Map data = WXPayUtil.xmlToMap(xml); if ("SUCCESS".equals(data.get("return_code"))) {//成功 //校验签名 boolean check = WXPayUtil.isSignatureValid(xml, WXPayConstants.APP_KEY); if (check) {//签名正确 //更新订单 System.out.println("订单号:"+data.get("out_trade_no")); System.out.println("微信订单号:"+data.get("transaction_id")); System.out.println("收到金额:"+data.get("cash_fee")); System.out.println("支付状态:"+data.get("result_code")); response.getWriter().write(OK); } else {//签名错误 response.getWriter().write(ERROR); } } } catch (Exception e) { e.printStackTrace(); } try { response.getWriter().flush(); response.getWriter().close(); } catch (IOException e) { e.printStackTrace(); } } }
页面 chat.html
1 DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Titletitle> 6 head> 7 <body> 8 <form action="wechat.html" method="post"> 9 金额<input name="amount"/> 10 <input type="submit" value="提交"/> 11 form> 12 <img th:src="${'code.html?code='+code_url}"> 13 body> 14 html>