Browse Source

Merge remote-tracking branch 'origin/dev0.0.1' into dev-lingfeng

bilingfeng 2 years ago
parent
commit
1a121a409f
39 changed files with 2345 additions and 37 deletions
  1. 66 0
      game-module/game-common/src/main/java/com/zanxiang/common/domain/ResultMap.java
  2. 23 0
      game-module/game-common/src/main/java/com/zanxiang/common/enums/DeleteEnum.java
  3. 23 0
      game-module/game-common/src/main/java/com/zanxiang/common/enums/MaxPayLock.java
  4. 45 0
      game-module/game-common/src/main/java/com/zanxiang/common/enums/Pay/WxPayConfigEnum.java
  5. 51 0
      game-module/game-common/src/main/java/com/zanxiang/common/enums/PayWayEnum.java
  6. 2 1
      game-module/game-common/src/main/java/com/zanxiang/common/enums/ResEnum.java
  7. 115 0
      game-module/game-common/src/main/java/com/zanxiang/common/utils/BaseHttpSSLSocketFactory.java
  8. 299 0
      game-module/game-common/src/main/java/com/zanxiang/common/utils/HttpUtils.java
  9. 60 0
      game-module/game-common/src/main/java/com/zanxiang/common/utils/MoneyUtil.java
  10. 15 15
      game-module/game-manage/src/main/java/com/zanxiang/manage/controller/DemoController.java
  11. 31 0
      game-module/game-sdk/pom.xml
  12. 2 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/SDKApplication.java
  13. 113 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/HttpUtil.java
  14. 46 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/MD5Util.java
  15. 114 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/PayCommonUtil.java
  16. 24 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/WxPayUrl.java
  17. 134 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/WxPayUtil.java
  18. 151 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/XMLUtil.java
  19. 44 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/ZxingUtils.java
  20. 64 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/controller/PayController.java
  21. 23 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/bo/ProductPayAttachParamBO.java
  22. 44 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/bo/ProductPayParamBO.java
  23. 18 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/bo/WxPayConfigBO.java
  24. 3 3
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/entity/GamePayWay.java
  25. 2 2
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/entity/UserLoginLog.java
  26. 2 1
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/params/CommonParam.java
  27. 33 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/params/ProductPayParam.java
  28. 27 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/GamePayWayService.java
  29. 2 3
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/IUserLoginLogService.java
  30. 45 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/GamePayWayServiceImpl.java
  31. 6 5
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/PlatformOrderServiceImpl.java
  32. 1 1
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/UserLoginLogServiceImpl.java
  33. 232 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/pay/AlipayServiceImpl.java
  34. 134 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/pay/PayService.java
  35. 323 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/pay/WxpayServiceImpl.java
  36. 21 0
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/OrderPayService.java
  37. 2 1
      game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/PlatformOrderService.java
  38. 2 2
      game-module/game-sdk/src/main/resources/bootstrap.yml
  39. 3 3
      game-module/game-sdk/src/test/java/com/zanxiang/sdk/OrderTests.java

+ 66 - 0
game-module/game-common/src/main/java/com/zanxiang/common/domain/ResultMap.java

@@ -0,0 +1,66 @@
+package com.zanxiang.common.domain;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 返回对象
+ *
+ * @author xufeng
+ * @date 2022/6/9 10:41
+ */
+public class ResultMap extends HashMap<String, Object> {
+
+    private static final long serialVersionUID = 1L;
+
+    public ResultMap() {
+        put("code", 0);
+    }
+
+    public static ResultMap error() {
+        return error(500, "未知异常,请联系管理员");
+    }
+
+    public static ResultMap error(String msg) {
+        return error(500, msg);
+    }
+
+    public static ResultMap error(int code, String msg) {
+        ResultMap r = new ResultMap();
+        r.put("code", code);
+        r.put("msg", msg);
+        return r;
+    }
+
+    public static ResultMap ok(Object msg) {
+        ResultMap r = new ResultMap();
+        r.put("msg", msg);
+        return r;
+    }
+
+
+    public static ResultMap ok(Map<String, Object> map) {
+        ResultMap r = new ResultMap();
+        r.putAll(map);
+        return r;
+    }
+
+    public static ResultMap ok() {
+        ResultMap r = new ResultMap();
+        r.put("msg", "操作成功");
+        return r;
+    }
+
+    public static Boolean isOk(ResultMap r) {
+        if (r.get("code").toString().equals("0")) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public ResultMap put(String key, Object value) {
+        super.put(key, value);
+        return this;
+    }
+}

+ 23 - 0
game-module/game-common/src/main/java/com/zanxiang/common/enums/DeleteEnum.java

@@ -0,0 +1,23 @@
+package com.zanxiang.common.enums;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author xufeng
+ * @date 2022/6/13 11:39
+ */
+@Getter
+@NoArgsConstructor
+public enum DeleteEnum {
+    NO(2, "正常"),
+    YES(1, "已删除");
+
+    private int code;
+    private String name;
+
+    DeleteEnum(int code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+}

+ 23 - 0
game-module/game-common/src/main/java/com/zanxiang/common/enums/MaxPayLock.java

@@ -0,0 +1,23 @@
+package com.zanxiang.common.enums;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author xufeng
+ * @date 2022/6/13 13:39
+ */
+@Getter
+@NoArgsConstructor
+public enum MaxPayLock {
+    LOCK(1, "已锁定"),
+    UNLOCK(0, "未锁定");
+
+    private int code;
+    private String name;
+
+    MaxPayLock(int code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+}

+ 45 - 0
game-module/game-common/src/main/java/com/zanxiang/common/enums/Pay/WxPayConfigEnum.java

@@ -0,0 +1,45 @@
+package com.zanxiang.common.enums.Pay;
+
+import java.util.*;
+
+/**
+ * @author xufeng
+ * @date 2022/6/9 15:29
+ */
+public enum WxPayConfigEnum {
+    APPID("appId", "商户支付appId"),
+    APPSECRET("appSecret", "公众帐号secert"),
+    MCHID("mchId", "商户号"),
+    APIKEY("apiKey", "商户支付密钥"),
+    SIGNTYPE("signType", "加密方式"),
+    CERTPATH("certPath", "密钥地址"),
+    NOTIFYURL("notifyUrl", "回调地址(默认为空即可)");
+
+    private final String code;
+    private final String msg;
+
+    WxPayConfigEnum(String code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public static List<String> getCodeList() {
+        List<String> arr = new ArrayList<>(20);
+        WxPayConfigEnum[] data = WxPayConfigEnum.values();
+        if (data.length == 0) {
+            return arr;
+        }
+        for (int i = 0; i < data.length; i++) {
+            arr.add(data[i].getCode());
+        }
+        return arr;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 51 - 0
game-module/game-common/src/main/java/com/zanxiang/common/enums/PayWayEnum.java

@@ -0,0 +1,51 @@
+package com.zanxiang.common.enums;
+
+/**
+ * 支付方式枚举
+ *
+ * @author xufeng
+ * @date 2022/6/9 10:41
+ */
+public enum PayWayEnum {
+    ALIPAY("Alipay", "支付宝", 1),
+    WXPAY("Wxpay", "微信支付", 2),
+    UNIONPAY("Unionpay", "银联支付", 3);
+
+    private final String code;
+    private final String name;
+    private final Integer num;
+
+    PayWayEnum(String code, String name, Integer num) {
+        this.code = code;
+        this.name = name;
+        this.num = num;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public Integer getNum() {
+        return num;
+    }
+
+    /**
+     * 通过num获取pay code
+     *
+     * @param num
+     * @return code
+     */
+    public static String getCodeByNum(Integer num) {
+        PayWayEnum[] values = PayWayEnum.values();
+        for (int i = 0; i < values.length; i++) {
+            if (values[i].getNum().equals(num))
+                return values[i].getCode();
+        }
+        return "";
+    }
+
+}

+ 2 - 1
game-module/game-common/src/main/java/com/zanxiang/common/enums/ResEnum.java

@@ -1,7 +1,8 @@
 package com.zanxiang.common.enums;
 
 public enum ResEnum {
-    SUCCESS(200, "SUCCESS"),
+    SUCCESS(200, "success"),//勿修改,影响支付响应
+    FAIL(400, "failed"),//勿修改,影响支付响应
     TO_LOGIN(310, "登录失效,请重新登录!"),
     ERROR(500, "系统异常"),
     REMOTE_SERVER_FAIL(503, "远程服务调用失败"),

+ 115 - 0
game-module/game-common/src/main/java/com/zanxiang/common/utils/BaseHttpSSLSocketFactory.java

@@ -0,0 +1,115 @@
+/**
+ * Licensed Property to China UnionPay Co., Ltd.
+ * <p>
+ * (C) Copyright of China UnionPay Co., Ltd. 2010
+ * All Rights Reserved.
+ * <p>
+ * <p>
+ * Modification History:
+ * =============================================================================
+ * Author         Date          Description
+ * ------------ ---------- ---------------------------------------------------
+ * xshu       2014-05-28     SSLSocket 链接工具类(用于https)
+ * =============================================================================
+ */
+package com.zanxiang.common.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.*;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.cert.X509Certificate;
+
+public class BaseHttpSSLSocketFactory extends SSLSocketFactory {
+    private static final Logger logger = LoggerFactory.getLogger(BaseHttpSSLSocketFactory.class);
+
+    private SSLContext getSSLContext() {
+        return createEasySSLContext();
+    }
+
+    @Override
+    public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2,
+                               int arg3) throws IOException {
+        return getSSLContext().getSocketFactory().createSocket(arg0, arg1,
+                arg2, arg3);
+    }
+
+    @Override
+    public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3)
+            throws IOException, UnknownHostException {
+        return getSSLContext().getSocketFactory().createSocket(arg0, arg1,
+                arg2, arg3);
+    }
+
+    @Override
+    public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
+        return getSSLContext().getSocketFactory().createSocket(arg0, arg1);
+    }
+
+    @Override
+    public Socket createSocket(String arg0, int arg1) throws IOException,
+            UnknownHostException {
+        return getSSLContext().getSocketFactory().createSocket(arg0, arg1);
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return null;
+    }
+
+    @Override
+    public String[] getDefaultCipherSuites() {
+        return null;
+    }
+
+    @Override
+    public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3)
+            throws IOException {
+        return getSSLContext().getSocketFactory().createSocket(arg0, arg1,
+                arg2, arg3);
+    }
+
+    private SSLContext createEasySSLContext() {
+        try {
+            SSLContext context = SSLContext.getInstance("SSL");
+            context.init(null,
+                    new TrustManager[]{MyX509TrustManager.manger}, null);
+            return context;
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    public static class MyX509TrustManager implements X509TrustManager {
+
+        static MyX509TrustManager manger = new MyX509TrustManager();
+
+        public MyX509TrustManager() {
+        }
+
+        public X509Certificate[] getAcceptedIssuers() {
+            return null;
+        }
+
+        public void checkClientTrusted(X509Certificate[] chain, String authType) {
+        }
+
+        public void checkServerTrusted(X509Certificate[] chain, String authType) {
+        }
+    }
+
+    /**
+     * 解决由于服务器证书问题导致HTTPS无法访问的情况 PS:HTTPS hostname wrong: should be <localhost>
+     */
+    public static class TrustAnyHostnameVerifier implements HostnameVerifier {
+        public boolean verify(String hostname, SSLSession session) {
+            // 直接返回true
+            return true;
+        }
+    }
+}

+ 299 - 0
game-module/game-common/src/main/java/com/zanxiang/common/utils/HttpUtils.java

@@ -0,0 +1,299 @@
+package com.zanxiang.common.utils;
+
+import org.springframework.util.StringUtils;
+
+import javax.net.ssl.*;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * http、https 请求工具类
+ */
+public class HttpUtils {
+
+    private static final String DEFAULT_CHARSET = "UTF-8";
+
+    private static final String _GET = "GET"; // GET
+    private static final String _POST = "POST";// POST
+    public static final int DEF_CONN_TIMEOUT = 30000;
+    public static final int DEF_READ_TIMEOUT = 30000;
+
+    /**
+     * 初始化http请求参数
+     *
+     * @param url
+     * @param method
+     * @param headers
+     * @return
+     * @throws Exception
+     */
+    private static HttpURLConnection initHttp(String url, String method,
+                                              Map<String, String> headers) throws Exception {
+        URL _url = new URL(url);
+        HttpURLConnection http = (HttpURLConnection) _url.openConnection();
+        // 连接超时
+        http.setConnectTimeout(DEF_CONN_TIMEOUT);
+        // 读取超时 --服务器响应比较慢,增大时间
+        http.setReadTimeout(DEF_READ_TIMEOUT);
+        http.setUseCaches(false);
+        http.setRequestMethod(method);
+        http.setRequestProperty("Content-Type",
+                "application/x-www-form-urlencoded");
+        http.setRequestProperty(
+                "User-Agent",
+                "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
+        if (null != headers && !headers.isEmpty()) {
+            for (Entry<String, String> entry : headers.entrySet()) {
+                http.setRequestProperty(entry.getKey(), entry.getValue());
+            }
+        }
+        http.setDoOutput(true);
+        http.setDoInput(true);
+        http.connect();
+        return http;
+    }
+
+    /**
+     * 初始化http请求参数
+     *
+     * @param url
+     * @param method
+     * @return
+     * @throws Exception
+     */
+    private static HttpsURLConnection initHttps(String url, String method,
+                                                Map<String, String> headers) throws Exception {
+        TrustManager[] tm = {new MyX509TrustManager()};
+//        System.setProperty("https.protocols", "TLSv1");
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, tm, new java.security.SecureRandom());
+        // 从上述SSLContext对象中得到SSLSocketFactory对象
+        SSLSocketFactory ssf = sslContext.getSocketFactory();
+        URL _url = new URL(url);
+        HttpsURLConnection http = (HttpsURLConnection) _url.openConnection();
+        // 设置域名校验
+        http.setHostnameVerifier(new HttpUtils().new TrustAnyHostnameVerifier());
+        // 连接超时
+        http.setConnectTimeout(DEF_CONN_TIMEOUT);
+        // 读取超时 --服务器响应比较慢,增大时间
+        http.setReadTimeout(DEF_READ_TIMEOUT);
+        http.setUseCaches(false);
+        http.setRequestMethod(method);
+        http.setRequestProperty("Content-Type",
+                "application/x-www-form-urlencoded");
+        http.setRequestProperty(
+                "User-Agent",
+                "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
+        if (null != headers && !headers.isEmpty()) {
+            for (Entry<String, String> entry : headers.entrySet()) {
+                http.setRequestProperty(entry.getKey(), entry.getValue());
+            }
+        }
+        http.setSSLSocketFactory(ssf);
+        http.setDoOutput(true);
+        http.setDoInput(true);
+        http.connect();
+        return http;
+    }
+
+    /**
+     * @return 返回类型:
+     * @throws Exception
+     * @description 功能描述: get 请求
+     */
+    public static String get(String url, Map<String, String> params,
+                             Map<String, String> headers) throws Exception {
+        HttpURLConnection http = null;
+        if (isHttps(url)) {
+            http = initHttps(initParams(url, params), _GET, headers);
+        } else {
+            http = initHttp(initParams(url, params), _GET, headers);
+        }
+        InputStream in = http.getInputStream();
+        BufferedReader read = new BufferedReader(new InputStreamReader(in,
+                DEFAULT_CHARSET));
+        String valueString = null;
+        StringBuffer bufferRes = new StringBuffer();
+        // 优化,不能光使用!=null做判断。有为""的情况,防止多次空循环
+        while (!StringUtils.isEmpty(valueString = read.readLine())) {
+            bufferRes.append(valueString);
+        }
+        in.close();
+        if (http != null) {
+            http.disconnect();// 关闭连接
+        }
+        return bufferRes.toString();
+    }
+
+    public static String get(String url) throws Exception {
+        return get(url, null);
+    }
+
+    public static String get(String url, Map<String, String> params)
+            throws Exception {
+        return get(url, params, null);
+    }
+
+    public static String post(String url, String params) throws Exception {
+        HttpURLConnection http = null;
+        if (isHttps(url)) {
+            http = initHttps(url, _POST, null);
+        } else {
+            http = initHttp(url, _POST, null);
+        }
+        OutputStream out = http.getOutputStream();
+        out.write(params.getBytes(DEFAULT_CHARSET));
+        out.flush();
+        out.close();
+
+        InputStream in = http.getInputStream();
+        BufferedReader read = new BufferedReader(new InputStreamReader(in,
+                DEFAULT_CHARSET));
+        String valueString = null;
+        StringBuffer bufferRes = new StringBuffer();
+        while ((valueString = read.readLine()) != null) {
+            bufferRes.append(valueString);
+        }
+        in.close();
+        if (http != null) {
+            http.disconnect();// 关闭连接
+        }
+        return bufferRes.toString();
+    }
+
+    public static String post(String url, String params, Map<String, String> headers) throws Exception {
+        HttpURLConnection http = null;
+        if (isHttps(url)) {
+            http = initHttps(url, _POST, headers);
+        } else {
+            http = initHttp(url, _POST, headers);
+        }
+        OutputStream out = http.getOutputStream();
+        out.write(params.getBytes(DEFAULT_CHARSET));
+        out.flush();
+        out.close();
+
+        InputStream in = http.getInputStream();
+        BufferedReader read = new BufferedReader(new InputStreamReader(in,
+                DEFAULT_CHARSET));
+        String valueString = null;
+        StringBuffer bufferRes = new StringBuffer();
+        while ((valueString = read.readLine()) != null) {
+            bufferRes.append(valueString);
+        }
+        in.close();
+        if (http != null) {
+            http.disconnect();// 关闭连接
+        }
+        return bufferRes.toString();
+    }
+
+    /**
+     * 功能描述: 构造请求参数
+     *
+     * @return 返回类型:
+     * @throws Exception
+     */
+    public static String initParams(String url, Map<String, String> params)
+            throws Exception {
+        if (null == params || params.isEmpty()) {
+            return url;
+        }
+        StringBuilder sb = new StringBuilder(url);
+        if (url.indexOf("?") == -1) {
+            sb.append("?");
+        }
+        sb.append(map2Url(params));
+        return sb.toString();
+    }
+
+    /**
+     * map构造url
+     *
+     * @return 返回类型:
+     * @throws Exception
+     */
+    public static String map2Url(Map<String, String> paramToMap)
+            throws Exception {
+        if (null == paramToMap || paramToMap.isEmpty()) {
+            return null;
+        }
+        StringBuffer url = new StringBuffer();
+        boolean isfist = true;
+        for (Entry<String, String> entry : paramToMap.entrySet()) {
+            if (isfist) {
+                isfist = false;
+            } else {
+                url.append("&");
+            }
+            url.append(entry.getKey()).append("=");
+            String value = entry.getValue();
+            if (!StringUtils.isEmpty(value)) {
+                url.append(URLEncoder.encode(value, DEFAULT_CHARSET));
+            }
+        }
+        return url.toString();
+    }
+
+    /**
+     * 检测是否https
+     *
+     * @param url
+     */
+    private static boolean isHttps(String url) {
+        return url.startsWith("https");
+    }
+
+    /**
+     * https 域名校验
+     *
+     * @return
+     */
+    public class TrustAnyHostnameVerifier implements HostnameVerifier {
+        @Override
+        public boolean verify(String hostname, SSLSession session) {
+            return true;// 直接返回true
+        }
+    }
+
+    private static class MyX509TrustManager implements X509TrustManager {
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return null;
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+    }
+
+    ;
+
+    public static void main(String[] args) {
+        String str = "";
+        try {
+            str = get("https://www.baidu.com");
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+
+        System.out.println(str);
+    }
+
+}

+ 60 - 0
game-module/game-common/src/main/java/com/zanxiang/common/utils/MoneyUtil.java

@@ -0,0 +1,60 @@
+package com.zanxiang.common.utils;
+
+import org.springframework.util.Assert;
+
+import java.math.BigDecimal;
+
+/**
+ * @author xufeng
+ * @date 2022/6/13 10:57
+ */
+public abstract class MoneyUtil {
+
+    /**
+     * 将单位为元的金额转换为单位为分
+     *
+     * @param yuan 单位为元的字符型值
+     * @return
+     */
+    public static int yuan2Fen(BigDecimal yuan) {
+        int value = 0;
+
+        try {
+            BigDecimal var2 = new BigDecimal(100);
+            BigDecimal var3 = yuan.multiply(var2);
+            value = Integer.parseInt(var3.stripTrailingZeros().toPlainString());
+        } catch (Exception e) {
+            throw new IllegalArgumentException(String.format("非法金额[%s]", yuan));
+        }
+
+        Assert.isTrue(value >= 0, String.format("非法金额[%s]", yuan));
+        return value;
+    }
+
+    /**
+     * 将单位为分的金额转换为单位为元
+     *
+     * @param fen 单位为分的字符型值
+     * @return
+     */
+    public static String fen2Yuan(int fen) {
+        BigDecimal var1 = new BigDecimal(fen);
+        BigDecimal var2 = new BigDecimal(100);
+        BigDecimal var3 = var1.divide(var2);
+        return var3.stripTrailingZeros().toPlainString();
+    }
+
+    /**
+     * 将单位为分的金额转换为单位为元
+     *
+     * @param fen 单位为分的字符型值
+     * @return
+     */
+    public static BigDecimal fen2YuanDecimal(int fen) {
+        BigDecimal var1 = new BigDecimal(fen);
+        BigDecimal var2 = new BigDecimal(100);
+        BigDecimal var3 = var1.divide(var2);
+        return var3;
+    }
+}
+

+ 15 - 15
game-module/game-manage/src/main/java/com/zanxiang/manage/controller/DemoController.java

@@ -27,19 +27,19 @@ public class DemoController {
     @Autowired
     private DemoService demoService;
 
-    @GetMapping("/info")
-    @ApiOperation("/info")
-    public ResultVo<DemoVO> getDemo(@RequestParam(name = "id", defaultValue = "1", required = false) String id) {
-        Demo demo = demoService.getById(id);
-        return ResultVo.ok(BeanUtils.copy(demo, DemoVO.class));
-    }
-
-    @GetMapping("/add")
-    @ApiOperation("/add")
-    public ResultVo<Boolean> addDemo(@RequestParam("name") String name, @RequestParam("content") String content) {
-        Demo demo = new Demo();
-        demo.setContent(content);
-        demo.setName(name);
-        return ResultVo.ok(demoService.save(demo));
-    }
+//    @GetMapping("/info")
+//    @ApiOperation("/info")
+//    public ResultVo<DemoVO> getDemo(@RequestParam(name = "id", defaultValue = "1", required = false) String id) {
+//        Demo demo = demoService.getById(id);
+//        return ResultVo.ok(BeanUtils.copy(demo, DemoVO.class));
+//    }
+//
+//    @GetMapping("/add")
+//    @ApiOperation("/add")
+//    public ResultVo<Boolean> addDemo(@RequestParam("name") String name, @RequestParam("content") String content) {
+//        Demo demo = new Demo();
+//        demo.setContent(content);
+//        demo.setName(name);
+//        return ResultVo.ok(demoService.save(demo));
+//    }
 }

+ 31 - 0
game-module/game-sdk/pom.xml

@@ -37,5 +37,36 @@
             <version>6.0.17.Final</version>
             <scope>compile</scope>
         </dependency>
+        <!--    支付相关pom  -->
+        <dependency>
+            <groupId>com.alipay.sdk</groupId>
+            <artifactId>alipay-easysdk</artifactId>
+            <version>2.1.0</version>
+        </dependency>
+        <!--        <dependency>-->
+        <!--            <groupId>dom4j</groupId>-->
+        <!--            <artifactId>dom4j</artifactId>-->
+        <!--            <version>1.6.1</version>-->
+        <!--        </dependency>-->
+        <dependency>
+            <groupId>jdom</groupId>
+            <artifactId>jdom</artifactId>
+            <version>1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+            <version>3.4.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.liyiorg</groupId>
+            <artifactId>weixin-popular</artifactId>
+            <version>2.8.16</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+            <version>3.0.2</version>
+        </dependency>
     </dependencies>
 </project>

+ 2 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/SDKApplication.java

@@ -5,12 +5,14 @@ import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.Configuration;
 
 
 @Slf4j
 @EnableDiscoveryClient
 @SpringBootApplication
 @MapperScan("com.zanxiang.sdk.mapper")
+@Configuration
 public class SDKApplication {
 
     public static void main(String[] args) {

+ 113 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/HttpUtil.java

@@ -0,0 +1,113 @@
+package com.zanxiang.sdk.common.util;
+
+import com.zanxiang.common.utils.BaseHttpSSLSocketFactory;
+
+import javax.net.ssl.*;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+/**
+ * 获取订单url
+ */
+public class HttpUtil {
+
+    public static final int DEF_CONN_TIMEOUT = 30000;
+    public static final int DEF_READ_TIMEOUT = 30000;
+
+    private final static String DEFAULT_ENCODING = "UTF-8";
+
+    public static String postData(String urlStr, String data) {
+        return postData(urlStr, data, null);
+    }
+
+    public static String postData(String urlStr, String data, String contentType) {
+        BufferedReader reader = null;
+        try {
+            URLConnection conn = initHttps(urlStr);
+            if (contentType != null) {
+                conn.setRequestProperty("content-type", contentType);
+            }
+            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
+            if (data == null) {
+                data = "";
+            }
+            writer.write(data);
+            writer.flush();
+            writer.close();
+
+            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
+            StringBuilder sb = new StringBuilder();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line);
+                sb.append("\r\n");
+            }
+            return sb.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (IOException e) {
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 初始化http请求参数
+     */
+    private static HttpsURLConnection initHttps(String url) throws Exception {
+        TrustManager[] tm = {new MyX509TrustManager()};
+//        System.setProperty("https.protocols", "TLSv1");
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, tm, new java.security.SecureRandom());
+        // 从上述SSLContext对象中得到SSLSocketFactory对象
+        SSLSocketFactory ssf = sslContext.getSocketFactory();
+        URL _url = new URL(url);
+        HttpsURLConnection http = (HttpsURLConnection) _url.openConnection();
+        // 设置域名校验
+        http.setHostnameVerifier(new BaseHttpSSLSocketFactory.TrustAnyHostnameVerifier());
+        // 连接超时
+        http.setConnectTimeout(DEF_CONN_TIMEOUT);
+        // 读取超时 --服务器响应比较慢,增大时间
+        http.setReadTimeout(DEF_READ_TIMEOUT);
+        http.setUseCaches(false);
+        http.setRequestProperty("Content-Type",
+                "application/x-www-form-urlencoded");
+        http.setRequestProperty(
+                "User-Agent",
+                "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
+        http.setSSLSocketFactory(ssf);
+        http.setDoOutput(true);
+        http.setDoInput(true);
+        http.connect();
+        return http;
+    }
+
+    private static class MyX509TrustManager implements X509TrustManager {
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return null;
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+    }
+
+}

+ 46 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/MD5Util.java

@@ -0,0 +1,46 @@
+package com.zanxiang.sdk.common.util;
+
+import com.zanxiang.common.utils.StringUtils;
+
+import java.security.MessageDigest;
+
+/**
+ * MD5加密
+ */
+public class MD5Util {
+
+    private static String byteArrayToHexString(byte[] b) {
+        StringBuffer resultSb = new StringBuffer();
+        for (int i = 0; i < b.length; i++) {
+            resultSb.append(byteToHexString(b[i]));
+        }
+        return resultSb.toString();
+    }
+
+    private static String byteToHexString(byte b) {
+        int n = b;
+        if (n < 0) {
+            n += 256;
+        }
+        int d1 = n / 16;
+        int d2 = n % 16;
+        return hexDigits[d1] + hexDigits[d2];
+    }
+
+    public static String MD5Encode(String origin, String charsetName) {
+        String resultString = null;
+        try {
+            resultString = origin;
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            if (StringUtils.isBlank(charsetName)) {
+                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
+            } else {
+                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetName)));
+            }
+        } catch (Exception exception) {
+        }
+        return resultString;
+    }
+
+    private static final String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
+}

+ 114 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/PayCommonUtil.java

@@ -0,0 +1,114 @@
+package com.zanxiang.sdk.common.util;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class PayCommonUtil {
+
+    /**
+     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名
+     *
+     * @param characterEncoding
+     * @param packageParams
+     * @param API_KEY
+     * @return
+     */
+    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
+        StringBuffer sb = new StringBuffer();
+        Set es = packageParams.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if (!"sign".equals(k) && null != v && !"".equals(v)) {
+                sb.append(k + "=" + v + "&");
+            }
+        }
+        sb.append("key=" + API_KEY);
+        //算出摘要  
+        String mySign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
+        String tenPaySign = ((String) packageParams.get("sign")).toLowerCase();
+        return tenPaySign.equals(mySign);
+    }
+
+    /**
+     * sign签名
+     *
+     * @param characterEncoding
+     * @param packageParams
+     * @param API_KEY
+     * @return
+     */
+    public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
+        StringBuffer sb = new StringBuffer();
+        Set es = packageParams.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
+                sb.append(k + "=" + v + "&");
+            }
+        }
+        sb.append("key=" + API_KEY);
+        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
+        return sign;
+    }
+
+    /**
+     * 将请求参数转换为xml格式的string
+     *
+     * @param parameters
+     * @return
+     */
+    public static String getRequestXml(SortedMap<Object, Object> parameters) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("<xml>");
+        Set es = parameters.entrySet();
+        Iterator it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
+                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
+            } else {
+                sb.append("<" + k + ">" + v + "</" + k + ">");
+            }
+        }
+        sb.append("</xml>");
+        return sb.toString();
+    }
+
+    /**
+     * 取出一个指定长度大小的随机正整数.
+     *
+     * @param length
+     * @return
+     */
+    public static int buildRandom(int length) {
+        int num = 1;
+        double random = Math.random();
+        if (random < 0.1) {
+            random = random + 0.1;
+        }
+        for (int i = 0; i < length; i++) {
+            num = num * 10;
+        }
+        return (int) ((random * num));
+    }
+
+    /**
+     * 获取当前时间
+     *
+     * @return String
+     */
+    public static String getCurrTime() {
+        Date now = new Date();
+        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+        String s = outFormat.format(now);
+        return s;
+    }
+}

+ 24 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/WxPayUrl.java

@@ -0,0 +1,24 @@
+package com.zanxiang.sdk.common.util;
+
+/**
+ * 微信支付接口地址
+ */
+public class WxPayUrl {
+
+    // 微信支付统一接口(POST)
+    public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
+    // 微信退款接口(POST)
+    public final static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
+    // 订单查询接口(POST)
+    public final static String CHECK_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
+    // 关闭订单接口(POST)
+    public final static String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder";
+    // 退款查询接口(POST)
+    public final static String CHECK_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery";
+    // 对账单接口(POST)
+    public final static String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill";
+    // 短链接转换接口(POST)
+    public final static String SHORT_URL = "https://api.mch.weixin.qq.com/tools/shorturl";
+    // 接口调用上报接口(POST)
+    public final static String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report";
+}

+ 134 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/WxPayUtil.java

@@ -0,0 +1,134 @@
+package com.zanxiang.sdk.common.util;
+
+import com.zanxiang.sdk.domain.bo.WxPayConfigBO;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLContexts;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ResourceUtils;
+
+import javax.net.ssl.SSLContext;
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * 相关配置参数
+ */
+public class WxPayUtil {
+
+    private WxPayConfigBO wxPay;
+
+    public WxPayUtil(WxPayConfigBO wxPay) {
+        this.wxPay = wxPay;
+    }
+
+    /**
+     * 基础参数
+     *
+     * @param packageParams
+     */
+    public void commonParams(SortedMap<Object, Object> packageParams) {
+        String appId = wxPay.getAppId();
+        String mch_id = wxPay.getMchId();
+        String currTime = PayCommonUtil.getCurrTime();
+        String strTime = currTime.substring(8);
+        String strRandom = PayCommonUtil.buildRandom(4) + "";
+        String nonce_str = strTime + strRandom;
+        packageParams.put("appid", appId);
+        packageParams.put("mch_id", mch_id);
+        packageParams.put("nonce_str", nonce_str);
+    }
+
+    /**
+     * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX)
+     * 减小二维码数据量,提升扫描速度和精确度
+     *
+     * @param urlCode
+     * @return
+     */
+//    public String shortUrl(String urlCode) {
+//        try {
+//            String key = wxPay.getApiKey();
+//            SortedMap<Object, Object> packageParams = new TreeMap<>();
+//            commonParams(packageParams);
+//            packageParams.put("long_url", urlCode);// URL链接
+//            String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
+//            packageParams.put("sign", sign);// 签名
+//            String requestXML = PayCommonUtil.getRequestXml(packageParams);
+//            String resXml = HttpUtil.postData(WxPayUrl.SHORT_URL, requestXML);
+//            Map map = XMLUtil.doXMLParse(resXml);
+//            String returnCode = (String) map.get("return_code");
+//            if (Constants.SUCCESS.equalsIgnoreCase(returnCode)) {
+//                String resultCode = (String) map.get("return_code");
+//                if (Constants.SUCCESS.equalsIgnoreCase(resultCode)) {
+//                    urlCode = (String) map.get("short_url");
+//                    return urlCode;
+//                } else {
+//                    return Constants.FAIL;
+//                }
+//            } else {
+//                return Constants.FAIL;
+//            }
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//            return Constants.FAIL;
+//        }
+//    }
+//    public String doRefund(String url, String data) throws Exception {
+//        /**
+//         * 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的
+//         */
+//        KeyStore keyStore = KeyStore.getInstance("PKCS12");
+//        File certfile = ResourceUtils.getFile("classpath:cert" +
+//                Constants.SF_FILE_SEPARATOR + wxPay.getCertPath());
+//        FileInputStream instream = new FileInputStream(certfile);
+//        try {
+//            keyStore.load(instream, wxPay.getMchId().toCharArray());
+//        } finally {
+//            instream.close();
+//        }
+//        SSLContext sslcontext = SSLContexts.custom()
+//                .loadKeyMaterial(keyStore, wxPay.getMchId().toCharArray())
+//                .build();
+//        SSLConnectionSocketFactory SSL = new SSLConnectionSocketFactory(
+//                sslcontext,
+//                new String[]{"TLSv1"},
+//                null,
+//                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
+//        CloseableHttpClient httpclient = HttpClients.custom()
+//                .setSSLSocketFactory(SSL)
+//                .build();
+//        try {
+//            HttpPost httPost = new HttpPost(url);
+//            httPost.setEntity(new StringEntity(data, "UTF-8"));
+//            CloseableHttpResponse response = httpclient.execute(httPost);
+//            try {
+//                HttpEntity entity = response.getEntity();
+//                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
+//                EntityUtils.consume(entity);
+//                return jsonStr;
+//            } finally {
+//                response.close();
+//            }
+//        } finally {
+//            httpclient.close();
+//        }
+//    }
+    public static String subZeroAndDot(String s) {
+        if (s.indexOf(".") > 0) {
+            s = s.replaceAll("0+?$", "");// 去掉多余的0
+            s = s.replaceAll("[.]$", "");// 如最后一位是.则去掉
+        }
+        return s;
+    }
+}

+ 151 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/XMLUtil.java

@@ -0,0 +1,151 @@
+package com.zanxiang.sdk.common.util;
+
+import com.zanxiang.common.utils.StringUtils;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * XML解析
+ */
+public class XMLUtil {
+
+    /**
+     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
+     *
+     * @param strXml
+     * @return
+     * @throws JDOMException
+     * @throws IOException
+     */
+    public static Map doXMLParse(String strXml) throws JDOMException, IOException {
+        strXml = filterXXE(strXml);
+        strXml = strXml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
+        if (StringUtils.isBlank(strXml)) {
+            return null;
+        }
+        Map m = new HashMap();
+        InputStream in = new ByteArrayInputStream(strXml.getBytes("UTF-8"));
+        SAXBuilder builder = new SAXBuilder();
+        Document doc = builder.build(in);
+        Element root = doc.getRootElement();
+        List list = root.getChildren();
+        Iterator it = list.iterator();
+        while (it.hasNext()) {
+            Element e = (Element) it.next();
+            String k = e.getName();
+            String v;
+            List children = e.getChildren();
+            if (children.isEmpty()) {
+                v = e.getTextNormalize();
+            } else {
+                v = XMLUtil.getChildrenText(children);
+            }
+
+            m.put(k, v);
+        }
+        in.close();
+        return m;
+    }
+
+    /**
+     * 获取子结点的xml
+     *
+     * @param children
+     * @return String
+     */
+    public static String getChildrenText(List children) {
+        StringBuffer sb = new StringBuffer();
+        if (!children.isEmpty()) {
+            Iterator it = children.iterator();
+            while (it.hasNext()) {
+                Element e = (Element) it.next();
+                String name = e.getName();
+                String value = e.getTextNormalize();
+                List list = e.getChildren();
+                sb.append("<" + name + ">");
+                if (!list.isEmpty()) {
+                    sb.append(XMLUtil.getChildrenText(list));
+                }
+                sb.append(value);
+                sb.append("</" + name + ">");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 通过DOCTYPE和ENTITY来加载本地受保护的文件、替换掉即可
+     * 漏洞原理:https://my.oschina.net/u/574353/blog/1841103
+     *      * 防止 XXE漏洞 注入实体攻击
+     *      * 过滤 过滤用户提交的XML数据
+     *      * 过滤关键词:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC。
+     *    
+     */
+    public static String filterXXE(String xmlStr) {
+        xmlStr = xmlStr.replace("DOCTYPE", "").replace("SYSTEM", "").replace("ENTITY", "").replace("PUBLIC", "");
+        return xmlStr;
+    }
+
+    /**
+     * 微信给出的 XXE漏洞方案
+     * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5
+     *
+     * @param strXML
+     * @return
+     * @throws Exception
+     */
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static Map doXMLParse2(String strXML) throws Exception {
+        Map<String, String> m = new HashMap<>();
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+        String FEATURE = null;
+        try {
+            FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
+            documentBuilderFactory.setFeature(FEATURE, true);
+
+            FEATURE = "http://xml.org/sax/features/external-general-entities";
+            documentBuilderFactory.setFeature(FEATURE, false);
+
+            FEATURE = "http://xml.org/sax/features/external-parameter-entities";
+            documentBuilderFactory.setFeature(FEATURE, false);
+
+            FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+            documentBuilderFactory.setFeature(FEATURE, false);
+
+            documentBuilderFactory.setXIncludeAware(false);
+            documentBuilderFactory.setExpandEntityReferences(false);
+        } catch (ParserConfigurationException e) {
+            e.printStackTrace();
+        }
+        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;
+                m.put(element.getNodeName(), element.getTextContent());
+            }
+        }
+        stream.close();
+        return m;
+    }
+}

+ 44 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/common/util/ZxingUtils.java

@@ -0,0 +1,44 @@
+package com.zanxiang.sdk.common.util;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * 二维码
+ */
+public class ZxingUtils {
+
+    static final String FORMAT = "png";
+
+    static final int height = 256;
+
+    /**
+     * 生成二维码
+     *
+     * @param qrCode
+     * @param imgPath
+     */
+    public static void createQRCodeImage(String qrCode, String imgPath) {
+        HashMap hashMap = new HashMap();
+        hashMap.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+        hashMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
+        hashMap.put(EncodeHintType.MARGIN, 2);
+        try {
+            BitMatrix bitMatrix = new MultiFormatWriter().encode(qrCode, BarcodeFormat.QR_CODE, height, height, hashMap);
+            MatrixToImageWriter.writeToPath(bitMatrix, FORMAT, new File(imgPath).toPath());
+        } catch (WriterException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 64 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/controller/PayController.java

@@ -0,0 +1,64 @@
+package com.zanxiang.sdk.controller;
+
+import com.alibaba.nacos.common.utils.IPUtil;
+import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.common.enums.PayWayEnum;
+import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
+import com.zanxiang.sdk.domain.params.ProductPayParam;
+import com.zanxiang.sdk.service.Impl.pay.PayService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.jdom.JDOMException;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 支付公共入口
+ *
+ * @author xufeng
+ * @date 2022/6/8 14:49
+ */
+@Api(tags = "支付公共入口")
+@RestController
+@RequestMapping(value = "/pay")
+public class PayController {
+
+    @Resource
+    private PayService payService;
+
+    @ApiOperation(value = "支付参数生成")
+    @GetMapping(value = "/create")
+    public ResultMap create(@RequestBody ProductPayParam product) {
+        String userId = "1";
+        ProductPayParamBO bo = new ProductPayParamBO();
+        bo.setUserId(userId);
+        bo.setPayDevice(product.getPayDevice());
+        bo.setSpbillCreateIp(IPUtil.localHostIP());
+        bo.setOutTradeNo(product.getOrderId());
+        bo.setPayWay(product.getPayWay());
+        return payService.payCreate(bo);
+    }
+
+    @ApiOperation(value = "支付宝支付异步回调(二维码、H5、网站)")
+    @RequestMapping(value = "notify", method = RequestMethod.POST)
+    public String alipayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {
+        return payService.notify(request, response, PayWayEnum.ALIPAY.getNum());
+    }
+
+    @ApiOperation(value = "微信支付")
+    @RequestMapping(value = "wxPayNotify", method = RequestMethod.POST)
+    public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {
+        return payService.notify(request, response, PayWayEnum.WXPAY.getNum());
+    }
+
+    @ApiOperation(value = "支付宝支付同步回调(二维码、H5、网站)")
+    @RequestMapping(value = "alipaySynNotify", method = RequestMethod.POST)
+    public ResultMap synNotify(HttpServletRequest request) {
+        return payService.synNotify(request, PayWayEnum.ALIPAY.getNum());
+    }
+
+}

+ 23 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/bo/ProductPayAttachParamBO.java

@@ -0,0 +1,23 @@
+package com.zanxiang.sdk.domain.bo;
+
+import lombok.Data;
+
+/**
+ * 支付调起生成相关参数
+ *
+ * @author xufeng
+ * @date 2022/6/8 15:21
+ */
+@Data
+public class ProductPayAttachParamBO {
+    private static final long serialVersionUID = 1L;
+
+    private String userId;
+
+    private String gamePayWayId;
+
+    private String orderId;
+
+    private Integer payWay;
+
+}

+ 44 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/bo/ProductPayParamBO.java

@@ -0,0 +1,44 @@
+package com.zanxiang.sdk.domain.bo;
+
+import lombok.Data;
+
+/**
+ * 支付调起生成相关参数
+ *
+ * @author xufeng
+ * @date 2022/6/8 15:21
+ */
+@Data
+public class ProductPayParamBO {
+    private static final long serialVersionUID = 1L;
+
+    private String gameId;
+
+    private String userId; //玩家id
+
+    private String productId;// 商品ID
+
+    private String subject;//订单名称
+
+    private String body;// 商品描述
+
+    private String totalFee;// 总金额(单位是分)
+
+    private String outTradeNo;// 订单号(唯一)
+
+    private String spbillCreateIp;// 发起人IP地址
+
+    private ProductPayAttachParamBO attach;// 附件数据主要用于商户携带订单的自定义数据
+
+    private Integer payDevice;// 支付方式 (1:PC,平板 2:Wap 3:App )
+
+    private Integer payWay;// 支付类型(1:支付宝 2:微信 3:银联)
+
+
+    //================以下非传项=================
+
+    private String frontUrl;// 前台回调地址  非扫码支付使用
+
+    private Object config; //支付相关配置
+
+}

+ 18 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/bo/WxPayConfigBO.java

@@ -0,0 +1,18 @@
+package com.zanxiang.sdk.domain.bo;
+
+import lombok.Data;
+
+/**
+ * @author xufeng
+ * @date 2022/6/14 10:58
+ */
+@Data
+public class WxPayConfigBO {
+    private String appId;
+    private String appSecret;
+    private String mchId;
+    private String apiKey;
+    private String signType;
+    private String certPath;
+    private String notifyUrl;
+}

+ 3 - 3
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/entity/GamePayWay.java

@@ -27,13 +27,13 @@ public class GamePayWay {
     /**
      * 主键id
      */
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private String id;
 
     /**
      * 游戏id
      */
-    private Long gameId;
+    private String gameId;
 
     /**
      * 支付方式id

+ 2 - 2
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/entity/UserLoginLog.java

@@ -56,7 +56,7 @@ public class UserLoginLog {
     /**
      * 游戏id
      */
-    private Long gameId;
+    private String gameId;
 
     /**
      * 登录时间
@@ -70,7 +70,7 @@ public class UserLoginLog {
      * @param user    : 用户信息
      * @param gameId  : 游戏id
      */
-    public UserLoginLog(String loginIp, User user, Long gameId) {
+    public UserLoginLog(String loginIp, User user, String gameId) {
         this.userId = user.getId();
         this.agentId = user.getAgentId();
         this.fromDevice = user.getFromDevice();

+ 2 - 1
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/params/CommonParam.java

@@ -11,7 +11,8 @@ public class CommonParam {
     private String token;
 
     @ApiModelProperty("游戏id")
-    private Long gameId;
+    @JsonAlias("app_id")
+    private String gameId;
 
     //==============device 相关===============
     @ApiModelProperty("设备id")

+ 33 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/domain/params/ProductPayParam.java

@@ -0,0 +1,33 @@
+package com.zanxiang.sdk.domain.params;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.sun.istack.NotNull;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 支付调起生成相关参数
+ *
+ * @author xufeng
+ * @date 2022/6/8 15:21
+ */
+@Data
+public class ProductPayParam extends CommonParam {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("订单id")
+    @JsonAlias("device-orderId")
+    @NotNull
+    private String orderId;
+
+    @ApiModelProperty("支付类型(1:支付宝 2:微信 3:银联)")
+    @JsonAlias("payway")
+    @NotNull
+    private Integer payWay;
+
+    @ApiModelProperty("支付方式 (1:PC,平板 2:Wap 3:App )")
+    @NotNull
+    private Integer payDevice;
+
+
+}

+ 27 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/GamePayWayService.java

@@ -0,0 +1,27 @@
+package com.zanxiang.sdk.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zanxiang.sdk.domain.entity.GamePayWay;
+
+/**
+ * @author xufeng
+ * @date 2022/6/13 11:27
+ */
+public interface GamePayWayService extends IService<GamePayWay> {
+    /**
+     * 根据id获取游戏支付方式详情
+     *
+     * @param id
+     * @return
+     */
+    GamePayWay getInfo(String id);
+
+    /**
+     * 根据游戏ID与支付方式获支付配置
+     *
+     * @param gameId
+     * @param payType
+     * @return
+     */
+    GamePayWay getPayWayToOrderPay(String gameId, Integer payType);
+}

+ 2 - 3
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/IUserLoginLogService.java

@@ -11,9 +11,8 @@ public interface IUserLoginLogService {
 
     /**
      * 添加用户登录日志
-     *
-     * @param user   : 用户信息
+     *  @param user   : 用户信息
      * @param gameId : 游戏id
      */
-    void addUserLoginLog(String realIp, User user, Long gameId);
+    void addUserLoginLog(String realIp, User user, String gameId);
 }

+ 45 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/GamePayWayServiceImpl.java

@@ -0,0 +1,45 @@
+package com.zanxiang.sdk.service.Impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zanxiang.common.enums.DeleteEnum;
+import com.zanxiang.common.enums.MaxPayLock;
+import com.zanxiang.common.utils.StringUtils;
+import com.zanxiang.sdk.domain.entity.GamePayWay;
+import com.zanxiang.sdk.mapper.GamePayWayMapper;
+import com.zanxiang.sdk.service.GamePayWayService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xufeng
+ * @date 2022/6/13 11:46
+ */
+@Component
+@Slf4j
+@Service
+public class GamePayWayServiceImpl extends ServiceImpl<GamePayWayMapper, GamePayWay> implements GamePayWayService {
+
+    @Override
+    public GamePayWay getInfo(String id) {
+        return getOne(new LambdaQueryWrapper<GamePayWay>().eq(GamePayWay::getId, id).eq(GamePayWay::getIsDelete, DeleteEnum.NO));
+    }
+
+    @Override
+    public GamePayWay getPayWayToOrderPay(String gameId, Integer payType) {
+        GamePayWay payInfo = getOne(new LambdaQueryWrapper<GamePayWay>()
+                .eq(GamePayWay::getGameId, gameId)
+                .eq(GamePayWay::getPayWayId, payType)
+                .eq(GamePayWay::getIsDelete, DeleteEnum.NO.getCode())
+                .eq(GamePayWay::getStatus, DeleteEnum.NO.getCode())
+                .eq(GamePayWay::getMaxPayLock, MaxPayLock.UNLOCK.getCode())
+                .orderBy(true, true, GamePayWay::getCreateTime));
+        if (payInfo == null)
+            throw new RuntimeException("暂无该游戏支付方式 gameId:" + gameId + " payType:" + payType);
+
+        if (StringUtils.isEmpty(payInfo.getPayConfig()))
+            throw new RuntimeException("游戏支付方式为空 gameId:" + gameId + " payType:" + payType);
+        return payInfo;
+    }
+}

+ 6 - 5
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/PlatformOrderServiceImpl.java

@@ -1,8 +1,6 @@
 package com.zanxiang.sdk.service.Impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zanxiang.common.enums.OrderStateEnum;
 import com.zanxiang.common.utils.StringUtils;
@@ -20,7 +18,6 @@ import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.time.LocalDateTime;
 import java.util.Date;
 
 /**
@@ -60,6 +57,10 @@ public class PlatformOrderServiceImpl extends ServiceImpl<OrderMapper, Order> im
         try {
             this.checkParam(platformOrderBO, "pay");
             Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getId, platformOrderBO.getId()));
+            //已支付情况,直接返回成功
+            if (!order.getStatus().equals(OrderStateEnum.SUCCESS.getCode())) {
+                return true;
+            }
             if (!order.getStatus().equals(OrderStateEnum.NO_PAY.getCode())) {
                 throw new ParameterResolutionException("订单状态非待支付");
             }
@@ -102,8 +103,8 @@ public class PlatformOrderServiceImpl extends ServiceImpl<OrderMapper, Order> im
     }
 
     @Override
-    public PlatformOrderDTO info(String id) {
-        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getId, id));
+    public PlatformOrderDTO info(String id, String userId) {
+        Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getId, id).eq(Order::getUserId, userId));
         return BeanUtils.copy(order, PlatformOrderDTO.class);
     }
 

+ 1 - 1
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/UserLoginLogServiceImpl.java

@@ -24,7 +24,7 @@ public class UserLoginLogServiceImpl extends ServiceImpl<UserLoginLogMapper, Use
      * @param gameId : 游戏id
      */
     @Override
-    public void addUserLoginLog(String realIp, User user, Long gameId) {
+    public void addUserLoginLog(String realIp, User user, String gameId) {
         UserLoginLog userLoginLog = new UserLoginLog(realIp, user, gameId);
         super.save(userLoginLog);
     }

+ 232 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/pay/AlipayServiceImpl.java

@@ -0,0 +1,232 @@
+package com.zanxiang.sdk.service.Impl.pay;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.TypeReference;
+import com.alipay.easysdk.factory.Factory;
+import com.alipay.easysdk.kernel.Config;
+import com.alipay.easysdk.kernel.util.ResponseChecker;
+import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
+import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
+import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
+import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.common.enums.OrderStateEnum;
+import com.zanxiang.common.enums.ResEnum;
+import com.zanxiang.common.utils.URIUtil;
+import com.zanxiang.sdk.domain.bo.PlatformOrderBO;
+import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
+import com.zanxiang.sdk.service.OrderPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * @author xufeng
+ * @date 2022/6/8 15:37
+ */
+@Component
+@Slf4j
+@Service
+public class AlipayServiceImpl extends PayService implements OrderPayService {
+    private static final Logger logger = LoggerFactory.getLogger(AlipayServiceImpl.class);
+
+    private String returnUrl = "http://localhost/pay/notify";
+
+    /**
+     * 支付调起
+     *
+     * @param product
+     * @return
+     */
+    @Override
+    public ResultMap create(ProductPayParamBO product) {
+        this.attach = product.getAttach();
+        configInit(product.getConfig());
+        Map<String, String> attachMap = JSON.parseObject(JSON.toJSONString(this.attach), new TypeReference<Map<String, String>>() {
+        });
+        this.returnUrl = URIUtil.fillUrlParams(this.returnUrl, attachMap, true);
+
+        switch (product.getPayDevice()) {
+            case 1:
+                return this.pc(product);
+            case 2:
+                return this.mobile(product);
+            case 3:
+                return this.app(product);
+            default:
+                throw new RuntimeException("未知支付方式");
+        }
+    }
+
+    /**
+     * 异步回调
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    @Override
+    public String notify(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            Map<String, String> params = new HashMap<>();
+            Enumeration<String> parameterNames = request.getParameterNames();
+            while (parameterNames.hasMoreElements()) {
+                String parameterName = parameterNames.nextElement();
+                params.put(parameterName, request.getParameter(parameterName));
+            }
+            String outTradeNo = request.getParameter("out_trade_no");
+            Boolean flag = Factory.Payment.Common().verifyNotify(params);
+            if (flag) {
+                PlatformOrderBO bo = new PlatformOrderBO();
+                bo.setId(outTradeNo);
+                bo.setUserId(request.getParameter("user_id"));
+                bo.setRealAmount(new BigDecimal(request.getParameter("totalFee")));
+                bo.setMerchantOrderNo(request.getParameter("trade_no"));
+                bo.setStatus(OrderStateEnum.SUCCESS.getCode());
+                bo.setGamePaywayId(request.getParameter("gamePayWayId"));
+                bo.setPayTime(new Date());
+                Boolean result = platformOrderService.pay(bo);
+                if (result) {
+                    logger.info("支付宝异步回调成功 request:{},商户订单号为:{}", request, outTradeNo);
+                    return ResEnum.SUCCESS.getMsg();
+                }
+            }
+            logger.error("支付宝异步回调-验证签名失败 request:{}", request);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return ResEnum.FAIL.getMsg();
+    }
+
+    /**
+     * 同步回调
+     *
+     * @param request
+     * @return
+     */
+    @Override
+    public ResultMap synNotify(HttpServletRequest request) {
+        try {
+            //获取支付宝GET过来反馈信息
+            Map<String, String> params = new HashMap<>();
+            Map<String, String[]> requestParams = request.getParameterMap();
+            for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
+                String name = iter.next();
+                String[] values = requestParams.get(name);
+                String valueStr = "";
+                for (int i = 0; i < values.length; i++) {
+                    valueStr = (i == values.length - 1) ? valueStr + values[i]
+                            : valueStr + values[i] + ",";
+                }
+                //乱码解决,这段代码在出现乱码时使用
+                valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
+                params.put(name, valueStr);
+            }
+            //商户订单号
+            String orderNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
+            //前台回调验证签名 v1 or v2
+            Boolean flag = Factory.Payment.Common().verifyNotify(params);
+            if (flag) {
+                logger.info("订单号" + orderNo + "验证签名结果[成功].");
+                //处理业务逻辑
+            } else {
+                logger.info("订单号" + orderNo + "验证签名结果[失败].");
+            }
+            return ResultMap.ok("success");
+        } catch (Exception e) {
+            e.printStackTrace();
+            //处理异常信息
+            return ResultMap.error(e.getMessage());
+        }
+        //支付成功、跳转到成功页面
+        //return "success.html";
+    }
+
+    /**
+     * 手机支付
+     *
+     * @param product
+     * @return
+     */
+    private ResultMap mobile(ProductPayParamBO product) {
+        try {
+            logger.info("mobile端支付生成,请求参数 bo:{}", product);
+            AlipayTradeWapPayResponse response = Factory.Payment
+                    .Wap()
+                    .pay(JSONObject.toJSONString(this.attach), product.getOutTradeNo(), product.getTotalFee(), "", this.returnUrl);
+            logger.info("mobile端支付生成,result:{}", response);
+            if (ResponseChecker.success(response)) {
+                return ResultMap.ok(response.getBody());
+            } else {
+                return ResultMap.ok(ResEnum.FAIL.getMsg());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return ResultMap.error(ResEnum.FAIL.getMsg());
+        }
+    }
+
+    /**
+     * 电脑支付
+     *
+     * @param product
+     * @return
+     */
+    private ResultMap pc(ProductPayParamBO product) {
+        try {
+            logger.info("pc端支付生成,请求参数 bo:{}", product);
+            AlipayTradePagePayResponse response = Factory.Payment
+                    .Page()
+                    .pay(product.getSubject(), product.getOutTradeNo(), product.getTotalFee(), this.returnUrl);
+            logger.info("pc端支付生成,result:{}", response);
+            if (ResponseChecker.success(response)) {
+                return ResultMap.ok(response.getBody());
+            } else {
+                return ResultMap.error(ResEnum.FAIL.getMsg());
+            }
+        } catch (Exception e) {
+            logger.error("pc端支付生成异常 e:{}", e);
+            e.printStackTrace();
+            return ResultMap.error(ResEnum.FAIL.getMsg());
+        }
+    }
+
+    /**
+     * app支付
+     *
+     * @param product
+     * @return
+     */
+    private ResultMap app(ProductPayParamBO product) {
+        try {
+            AlipayTradeAppPayResponse response = Factory.Payment
+                    .App()
+                    .pay(product.getSubject(), product.getOutTradeNo(), product.getTotalFee());
+            if (ResponseChecker.success(response)) {
+                return ResultMap.ok(response.getBody());
+            } else {
+                return ResultMap.error(ResEnum.FAIL.getMsg());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error("app端支付生成异常 e:{}", e);
+            return ResultMap.error(ResEnum.FAIL.getMsg());
+        }
+    }
+
+    /**
+     * 配置初始化
+     */
+    public void configInit(Object obj) {
+        Config config = JSONObject.parseObject(obj.toString(), Config.class);
+        Factory.setOptions(config);
+    }
+
+}

+ 134 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/pay/PayService.java

@@ -0,0 +1,134 @@
+package com.zanxiang.sdk.service.Impl.pay;
+
+import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.common.enums.PayWayEnum;
+import com.zanxiang.common.exception.BaseException;
+import com.zanxiang.sdk.domain.bo.ProductPayAttachParamBO;
+import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
+import com.zanxiang.sdk.domain.dto.PlatformOrderDTO;
+import com.zanxiang.sdk.domain.entity.GamePayWay;
+import com.zanxiang.sdk.service.GamePayWayService;
+import com.zanxiang.sdk.service.OrderPayService;
+import com.zanxiang.sdk.service.PlatformOrderService;
+import lombok.extern.slf4j.Slf4j;
+import org.jdom.JDOMException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 支付调用
+ *
+ * @author xufeng
+ * @date 2022/6/8 16:45
+ */
+@Slf4j
+@Service
+@Configurable
+public class PayService {
+    private static final Logger logger = LoggerFactory.getLogger(PayService.class);
+
+    @Autowired
+    public PlatformOrderService platformOrderService;
+
+    @Autowired
+    public GamePayWayService gamePayWayService;
+
+    /**
+     * 自定义参数
+     */
+    protected ProductPayAttachParamBO attach;
+
+    /**
+     * 创建订单支付参数
+     *
+     * @param product
+     */
+    public ResultMap payCreate(ProductPayParamBO product) {
+        try {
+            //获取订单信息
+            PlatformOrderDTO info = platformOrderService.info(product.getOutTradeNo(), product.getUserId());
+            if (info == null)
+                throw new RuntimeException("订单不存在");
+
+            //根据订单中游戏id,获取游戏的支付方式
+            GamePayWay payInfo = gamePayWayService.getPayWayToOrderPay(info.getGameId(), product.getPayWay());
+
+            product.setProductId(info.getProductId());
+            product.setSubject(info.getProductName());
+            product.setGameId(info.getGameId());
+            product.setTotalFee(String.valueOf(info.getAmount()));
+            product.setOutTradeNo(info.getId());
+            product.setFrontUrl("");
+            product.setConfig(payInfo.getPayConfig());
+            ProductPayAttachParamBO attachBO = new ProductPayAttachParamBO();
+            attachBO.setUserId(product.getUserId());
+            attachBO.setOrderId(product.getOutTradeNo());
+            attachBO.setPayWay(product.getPayWay());
+            attachBO.setGamePayWayId(payInfo.getId());
+            product.setAttach(attachBO);
+            String payCode = PayWayEnum.getCodeByNum(product.getPayWay());
+            OrderPayService device = getDevice(payCode);
+            ResultMap result = device.create(product);
+            logger.info("订单支付参数生成 ProductPayParamBO:{},result:{}", product, result);
+            return result;
+        } catch (RuntimeException e) {
+            e.printStackTrace();
+            logger.info("订单支付参数生成异常 ProductPayParamBO:{},e:{}", product, e);
+            return ResultMap.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 支付异步回调
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    public String notify(HttpServletRequest request, HttpServletResponse response, Integer payType) throws IOException, JDOMException {
+        String payCode = PayWayEnum.getCodeByNum(payType);
+        OrderPayService device = getDevice(payCode);
+        return device.notify(request, response);
+    }
+
+    /**
+     * 同步消息通知
+     *
+     * @param request
+     * @return
+     */
+    public ResultMap synNotify(HttpServletRequest request, Integer payType) {
+        String payCode = PayWayEnum.getCodeByNum(payType);
+        OrderPayService device = getDevice(payCode);
+        return device.synNotify(request);
+    }
+
+    /**
+     * 获取支付驱动方式
+     *
+     * @param payCode
+     * @return
+     */
+    private static OrderPayService getDevice(String payCode) {
+        if (payCode.equalsIgnoreCase("Alipay"))
+            return new AlipayServiceImpl();
+        else if (payCode.equalsIgnoreCase("Wxpay"))
+            return new WxpayServiceImpl();
+        else
+            throw new BaseException("支付方式不存在");
+    }
+
+//    protected GamePayWay getPayInfo(String gameId, Integer payWay) {
+//        //根据订单中游戏id,获取游戏的支付方式
+//        GamePayWay payInfo = gamePayWayService.getPayWayToOrderPay(gameId, payWay);
+//        this.attach.setGamePayWayId(payInfo.getId());
+//        return payInfo;
+//    }
+}

+ 323 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/Impl/pay/WxpayServiceImpl.java

@@ -0,0 +1,323 @@
+package com.zanxiang.sdk.service.Impl.pay;
+
+import com.alibaba.fastjson.JSONObject;
+import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.common.enums.OrderStateEnum;
+import com.zanxiang.common.enums.ResEnum;
+import com.zanxiang.common.utils.RandomStringUtil;
+import com.zanxiang.sdk.common.util.*;
+import com.zanxiang.sdk.domain.bo.PlatformOrderBO;
+import com.zanxiang.sdk.domain.bo.ProductPayAttachParamBO;
+import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
+import com.zanxiang.sdk.domain.bo.WxPayConfigBO;
+import com.zanxiang.sdk.domain.entity.GamePayWay;
+import com.zanxiang.sdk.service.GamePayWayService;
+import com.zanxiang.sdk.service.OrderPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.jdom.JDOMException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+import weixin.popular.api.SnsAPI;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * @author xufeng
+ * @date 2022/6/8 15:37
+ */
+@Component
+@Slf4j
+@Service
+public class WxpayServiceImpl extends PayService implements OrderPayService {
+    private static final Logger logger = LoggerFactory.getLogger(WxpayServiceImpl.class);
+
+    @Autowired
+    private GamePayWayService gamePayWayService;
+
+    private WxPayConfigBO config;
+
+    private WxPayUtil wxPayUtil;
+
+    private String serverUrl = "http://localhost/";
+
+    private String notifyUrl = "http://localhost/";
+
+    private String filePath = "/tmp/wxpay/";
+
+    private String body;
+    private String totalFee;
+    private String outTradeNo;
+
+    /**
+     * 自定义参数
+     */
+    private ProductPayAttachParamBO attach;
+
+
+    /**
+     * 支付调起
+     *
+     * @param product
+     * @return
+     */
+    @Override
+    public ResultMap create(ProductPayParamBO product) {
+        System.out.println("WxpayService payCreate");
+
+        this.body = product.getSubject();
+        this.totalFee = String.valueOf(Float.parseFloat(product.getTotalFee()) * 100);
+        this.outTradeNo = product.getOutTradeNo() + RandomStringUtil.randomNumStr(5);
+        this.attach = product.getAttach();
+
+        this.configInit(product.getConfig());
+
+        switch (product.getPayWay()) {
+            case 1:
+                return this.pc(product);
+//                return this.mobile(product, "NATIVE");
+            case 2:
+            case 3:
+                return this.mobile(product, "MWEB");
+            case 4:
+                return this.mp(product);
+            default:
+                throw new RuntimeException("未知支付方式");
+        }
+    }
+
+    /**
+     * 异步回调
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    @Override
+    public String notify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {
+        // 读取参数
+        InputStream inputStream = request.getInputStream();
+        StringBuffer sb = new StringBuffer();
+        String s;
+        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+        while ((s = in.readLine()) != null) {
+            sb.append(s);
+        }
+        in.close();
+        inputStream.close();
+        logger.info("微信支付回调 body:{}", sb);
+        // 解析xml成map
+        Map<String, String> m = XMLUtil.doXMLParse(sb.toString());
+        // 过滤空 设置 TreeMap
+        SortedMap<Object, Object> packageParams = new TreeMap<>();
+        Iterator it = m.keySet().iterator();
+        while (it.hasNext()) {
+            String parameter = (String) it.next();
+            String parameterValue = m.get(parameter);
+
+            String v = "";
+            if (null != parameterValue) {
+                v = parameterValue.trim();
+            }
+            packageParams.put(parameter, v);
+        }
+        ProductPayAttachParamBO attachBO = this.decodeAttach(packageParams.get("attach"));
+        GamePayWay payInfo = gamePayWayService.getInfo(attachBO.getGamePayWayId());
+        this.config = JSONObject.parseObject(payInfo.getPayConfig(), WxPayConfigBO.class);
+        // 账号信息
+        String key = config.getApiKey(); // key
+        // 判断签名是否正确
+        if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {
+            logger.info("微信支付成功回调");
+            // ------------------------------
+            // 处理业务开始
+            // ------------------------------
+            String resXml;
+            if ("SUCCESS".equals(packageParams.get("result_code"))) {
+                // 这里是支付成功
+                String orderNo = (String) packageParams.get("out_trade_no");
+                logger.info("微信订单号{}付款成功", orderNo);
+                PlatformOrderBO bo = new PlatformOrderBO();
+                bo.setId(attachBO.getOrderId());
+                bo.setUserId(attachBO.getUserId());
+                bo.setRealAmount(new BigDecimal(packageParams.get("total_amount").toString()));
+                bo.setMerchantOrderNo(request.getParameter("trade_no"));
+                bo.setStatus(OrderStateEnum.SUCCESS.getCode());
+                bo.setGamePaywayId(attachBO.getGamePayWayId());
+                bo.setPayTime(new Date());
+                Boolean result = platformOrderService.pay(bo);
+                if (result) {
+                    logger.info("支付宝异步回调成功 request:{},商户订单号为:{}", request, outTradeNo);
+                    return ResEnum.SUCCESS.getMsg();
+                }
+                // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
+                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
+            } else {
+                logger.info("支付失败,错误信息:{}", packageParams.get("err_code"));
+                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
+            }
+            // ------------------------------
+            // 处理业务完毕
+            // ------------------------------
+            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
+            out.write(resXml.getBytes());
+            out.flush();
+            out.close();
+        } else {
+            logger.info("通知签名验证失败");
+        }
+        return null;
+    }
+
+    /**
+     * 同步回调
+     *
+     * @param request
+     * @return
+     */
+
+    @Override
+    public ResultMap synNotify(HttpServletRequest request) {
+        logger.info("微信支付无同步回调 request:{}", request);
+        return null;
+    }
+
+
+    /**
+     * pc支付
+     *
+     * @param product
+     * @return
+     */
+    public ResultMap pc(ProductPayParamBO product) {
+        //商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改 加入回调URL
+        SortedMap<Object, Object> packageParams = new TreeMap<>();
+        //封装通用参数
+        wxPayUtil.commonParams(packageParams);
+        packageParams.put("attach", this.attach);
+        //生成签名
+        String sign = PayCommonUtil.createSign("UTF-8", packageParams, config.getApiKey());
+        //组装二维码信息
+        StringBuffer qrCode = new StringBuffer();
+        qrCode.append("weixin://wxpay/bizpayurl?");
+        qrCode.append("appid=" + config.getAppId());
+        qrCode.append("&mch_id=" + config.getMchId());
+        qrCode.append("&nonce_str=" + packageParams.get("nonce_str"));
+//        qrCode.append("&attach=" + this.attach);
+        qrCode.append("&out_trade_no=" + this.outTradeNo);
+        qrCode.append("&time_stamp=" + packageParams.get("time_stamp"));
+        qrCode.append("&sign=" + sign);
+        String imgName = this.outTradeNo + ".png";
+        String imgPath = filePath + imgName;
+        logger.info("小程序支付调起,请求参数:qrCode:{}, imgPath:{}", qrCode, imgPath);
+        ZxingUtils.createQRCodeImage(qrCode.toString(), imgPath);
+        return ResultMap.ok(imgPath);
+    }
+
+
+    /**
+     * 小程序(需要HTTPS)
+     *
+     * @param product
+     * @return
+     */
+    public ResultMap mp(ProductPayParamBO product) {
+        //redirect_uri 需要在微信支付端添加认证网址
+        totalFee = wxPayUtil.subZeroAndDot(this.totalFee);
+        String redirect_uri = serverUrl + "weixinMobile/dopay?outTradeNo=" + product.getOutTradeNo() + "&totalFee=" + totalFee;
+        //也可以通过state传递参数 redirect_uri 后面加参数未经过验证
+        String result = SnsAPI.connectOauth2Authorize(config.getAppId(), redirect_uri, true, null);
+        return ResultMap.ok(result);
+    }
+
+    /**
+     * H5手机支付(jspapi)
+     *
+     * @param product
+     * @return
+     */
+    public ResultMap mobile(ProductPayParamBO product, String trade_type) {
+        logger.info("订单号:{}生成微信支付码", product.getOutTradeNo());
+        try {
+            // 账号信息
+            String key = config.getApiKey();
+//            String trade_type = "NATIVE";// 交易类型 原生扫码支付
+//            String trade_type = "MWEB";// 交易类型 原生扫码支付
+            SortedMap<Object, Object> packageParams = new TreeMap<>();
+            wxPayUtil.commonParams(packageParams);
+            packageParams.put("body", this.body);// 商品描述
+            packageParams.put("out_trade_no", this.outTradeNo);// 商户订单号
+            totalFee = wxPayUtil.subZeroAndDot(this.totalFee);
+            packageParams.put("total_fee", totalFee);// 总金额
+            packageParams.put("spbill_create_ip", product.getSpbillCreateIp());// 发起人IP地址
+            packageParams.put("notify_url", notifyUrl);// 回调地址
+            packageParams.put("trade_type", trade_type);// 交易类型
+            packageParams.put("attach", JSONObject.toJSONString(this.attach));
+
+//            if (trade_type.equals("MWEB")) {
+//                //H5支付专用
+//                JSONObject value = new JSONObject();
+//                value.put("type", "WAP");
+//                value.put("wap_url", "https://localhost");////WAP网站URL地址
+//                value.put("wap_name", "111");//WAP 网站名
+//                JSONObject scene_info = new JSONObject();
+//                scene_info.put("h5_info", value);
+//                packageParams.put("scene_info", scene_info.toString());
+//            }
+            String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
+            packageParams.put("sign", sign);// 签名
+            logger.info("订单号:{} 加密请求参数 packageParams:{}", product.getOutTradeNo(), packageParams);
+            String requestXML = PayCommonUtil.getRequestXml(packageParams);
+            String resXml = HttpUtil.postData(WxPayUrl.UNIFIED_ORDER_URL, requestXML);
+            Map map = XMLUtil.doXMLParse(resXml);
+            logger.info("订单号:{} 微信响应 map:{}", product.getOutTradeNo(), map);
+            String returnCode = (String) map.get("return_code");
+            if ("SUCCESS".equals(returnCode)) {
+                String resultCode = (String) map.get("result_code");
+                if ("SUCCESS".equals(resultCode)) {
+                    logger.info("订单号:{}生成微信支付码成功", product.getOutTradeNo());
+                    String urlCode = trade_type.equals("MWEB") ? (String) map.get("mweb_url") : (String) map.get("code_url");
+                    String imgName = this.outTradeNo + ".png";
+                    String imgPath = filePath + imgName;
+                    ZxingUtils.createQRCodeImage(urlCode, imgPath);
+                    return ResultMap.ok(imgPath);
+                } else {
+                    String errCodeDes = (String) map.get("err_code_des");
+                    logger.error("订单号:{}生成微信支付码(系统)失败:{}", product.getOutTradeNo(), errCodeDes);
+                    return ResultMap.error(errCodeDes);
+                }
+            } else {
+                String returnMsg = (String) map.get("return_msg");
+                logger.error("(订单号:{}生成微信支付码(通信)失败:{}", product.getOutTradeNo(), returnMsg);
+                return ResultMap.error(returnMsg);
+            }
+        } catch (Exception e) {
+            logger.error("订单号:{}生成微信支付码失败(系统异常))", product.getOutTradeNo(), e);
+            return ResultMap.error();
+        }
+    }
+
+    /**
+     * 配置初始化
+     */
+    public void configInit(Object obj) {
+        this.config = JSONObject.parseObject(obj.toString(), WxPayConfigBO.class);
+        this.wxPayUtil = new WxPayUtil(this.config);
+    }
+
+    /**
+     * 反解析自定义参数
+     *
+     * @param obj
+     * @return
+     */
+    private ProductPayAttachParamBO decodeAttach(Object obj) {
+        return JSONObject.parseObject(obj.toString(), ProductPayAttachParamBO.class);
+    }
+}

+ 21 - 0
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/OrderPayService.java

@@ -0,0 +1,21 @@
+package com.zanxiang.sdk.service;
+
+import com.zanxiang.common.domain.ResultMap;
+import com.zanxiang.sdk.domain.bo.ProductPayParamBO;
+import org.jdom.JDOMException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author xufeng
+ * @date 2022/6/8 15:31
+ */
+public interface OrderPayService {
+    ResultMap create(ProductPayParamBO product);
+
+    String notify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException;
+
+    ResultMap synNotify(HttpServletRequest request);
+}

+ 2 - 1
game-module/game-sdk/src/main/java/com/zanxiang/sdk/service/PlatformOrderService.java

@@ -42,7 +42,8 @@ public interface PlatformOrderService extends IService<Order> {
      * 单一订单详情获取
      *
      * @param id
+     * @param userId
      * @return PlatformOrderDTO
      */
-    PlatformOrderDTO info(String id);
+    PlatformOrderDTO info(String id, String userId);
 }

+ 2 - 2
game-module/game-sdk/src/main/resources/bootstrap.yml

@@ -52,5 +52,5 @@ ali-sms:
 logging:
   level:
     root: warn
-    com.zanxiang.manage: debug
-    com.zanxiang.manage.mapper: debug
+    com.zanxiang.sdk: debug
+    com.zanxiang.sdk.mapper: debug

+ 3 - 3
game-module/game-sdk/src/test/java/com/zanxiang/sdk/OrderTests.java

@@ -1,13 +1,13 @@
 package com.zanxiang.sdk;
 
-import java.math.BigDecimal;
-
 import com.zanxiang.sdk.domain.bo.PlatformOrderBO;
 import com.zanxiang.sdk.domain.dto.PlatformOrderDTO;
 import com.zanxiang.sdk.service.PlatformOrderService;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.math.BigDecimal;
+
 /**
  * 订单模块单元测试类
  *
@@ -22,7 +22,7 @@ public class OrderTests extends SDKApplicationTests {
 
     @Test
     void getOrderInfo() {
-        PlatformOrderDTO info = platformOrderService.info("1");
+        PlatformOrderDTO info = platformOrderService.info("1", "1");
         System.out.println(info);
     }