diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml index 363ba1b9..b9e9a487 100644 --- a/ruoyi-common/ruoyi-common-core/pom.xml +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -112,6 +112,17 @@ commons-beanutils commons-beanutils + + dom4j + dom4j + 1.4 + compile + + + org.apache.httpcomponents + httpclient + 4.3.5 + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/WXPayConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/WXPayConstants.java new file mode 100644 index 00000000..63141bb3 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/WXPayConstants.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.core.constant; + +import org.apache.http.client.HttpClient; + +/** + * 常量 + */ +public class WXPayConstants { + + 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"; + +} + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java index 9b9ecc51..a92dbd38 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java @@ -94,6 +94,15 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils return DateFormatUtils.format(now, "yyyy/MM/dd"); } + /** + * 日期路径 即年月日时分秒 如20180808010515 + */ + public static final String timePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, YYYYMMDDHHMMSS); + } + /** * 日期路径 即年/月/日 如20180808 */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/PayUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/PayUtils.java new file mode 100644 index 00000000..bd70b8a8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/PayUtils.java @@ -0,0 +1,280 @@ +package com.ruoyi.common.core.utils; + +import org.dom4j.DocumentHelper; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +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.math.BigDecimal; +import java.net.InetAddress; +import java.text.SimpleDateFormat; +import java.util.*; + +public class PayUtils { + /** + * 获取当前机器的ip + * + * @return String + */ + public static String getLocalIp(){ + InetAddress ia=null; + String localip = null; + try { + ia=ia.getLocalHost(); + localip=ia.getHostAddress(); + } catch (Exception e) { + e.printStackTrace(); + } + return localip; + + } + + /** + * Map转换为 Xml + * + * @param map + * @return Xml + * @throws Exception + */ + public static String mapToXml(Map map) throws Exception { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + //防止XXE攻击 + documentBuilderFactory.setXIncludeAware(false); + documentBuilderFactory.setExpandEntityReferences(false); + DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder(); + org.w3c.dom.Document document = documentBuilder.newDocument(); + org.w3c.dom.Element root = document.createElement("xml"); + document.appendChild(root); + for (String key: map.keySet()) { + String value = map.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(); + try { + writer.close(); + } + catch (Exception ex) { + } + return output; + } + + + /** + * 创建签名Sign + * + * @param key + * @param parameters + * @return + */ + public static String createSign(Map parameters,String key){ + StringBuffer sb = new StringBuffer(); + Set es = parameters.entrySet(); + Iterator it = es.iterator(); + while(it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + String k = (String)entry.getKey(); + if(entry.getValue() != null || !"".equals(entry.getValue())) { + String v = String.valueOf(entry.getValue()); + if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { + sb.append(k + "=" + v + "&"); + } + } + } + sb.append("key=" + key); + String sign = StringUtils.md5(sb.toString()).toUpperCase(); + return sign; + } + + + /** + * XML转换为Map + * + * @param strXML + * @return Map + * @throws Exception + */ + public static Map getMapFromXML(String strXML) throws Exception { + try { + Map data = new HashMap(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + //防止XXE攻击 + 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; + } + } + + /** + * 生成随机数 + * + * @return + */ + public static String makeUUID(int len) { + return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len); + } + + /** + * 获取当前的Timestamp + * + * @return + */ + public static String getCurrentTimeStamp() { + return Long.toString(System.currentTimeMillis()/1000); + } + + /** + * 获取当前的时间 + * @return + */ + public static long getCurrentTimestampMs() { + return System.currentTimeMillis(); + } + + /** + * 生成订单号 + * + * @return + */ + public static String generateOrderNo() { + SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd"); + return sdf.format(new Date())+makeUUID(16); + } + + /** + * 获取当前工程url + * + * @param request + * @return + */ + public static String getCurrentUrl(HttpServletRequest request){ + return request.getScheme() +"://" + request.getServerName() + ":" +request.getServerPort() +request.getContextPath(); + } + + /** + * Xml字符串转换为Map + * + * @param xmlStr + * @return + */ + public static Map xmlStrToMap(String xmlStr){ + + try { + Map map = new HashMap(); + org.dom4j.Document document = DocumentHelper.parseText(xmlStr); + org.dom4j.Element nodeElement = document.getRootElement(); + List node = nodeElement.elements(); + for (Iterator it = node.iterator(); it.hasNext();) { + org.dom4j.Element elm = (org.dom4j.Element) it.next(); + map.put(elm.getName(), elm.getText()); + elm = null; + } + return map; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + + /** + * 转换金额型到整型 + * @param money + * @return + */ + public static String moneyToIntegerStr(Double money){ + BigDecimal decimal = new BigDecimal(money); + int amount = decimal.multiply(new BigDecimal(100)) + .setScale(0, BigDecimal.ROUND_HALF_UP).intValue(); + return String.valueOf(amount); + } + + /** + * 除去数组中的空值和签名参数 + * @param sArray 签名参数组 + * @return 去掉空值与签名参数后的新签名参数组 + */ + public static Map paraFilter(Map sArray) { + + Map result = new HashMap(); + + if (sArray == null || sArray.size() <= 0) { + return result; + } + + for (String key : sArray.keySet()) { + String value = sArray.get(key); + if (value == null || value.equals("") || key.equalsIgnoreCase("sign") + || key.equalsIgnoreCase("sign_type")) { + continue; + } + result.put(key, value); + } + + return result; + } + + /** + * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 + * @param params 需要排序并参与字符拼接的参数组 + * @return 拼接后字符串 + */ + public static String createLinkString(Map params) { + List keys = new ArrayList(params.keySet()); + Collections.sort(keys); + String prestr = ""; + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); + if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符 + prestr = prestr + key + "=" + value; + } else { + prestr = prestr + key + "=" + value + "&"; + } + } + return prestr; + } +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java index 51c7734e..8848c38f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java @@ -4,6 +4,8 @@ import com.ruoyi.common.core.text.StrFormatter; import org.springframework.util.AntPathMatcher; import java.beans.PropertyDescriptor; +import java.math.BigInteger; +import java.security.MessageDigest; import java.util.Collection; import java.util.List; import java.util.Map; @@ -507,4 +509,21 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils } return false; } + + public static String md5(String str) { + try { + // 生成一个MD5加密计算摘要 + MessageDigest md = MessageDigest.getInstance("MD5"); + // 计算md5函数 + md.update(str.getBytes()); + // digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符 + // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值 + //一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方) + return new BigInteger(1, md.digest()).toString(16).toUpperCase(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + } \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/WXPayUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/WXPayUtil.java new file mode 100644 index 00000000..68ad4fad --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/WXPayUtil.java @@ -0,0 +1,298 @@ +package com.ruoyi.common.core.utils; + +import com.ruoyi.common.core.constant.WXPayConstants; +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 Map xmlToMap(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, WXPayConstants.SignType.MD5); + } + + /** + * 生成带有 sign 的 XML 格式字符串 + * + * @param data Map类型数据 + * @param key API密钥 + * @param signType 签名类型 + * @return 含有sign字段的XML + */ + public static String generateSignedXml(final Map data, String key, WXPayConstants.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, WXPayConstants.SignType.MD5); + } + + /** + * 判断签名是否正确,必须包含sign字段,否则返回false。 + * + * @param data Map类型数据 + * @param key API密钥 + * @param signType 签名方式 + * @return 签名是否正确 + * @throws Exception + */ + public static boolean isSignatureValid(Map data, String key, WXPayConstants.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, WXPayConstants.SignType.MD5); + } + + /** + * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 + * + * @param data 待签名数据 + * @param key API密钥 + * @param signType 签名方式 + * @return 签名 + */ + public static String generateSignature(final Map data, String key, WXPayConstants.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 (WXPayConstants.SignType.MD5.equals(signType)) { + return MD5(sb.toString()).toUpperCase(); + } else if (WXPayConstants.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(); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/WXPayXmlUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/WXPayXmlUtil.java new file mode 100644 index 00000000..75148fa1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/WXPayXmlUtil.java @@ -0,0 +1,31 @@ +package com.ruoyi.common.core.utils; + +import org.w3c.dom.Document; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +/** + * 2018/7/3 + */ +public final class WXPayXmlUtil { + public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException { + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); + 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(); + } +} diff --git a/xhpc-modules/pom.xml b/xhpc-modules/pom.xml index 5d3cfe79..79b11525 100644 --- a/xhpc-modules/pom.xml +++ b/xhpc-modules/pom.xml @@ -14,6 +14,7 @@ xhpc-power-pile xhpc-charging-station xhpc-user + xhpc-payment xhpc-modules diff --git a/xhpc-modules/xhpc-payment/.gitignore b/xhpc-modules/xhpc-payment/.gitignore new file mode 100644 index 00000000..549e00a2 --- /dev/null +++ b/xhpc-modules/xhpc-payment/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/xhpc-modules/xhpc-payment/.mvn/wrapper/MavenWrapperDownloader.java b/xhpc-modules/xhpc-payment/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..a45eb6ba --- /dev/null +++ b/xhpc-modules/xhpc-payment/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/xhpc-modules/xhpc-payment/.mvn/wrapper/maven-wrapper.jar b/xhpc-modules/xhpc-payment/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..2cc7d4a5 Binary files /dev/null and b/xhpc-modules/xhpc-payment/.mvn/wrapper/maven-wrapper.jar differ diff --git a/xhpc-modules/xhpc-payment/.mvn/wrapper/maven-wrapper.properties b/xhpc-modules/xhpc-payment/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..ffdc10e5 --- /dev/null +++ b/xhpc-modules/xhpc-payment/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/xhpc-modules/xhpc-payment/mvnw b/xhpc-modules/xhpc-payment/mvnw new file mode 100644 index 00000000..a16b5431 --- /dev/null +++ b/xhpc-modules/xhpc-payment/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/xhpc-modules/xhpc-payment/mvnw.cmd b/xhpc-modules/xhpc-payment/mvnw.cmd new file mode 100644 index 00000000..c8d43372 --- /dev/null +++ b/xhpc-modules/xhpc-payment/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/xhpc-modules/xhpc-payment/pom.xml b/xhpc-modules/xhpc-payment/pom.xml new file mode 100644 index 00000000..003d4a32 --- /dev/null +++ b/xhpc-modules/xhpc-payment/pom.xml @@ -0,0 +1,96 @@ + + + + com.ruoyi + xhpc-modules + 3.0.0 + + 4.0.0 + + xhpc-payment + + + 账号服务 + + + + 8 + 8 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-web + + + + + mysql + mysql-connector-java + + + + + com.ruoyi + ruoyi-common-datasource + + + + + com.ruoyi + ruoyi-common-datascope + + + + + com.alipay.sdk + alipay-sdk-java + ${alipay.sdk} + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + 2.4.0 + + + + repackage + + + + + + + diff --git a/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/PaymentApplication.java b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/PaymentApplication.java new file mode 100644 index 00000000..84c73b40 --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/PaymentApplication.java @@ -0,0 +1,21 @@ +package com.xhpc.payment; + +import com.ruoyi.common.security.annotation.EnableCustomConfig; +import com.ruoyi.common.security.annotation.EnableRyFeignClients; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/*@EnableCustomConfig*/ +@EnableRyFeignClients +@EnableFeignClients +@SpringBootApplication +@MapperScan("com.xhpc.payment.mapper") +public class PaymentApplication { + + public static void main(String[] args) { + SpringApplication.run(PaymentApplication.class, args); + } + +} diff --git a/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/controller/WxPaymentController.java b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/controller/WxPaymentController.java new file mode 100644 index 00000000..477137aa --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/controller/WxPaymentController.java @@ -0,0 +1,621 @@ +package com.xhpc.payment.controller; + +import com.ruoyi.common.core.constant.HttpStatus; +import com.ruoyi.common.core.utils.DateUtils; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.common.core.utils.WXPayUtil; +import com.ruoyi.common.core.web.domain.AjaxResult; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.net.ssl.SSLContext; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 微信支付 + * author:yuyang + * Date:2020/4/8 + * Time:16:30 + */ +@RestController +@RequestMapping("/wx") +@Api(value = "微信支付接口", tags = "微信支付接口") +public class WxPaymentController { + + private static final Logger logger = LoggerFactory.getLogger(WxPaymentController.class); + + private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + private static final Random RANDOM = new SecureRandom(); + + + @Autowired + private Environment environment; + + @GetMapping("/payment") + @ApiOperation(value = "微信支付", notes = "传入order") + public Object getWXunifiedorder(HttpServletRequest servletRequest) { + AjaxResult responseData = new AjaxResult(); + String openid = servletRequest.getParameter("openid"); + if (StringUtils.isNull(openid)) { + return responseData.error(HttpStatus.BAD_REQUEST, "openid不能为空"); + } + //总金额(是)订单总金额,单位为分 + String doubleValue = servletRequest.getParameter("doubleValue"); + if (StringUtils.isNull(doubleValue)) { + return responseData.error(HttpStatus.BAD_REQUEST, "充值金额不能为空"); + } + Double amount = Double.parseDouble(doubleValue); + String orderNumber = "wx"+ DateUtils.timePath(); + //此次生成充值订单 + // OrderVO orderInfo = orderService.selectOrderNumber(orderNumber); + if (0.0 == amount) { + return responseData.error(HttpStatus.BAD_REQUEST, "充值金额不能为0"); + } + //附加数据(否) + String attach = attachYu(StringUtils.valueOf("123456"), StringUtils.valueOf(amount), null, orderNumber); + //商品描述(是) + String body = "用户充值"; + //商户订单号(是) + String outTradeNo = orderNumber; + int Fee = amount.intValue(); + //终端ip(是) + String spbillCreateIp = getRemoteLoginUserIp(servletRequest); + //交易类型(是) + String tradeType = "JSAPI"; + Date date = new Date(); + //微信过期时间格式 + SimpleDateFormat format1 = new SimpleDateFormat("yyyyMMddHHmmss"); + //过期时间半个小时 + Date expireTime = new Date(date.getTime() + 5 * 60 * 1000); + String timeStr = format1.format(expireTime); + //发送请求 + PrintWriter out = null; + BufferedReader in = null; + String result = ""; + try { + URL realUrl = new URL(environment.getProperty("WXPAYUNIFIEDORDER")); + // 打开和URL之间的连接 + URLConnection conn = realUrl.openConnection(); + // 设置通用的请求属性 + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("Content-Type", "text/xml"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + // 发送POST请求必须设置如下两行 + conn.setDoOutput(true); + conn.setDoInput(true); + // 获取URLConnection对象对应的输出流 + out = new PrintWriter(conn.getOutputStream()); + out.print(createXMLParam(Fee, attach, tradeType, spbillCreateIp, outTradeNo, body, timeStr, environment.getProperty("SERVERDOMAIN"), environment.getProperty("APPID"), environment.getProperty("MCHID"), environment.getProperty("KEY"), openid)); + // flush输出流的缓冲 + out.flush(); + // 定义BufferedReader输入流来读取URL的响应 + in = new BufferedReader( + new InputStreamReader(conn.getInputStream())); + String line; + while ((line = in.readLine()) != null) { + result += line; + System.out.println(result); + } + } catch (Exception e) { + e.printStackTrace(); + } + //使用finally块来关闭输出流、输入流 + finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + //app + //Map sign = createSign(result, 1, orderInfo.getId(), environment.getProperty("KEY"), doubleValue, remarks); + Map sign = createSign(result, 1); + responseData.success(sign); + return responseData; + } + + + /** + * 生成签名 + * + * @param result xml格式字符串 + * @param pageType 页面类型 1-APP 2-PC + * @return + */ + public Map createSign(String result, Integer pageType) { + try { + Map map = WXPayUtil.xmlToMap(result); + String return_code = map.get("return_code"); + if ("FAIL".equals(return_code)) { + return null; + } else { + if (pageType == 1) { + //我这里是手动拼接的 可以使用微信具类生成签名 + String nonceStr = WXPayUtil.generateNonceStr(); + Map map1 = new HashMap<>(); + map1.put("money", map.get("doubleValue")); + map1.put("appid", map.get("appid")); + map1.put("prepayId", map.get("prepay_id")); + map1.put("noncestr", nonceStr); + map1.put("nonceStr", nonceStr + "\",\"packageValue\":" + "\"Sign=WXPay\""); + map1.put("package", "Sign=WXPay"); + map1.put("partnerid", map.get("mch_id")); + Long time = System.currentTimeMillis(); + String timeStr = time.toString().substring(0, time.toString().length() - 3); + map1.put("timestamp", timeStr); + map1.put("sign", map.get("sign")); + map1.put("url", map.get("code_url")); + map1.put("key", environment.getProperty("KEY")); + return map1; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 生成xml格式参数 + * 注释的是使用微信工具类生成的xml格式 添加参数时,按照abcd26个字母顺序添加 + * + * @param total_fee 支付金额 + * @param attach 自定义参数 + * @param trade_type 交易类型 + * @param ip 终端ip地址 + * @param out_trade_no 交易订单号 + * @param body 交易信息 + * @param timeExpire 交易过期时间 + * @param WXNotifyUrl 微信支付回调地址 + * @param appid 应用ID + * @param mch_id 商户号 + * @param key 添加key值 + * @return + */ + public String createXMLParam(Integer total_fee, String attach, String trade_type, String ip, String out_trade_no, String body, String timeExpire, String WXNotifyUrl, String appid, String mch_id, String key, String openid) { + String param = ""; + String nonce_str = generateNonceStr(); + String notify_url = WXNotifyUrl; + String spbill_create_ip = ip; + //应用ID + param += "appid=" + appid; + //附加数据 回调时可以获取 + param += "&attach=" + attach; + //body 商品描述 + param += "&body=" + body; + //商户号 + param += "&mch_id=" + mch_id; + //随机字符串 + param += "&nonce_str=" + nonce_str; + //回调地址 + param += "¬ify_url=" + notify_url; + if ("JSAPI".equals(trade_type)) { + //用户openid + param += "&openid=" + openid; + } + //商户订单号 + param += "&out_trade_no=" + out_trade_no; + //终端IP + param += "&spbill_create_ip=" + spbill_create_ip; + //交易结束时间 + param += "&time_expire=" + timeExpire; + //总金额 + param += "&total_fee=" + total_fee; + //支付类型 + param += "&trade_type=" + trade_type; + //生成签名 添加key值 + String stringSignTemp = param + "&key=" + key; + //签名 不参与签名 默认MD5算法 + String sign = StringUtils.md5(stringSignTemp); + + String xmlString = "\n" + + " " + appid + "\n" + + " " + attach + "\n" + + " " + body + "\n" + + " " + mch_id + "\n" + + " " + nonce_str + "\n" + + " " + notify_url + "\n"; + if ("JSAPI".equals(trade_type)) { + xmlString += " " + openid + "\n"; + } + xmlString += + " " + out_trade_no + "\n" + + " " + spbill_create_ip + "\n" + + " " + timeExpire + "\n" + + " " + total_fee + "\n" + + " " + trade_type + "\n" + + " " + sign + "\n" + + " "; + return xmlString; + } + + //附加参数 + public String attachYu(String id, String money, String payStyle, String out_trade_no) { + // 发送请求参数 业务参数 + StringBuilder attachSB = new StringBuilder(); + attachSB.append(",{\"customerId\":" + id); + attachSB.append(",\"money\":\"" + money + "\""); + attachSB.append(",\"out_trade_no\":\"" + out_trade_no + "\"}"); + String attach = attachSB.substring(1); + return attach; + } + + /** + * 获取随机字符串 Nonce Str + * + * @return String 随机字符串 + */ + public 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); + } + + //获取终端ip + public String getRemoteLoginUserIp(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + System.out.println("THE REAL IPS IS:" + ip); + if (ip != null && ip.length() > 15) { + if (ip.indexOf(",") > 0) { + ip = ip.substring(0, ip.indexOf(",")); + } + } + return ip; + } + + /** + * 修改支付订单 + * + * @param + * @param + * @return + * @throws Exception + */ + /* public int addOrUpdatePaymentRecord(Long orderId, String transactionId) throws Exception { + Order order = orderMapper.selectById(orderId); + String userId = StringUtils.valueOf(order.getUserId()); + User user = userMapper.getUserVo(userId); + order.setPrepayId(transactionId); + orderMapper.updateById(order); + return 0; + }*/ + public int paymentCallback(Map map) throws Exception { + String result_code = map.get("result_code"); + String return_code = map.get("return_code"); + String out_trade_no = map.get("out_trade_no"); + String openid = map.get("openid"); + String transaction_id = map.get("transaction_id"); + + int flag = 0; + //支付订单编号 + /*OrderVO order = orderMapper.selectOrderNumber(out_trade_no); + if (null != order) { + //检验是否需要再次回调刷新数据 + //TODO 微信后台回调,刷新订单支付状态等相关业务 + addOrUpdatePaymentRecord(order.getId(), transaction_id); + }*/ + return flag; + } + + /** + * 回调Api + */ + @RequestMapping("/paymentCallback") + public Object payNotify(HttpServletRequest request, HttpServletResponse response) { + try { + ServletInputStream in = null; + BufferedReader reader = null; + StringBuilder content = new StringBuilder(); + try { + in = request.getInputStream(); + reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); + String itemStr = "";// 作为输出字符串的临时串,用于判断是否读取完毕 + while (null != (itemStr = reader.readLine())) { + content.append(itemStr); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (null != reader) + reader.close(); + if (null != in) + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + content.toString(); + //将xml转换成map + Map map = WXPayUtil.xmlToMap(content.toString()); + String return_code = map.get("return_code"); + //自定义参数 为json格式的字符串 + //如果返回成功 + if ("SUCCESS".equals(return_code)) { + //将自定义参数转换成JSONObject对象,处理业务逻辑 + paymentCallback(map); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/xml; charset=utf-8"); + PrintWriter out = null; + try { + out = response.getWriter(); + out.print("\n" + + " \n" + + " \n" + + "\n"); + out.close(); + return null; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 生成xml格式 添加参数时,按照abcd26个字母顺序添加 + * + * @param tradeNo 微信单号 + * @param out_refund_no 退款单号 + * @param total_fee 总金额 + * @param refund_fee 退款金额 + * @param refund_desc 退款原因 + * @return + */ + private String createOutXMLParam(String tradeNo, String out_refund_no, String total_fee, String refund_fee, String refund_desc) { + HashMap map = new HashMap<>(); + //随机字符串 + String nonceStr = WXPayUtil.generateNonceStr(); + map.put("appid", environment.getProperty("APPID")); + //商户id + map.put("mch_id", environment.getProperty("MCHID")); + //随机字符串 + map.put("nonce_str", nonceStr); + //商户退款单号 一个订单唯一 + map.put("out_refund_no", tradeNo); + //退款原因 + map.put("refund_desc", refund_desc); + //退款金额 + map.put("refund_fee", refund_fee); + //订单金额 + map.put("total_fee", total_fee); + //微信订单号 + map.put("transaction_id", out_refund_no); + String xml = ""; + try { + String signature = WXPayUtil.generateSignature(map, environment.getProperty("KEY")); + map.put("sign", signature); + xml = WXPayUtil.mapToXml(map); + } catch (Exception e) { + e.printStackTrace(); + } + return xml; + } + + /** + * 加载证书 + * + * @param certPath 证书位置 + * @throws Exception + */ + private CloseableHttpClient initCert(String certPath) throws Exception { + String path = "apiclient_cert.p12"; + //File file = new File(this.getClass().getResource("/").getPath()+path); + File file = new File("/www/wwwroot/msjd.project2.tingsun.net/" + path); + // 证书密码,默认为商户ID + String key = environment.getProperty("MCHID"); + // 指定读取证书格式为PKCS12 + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + // 读取本机存放的PKCS12证书文件 + FileInputStream instream = new FileInputStream(file); + try { + // 指定PKCS12的密码(商户ID) + keyStore.load(instream, key.toCharArray()); + } finally { + instream.close(); + } + SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build(); + SSLConnectionSocketFactory sslsf = + new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + return HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } + + /** + * 修改退款订单 + * @param orderId + * @param transactionId + * @return + * @throws Exception + */ + /* public int addOrUpdate(Long orderId, String transactionId) throws Exception { + Order order = orderMapper.selectById(orderId); + String userId = StringUtils.valueOf(order.getUserId()); + User user = userMapper.getUserVo(userId); + order.setPrepayId(transactionId); + orderMapper.updateById(order); + return 0; + }*/ + + /** + * 企业退款 + * + * @return + */ +/* @GetMapping("/enterpriseCheckOut") + @ApiOperation(value = "企业退款", notes = "传入order") + public Object enterpriseCheckOut(HttpServletRequest servletRequest) { + String orderNumber = servletRequest.getParameter("orderNumber"); + String openid = servletRequest.getParameter("openid"); + String reason = "退款申请"; + return enterpriseOut(orderNumber, openid, reason); + } + + + public Object enterpriseOut(String orderNumber, String openid, String reason) { + OrderVO orderInfo = orderService.selectOrderNumber(orderNumber); + + String retreat = StringUtils.valueOf(map.get("retreat")); + if (StringUtils.validatorEmpty(retreat)) { + return new SuccessResponseData(ResponseConstants.NOT_EXISTING, "订单金额出错"); + } + CloseableHttpClient httpClient = null; + try { + //证书的地址 + ConfigListener.getConf().get("certPath"); + httpClient = initCert(ConfigListener.getConf().get("certPath")); + } catch (Exception e) { + e.printStackTrace(); + + } + //退款金额单位为分 + Double value = Double.valueOf(retreat) * 100; + Integer refund_fee = value.intValue(); + if (refund_fee <= 0) { + return new SuccessResponseData(ResponseConstants.NOT_EXISTING, "订单金额出错"); + } + String result = ""; + HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"); + AjaxResult successResponseData = new AjaxResult(); + StringEntity postEntity = new StringEntity(creatXMLParam(orderInfo.getPrepayId(), refund_fee.toString(), reason, openid), "UTF-8"); + httpPost.addHeader("Content-Type", "text/xml"); + httpPost.setEntity(postEntity); + try { + HttpResponse response = null; + try { + response = httpClient.execute(httpPost); + } catch (IOException e) { + e.printStackTrace(); + } + HttpEntity entity = response.getEntity(); + try { + result = EntityUtils.toString(entity, "UTF-8"); + } catch (IOException e) { + e.printStackTrace(); + } + } finally { + httpPost.abort(); + } + return parseXml(result, successResponseData, orderInfo.getId(), Double.valueOf(retreat)); + }*/ + + + /** + * 生成xml格式 添加参数时,按照abcd26个字母顺序添加 + * + * @param openid 用户openid + * @param partner_trade_no 商户退款单号 + * @param amount 退款金额 + * @param refund_desc 退款原因 + * @return + */ + private String creatXMLParam(String partner_trade_no, String amount, String refund_desc, String openid) { + String param = ""; + //随机字符串 + String nonceStr = WXPayUtil.generateNonceStr(); + //退款金额 + param += "amount=" + amount; + //校验用户姓名选项 + param += "&check_name=" + "NO_CHECK"; + //退款原因 + param += "&desc=" + refund_desc; + param += "&mch_appid=" + environment.getProperty("APPID"); + //商户id + param += "&mchid=" + environment.getProperty("MCHID"); + //随机字符串 + param += "&nonce_str=" + nonceStr; + //用户openid + param += "&openid=" + openid; + //商户退款单号 一个订单唯一 + param += "&partner_trade_no=" + partner_trade_no; + //生成签名 添加key值 + String stringSignTemp = param + "&key=" + environment.getProperty("KEY"); + //签名 不参与签名 默认MD5算法 + String sign = StringUtils.md5(stringSignTemp); + String xmlString = "\n" + + " " + amount + "\n" + + " " + "NO_CHECK" + "\n" + + " " + refund_desc + "\n" + + " " + environment.getProperty("APPID") + "\n" + + " " + environment.getProperty("MCHID") + "\n" + + " " + nonceStr + "\n" + + " " + openid + "\n" + + " " + partner_trade_no + "\n" + + " " + sign + "\n" + + " "; + return xmlString; + } + + /** + * 解析xml字符串 + * + * @param result 请求后的结果 + * @param jsonObject json对象 + * @return + */ +/* private AjaxResult parseXml(String result, AjaxResult jsonObject, Long id, Double refundMoney) { + try { + Map map = WXPayUtil.xmlToMap(result); + String return_code = map.get("return_code"); + if ("FAIL".equals(return_code)) { + jsonObject.error(map.get("return_msg")); + } else { + orderService.checkOut(id, refundMoney); + jsonObject.success(map); + } + } catch (Exception e) { + jsonObject.error(e.getMessage()); + e.printStackTrace(); + } + return jsonObject; + }*/ +} diff --git a/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/domain/XhpcRechargeOrder.java b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/domain/XhpcRechargeOrder.java new file mode 100644 index 00000000..8b2e6c76 --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/domain/XhpcRechargeOrder.java @@ -0,0 +1,120 @@ +package com.xhpc.payment.domain; + +import com.ruoyi.common.core.web.domain.BaseEntity; + +import java.math.BigDecimal; + + +/** + * @description 充值订单 xhpc_recharge_order + * @author + * @date 2021-07-22 + */ +public class XhpcRechargeOrder extends BaseEntity { + + + /** + * 充值订单id + */ + private Long rechargeOrderId; + + /** + * 用户id + */ + private Long userId; + + /** + * 微信支付订单号 + */ + private String prepayId; + + /** + * 支付宝订单编号 + */ + private String alipayNumber; + + /** + * 充值金额 + */ + private BigDecimal amount; + + /** + * 充值渠道(1微信 2支付宝) + */ + private Integer type; + + + /** + * 状态(0待支付 1充值成功,2充值失败) + */ + private Integer status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + private Integer delFlag; + + public Long getRechargeOrderId() { + return rechargeOrderId; + } + + public void setRechargeOrderId(Long rechargeOrderId) { + this.rechargeOrderId = rechargeOrderId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getPrepayId() { + return prepayId; + } + + public void setPrepayId(String prepayId) { + this.prepayId = prepayId; + } + + public String getAlipayNumber() { + return alipayNumber; + } + + public void setAlipayNumber(String alipayNumber) { + this.alipayNumber = alipayNumber; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getDelFlag() { + return delFlag; + } + + public void setDelFlag(Integer delFlag) { + this.delFlag = delFlag; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } +} diff --git a/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/mapper/XhpcRechargeOrderMapper.java b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/mapper/XhpcRechargeOrderMapper.java new file mode 100644 index 00000000..5daa3cca --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/mapper/XhpcRechargeOrderMapper.java @@ -0,0 +1,42 @@ +package com.xhpc.payment.mapper; + +import com.xhpc.payment.domain.XhpcRechargeOrder; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 充值订单信息 数据层 + * + * @author ruoyi + */ +public interface XhpcRechargeOrderMapper { + + /** + * 新增 充值订单信息 + * + * @param xhpcRechargeOrder 充值订单信息 + * @return 结果 + */ + public int insert(XhpcRechargeOrder xhpcRechargeOrder); + + /** + * 修改充值订单信息 + * + * @param xhpcRechargeOrder 充值订单信息 + * @return 结果 + */ + public int update(XhpcRechargeOrder xhpcRechargeOrder); + + + /** + * 查询充值订单详情 + * + * @param rechargeOrderId 充值订单id + * @return 结果 + */ + public Map info(@Param("rechargeOrderId") Long rechargeOrderId); + + +} diff --git a/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/service/IXhpcRechargeOrderService.java b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/service/IXhpcRechargeOrderService.java new file mode 100644 index 00000000..7030514f --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/service/IXhpcRechargeOrderService.java @@ -0,0 +1,37 @@ +package com.xhpc.payment.service; + +import com.xhpc.payment.domain.XhpcRechargeOrder; +import java.util.Map; + + +/** + * 充值订单信息 服务层 + * + * @author ruoyi + */ +public interface IXhpcRechargeOrderService { + + /** + * 更新 充值订单 + * + * @param xhpcRechargeOrder 充值订单 + */ + public int update(XhpcRechargeOrder xhpcRechargeOrder); + + + /** + * 充值订单详情 + * + * @param appUserId 充值订单id + * @return 结果 + */ + public Map info(Long appUserId); + + /** + * 新增 充值订单 + * @param openid + * @param amount + * @return + */ + public XhpcRechargeOrder add(String openid,double amount); +} \ No newline at end of file diff --git a/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/service/impl/XhpcRechargeOrderServiceImpl.java b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/service/impl/XhpcRechargeOrderServiceImpl.java new file mode 100644 index 00000000..02907185 --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/java/com/xhpc/payment/service/impl/XhpcRechargeOrderServiceImpl.java @@ -0,0 +1,58 @@ +package com.xhpc.payment.service.impl; + +import com.xhpc.payment.domain.XhpcRechargeOrder; +import com.xhpc.payment.mapper.XhpcRechargeOrderMapper; +import com.xhpc.payment.service.IXhpcRechargeOrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 充值订单信息 服务层 + * + * @author ruoyi + */ +@Service +public class XhpcRechargeOrderServiceImpl implements IXhpcRechargeOrderService { + + @Autowired + private XhpcRechargeOrderMapper xhpcRechargeOrderMapper; + + + /** + * 更新 充值订单 + * + * @param xhpcRechargeOrder 充值订单 + */ + @Override + public int update(XhpcRechargeOrder xhpcRechargeOrder) { + return xhpcRechargeOrderMapper.update(xhpcRechargeOrder); + } + + + /** + * 充值订单详情 + * + * @param appUserId 充值订单id + * @return 结果 + */ + @Override + public Map info(Long appUserId) { + return xhpcRechargeOrderMapper.info(appUserId); + } + + /** + * 新增 充值订单 + * @param openid + * @param amount + * @return + */ + @Override + public XhpcRechargeOrder add(String openid,double amount) { + XhpcRechargeOrder xhpcRechargeOrder = new XhpcRechargeOrder(); + xhpcRechargeOrderMapper.insert(xhpcRechargeOrder); + return xhpcRechargeOrder; + } + +} \ No newline at end of file diff --git a/xhpc-modules/xhpc-payment/src/main/resources/banner.txt b/xhpc-modules/xhpc-payment/src/main/resources/banner.txt new file mode 100644 index 00000000..27cacb9c --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ __ _ _ + (_) / _|(_)| | + _ __ _ _ ___ _ _ _ ______ | |_ _ | | ___ +| '__|| | | | / _ \ | | | || ||______|| _|| || | / _ \ +| | | |_| || (_) || |_| || | | | | || || __/ +|_| \__,_| \___/ \__, ||_| |_| |_||_| \___| + __/ | + |___/ \ No newline at end of file diff --git a/xhpc-modules/xhpc-payment/src/main/resources/bootstrap.yml b/xhpc-modules/xhpc-payment/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..648ff1b1 --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/resources/bootstrap.yml @@ -0,0 +1,46 @@ +ppsvc: + server: 0.0.0.0 + port: 8888 + +# Tomcat +server: + port: 9802 + +# Spring +spring: + application: + # 应用名称 + name: xhpc-payment + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 118.24.137.203:8848 + config: + # 配置中心地址 + server-addr: 118.24.137.203:8848 + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + +##获取微信openid地址 +WXGETJSCODE: "https://api.weixin.qq.com/sns/jscode2session?appid=wxb14ef93e9b7901f3&secret=b5c5672141b5930c30a1abee95a2dcbf&js_code=" +##阿里云身份证验证地址 +VERIFYCARD: "http://idenauthen.market.alicloudapi.com/idenAuthentication" +#阿里云身份证验证地址appcode +APPCODE: "APPCODE e26d9088b58e24af69411d5933cece47" +#小程序appid +APPID: "wxd0a48e00319ef8a7" +#小程序绑定商户id +MCHID: "1514355771" +#商户后台设置的key +KEY: "sichuanxianghuakejiyouxiangongsi" +#微信小程序支付地址 +WXPAYUNIFIEDORDER: "https://api.mch.weixin.qq.com/pay/unifiedorder" +#支付回调地址 +SERVERDOMAIN: "https://cdz.project2.tingsun.net/wx/paymentCallback" \ No newline at end of file diff --git a/xhpc-modules/xhpc-payment/src/main/resources/logback.xml b/xhpc-modules/xhpc-payment/src/main/resources/logback.xml new file mode 100644 index 00000000..83ed4f2d --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/xhpc-modules/xhpc-payment/src/main/resources/mapper/XhpcRechargeOrderMapper.xml b/xhpc-modules/xhpc-payment/src/main/resources/mapper/XhpcRechargeOrderMapper.xml new file mode 100644 index 00000000..21083fa3 --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/resources/mapper/XhpcRechargeOrderMapper.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + INSERT INTO xhpc_recharge_order + + + recharge_order_id, + + + user_id, + + + prepay_id, + + + alipay_number, + + + amount, + + + type, + + + status, + + + del_flag, + + + create_time, + + + create_by, + + + update_time, + + + update_by, + + + remark + + + + + #{rechargeOrderId}, + + + #{userId}, + + + #{prepayId}, + + + #{alipayNumber}, + + + #{amount}, + + + #{type}, + + + #{status}, + + + #{delFlag}, + + + #{createTime}, + + + #{createBy}, + + + #{updateTime}, + + + #{updateBy}, + + + #{remark} + + + + + + UPDATE xhpc_recharge_order + + user_id = #{userId}, + prepay_id = #{prepayId}, + alipay_number = #{alipayNumber}, + amount = #{amount}, + type = #{type}, + status = #{status}, + del_flag = #{delFlag}, + create_time = #{createTime}, + create_by = #{createBy}, + update_time = #{updateTime}, + update_by = #{updateBy}, + remark = #{remark} + + WHERE recharge_order_id = #{rechargeOrderId} + + + + + \ No newline at end of file diff --git a/xhpc-modules/xhpc-payment/src/main/resources/svcmainlogic.xml b/xhpc-modules/xhpc-payment/src/main/resources/svcmainlogic.xml new file mode 100644 index 00000000..9aee48b3 --- /dev/null +++ b/xhpc-modules/xhpc-payment/src/main/resources/svcmainlogic.xml @@ -0,0 +1,16 @@ + + + + + + + + + + +