From e9ad3ca0104dc5caac3db45374c8ae97168ee53b Mon Sep 17 00:00:00 2001
From: xin <1099200748@qq.com>
Date: Thu, 12 Jun 2025 21:31:29 +0800
Subject: [PATCH] 支付

---
 oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtils.java                   |  110 +++++
 oying-system/src/main/java/com/oying/modules/hwc/rest/SwiftPassController.java          |   38 +
 oying-system/src/main/java/com/oying/modules/hwc/utils/XmlUtils.java                    |  138 ++++++
 oying-system/src/main/java/com/oying/modules/hwc/domain/HwcResponse.java                |   30 +
 oying-common/src/main/java/com/oying/utils/enums/PayTypeEnum.java                       |   38 +
 pom.xml                                                                                 |    7 
 oying-system/src/main/java/com/oying/modules/hwc/service/impl/SwiftPassServiceImpl.java |  395 +++++++++++++++++++
 oying-common/src/main/java/com/oying/utils/enums/PayReturnStateEnum.java                |   44 ++
 oying-system/src/main/java/com/oying/modules/hwc/service/SwiftPassService.java          |   51 ++
 oying-common/src/main/java/com/oying/utils/enums/PayStateEnum.java                      |   52 ++
 oying-system/src/main/java/com/oying/modules/hwc/utils/MD5.java                         |   56 ++
 oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtil.java                    |   90 ++++
 oying-system/src/main/java/com/oying/modules/hwc/utils/RSAUtil.java                     |  104 +++++
 13 files changed, 1,153 insertions(+), 0 deletions(-)

diff --git a/oying-common/src/main/java/com/oying/utils/enums/PayReturnStateEnum.java b/oying-common/src/main/java/com/oying/utils/enums/PayReturnStateEnum.java
new file mode 100644
index 0000000..7983114
--- /dev/null
+++ b/oying-common/src/main/java/com/oying/utils/enums/PayReturnStateEnum.java
@@ -0,0 +1,44 @@
+package com.oying.utils.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author xin
+ * @description 退款状态
+ * @date 2024/10/25 下午4:35
+ */
+@Getter
+@AllArgsConstructor
+public enum PayReturnStateEnum {
+
+    SUCCESS("SUCCESS","退款成功"),
+
+    CLOSED("CLOSED","退款关闭"),
+
+    ABNORMAL("ABNORMAL","退款异常"),
+
+    UNKNOWN("UNKNOWN", "未知枚举");
+
+    private final String key;
+
+    private final String value;
+
+    public static PayReturnStateEnum find(String val) {
+        for (PayReturnStateEnum value : PayReturnStateEnum.values()) {
+            if (val.equals(value.getKey())) {
+                return value;
+            }
+        }
+        return UNKNOWN;
+    }
+
+    public static String getValue(String val) {
+        for (PayReturnStateEnum value : PayReturnStateEnum.values()) {
+            if (val.equals(value.getKey())) {
+                return value.getValue();
+            }
+        }
+        return UNKNOWN.getValue();
+    }
+}
diff --git a/oying-common/src/main/java/com/oying/utils/enums/PayStateEnum.java b/oying-common/src/main/java/com/oying/utils/enums/PayStateEnum.java
new file mode 100644
index 0000000..1f46015
--- /dev/null
+++ b/oying-common/src/main/java/com/oying/utils/enums/PayStateEnum.java
@@ -0,0 +1,52 @@
+package com.oying.utils.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author xin
+ * @description 支付状态
+ * @date 2024/10/25 下午4:09
+ */
+@Getter
+@AllArgsConstructor
+public enum PayStateEnum {
+
+    SUCCESS("SUCCESS", "支付成功"),
+
+    REFUND("REFUND", "转入退款"),
+
+    NOTPAY("NOTPAY", "未支付"),
+
+    CLOSED("CLOSED", "已关闭"),
+
+    REVOKED("REVOKED", "已撤销"),
+
+    USERPAYING("USERPAYING", "用户支付中"),
+
+    PAYERROR("PAYERROR", "支付失败"),
+
+    UNKNOWN("UNKNOWN", "未知枚举");
+
+    private final String key;
+
+    private final String value;
+
+    public static PayStateEnum find(String val) {
+        for (PayStateEnum value : PayStateEnum.values()) {
+            if (val.equals(value.getKey())) {
+                return value;
+            }
+        }
+        return UNKNOWN;
+    }
+
+    public static String getValue(String val) {
+        for (PayStateEnum value : PayStateEnum.values()) {
+            if (val.equals(value.getKey())) {
+                return value.getValue();
+            }
+        }
+        return UNKNOWN.getValue();
+    }
+}
diff --git a/oying-common/src/main/java/com/oying/utils/enums/PayTypeEnum.java b/oying-common/src/main/java/com/oying/utils/enums/PayTypeEnum.java
new file mode 100644
index 0000000..640c57a
--- /dev/null
+++ b/oying-common/src/main/java/com/oying/utils/enums/PayTypeEnum.java
@@ -0,0 +1,38 @@
+package com.oying.utils.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author xin
+ * @description 支付类型
+ * @date 2024/10/25 下午4:09
+ */
+@Getter
+@AllArgsConstructor
+public enum PayTypeEnum {
+
+    /* 通过邮箱重置密码 */
+    HWC("HWC", "汇旺财");
+
+    private final String key;
+    private final String value;
+
+    public static PayTypeEnum find(String val) {
+        for (PayTypeEnum value : PayTypeEnum.values()) {
+            if (val.equals(value.getKey())) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    public static String getValue(String val) {
+        for (PayTypeEnum value : PayTypeEnum.values()) {
+            if (val.equals(value.getKey())) {
+                return value.getValue();
+            }
+        }
+        return null;
+    }
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/domain/HwcResponse.java b/oying-system/src/main/java/com/oying/modules/hwc/domain/HwcResponse.java
new file mode 100644
index 0000000..83b1942
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/domain/HwcResponse.java
@@ -0,0 +1,30 @@
+package com.oying.modules.hwc.domain;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author xin
+ * @description
+ * @date 2025/1/23 下午9:56
+ */
+@Getter
+@Setter
+public class HwcResponse {
+
+    @JSONField(name = "appId")
+    private String appId;
+    @JSONField(name = "timeStamp")
+    private String timeStamp;
+    @JSONField(name = "nonceStr")
+    private String nonceStr;
+    @JSONField(name = "package")
+    private String packageVal;
+    @JSONField(name = "signType")
+    private String signType;
+    @JSONField(name = "paySign")
+    private String paySign;
+    @JSONField(name = "status")
+    private String status;
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/rest/SwiftPassController.java b/oying-system/src/main/java/com/oying/modules/hwc/rest/SwiftPassController.java
new file mode 100644
index 0000000..f544bdb
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/rest/SwiftPassController.java
@@ -0,0 +1,38 @@
+package com.oying.modules.hwc.rest;
+
+import com.oying.annotation.rest.AnonymousPostMapping;
+import com.oying.modules.hwc.service.SwiftPassService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author xin
+ * @description
+ * @date 2025/1/24 下午1:01
+ */
+@RestController
+@RequiredArgsConstructor
+@Api(tags = "HWS:汇旺财回调")
+@RequestMapping("/api/swiftPass")
+public class SwiftPassController {
+
+    private final SwiftPassService swiftPassService;
+
+    @ApiOperation("HWS支付回调")
+    @AnonymousPostMapping(value = "/alipayCallback")
+    public void alipayCallback(HttpServletRequest request, HttpServletResponse response) {
+        swiftPassService.alipayCallback(request, response);
+    }
+
+    @ApiOperation("HWS退款回调")
+    @AnonymousPostMapping(value = "/returnNotify")
+    public void returnNotify(HttpServletRequest request, HttpServletResponse response) {
+        swiftPassService.returnNotify(request, response);
+    }
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/service/SwiftPassService.java b/oying-system/src/main/java/com/oying/modules/hwc/service/SwiftPassService.java
new file mode 100644
index 0000000..9083daa
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/service/SwiftPassService.java
@@ -0,0 +1,51 @@
+package com.oying.modules.hwc.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.oying.modules.hwc.domain.HwcResponse;
+import com.oying.utils.enums.PayTypeEnum;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotNull;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * @author xin
+ * @description
+ * @date 2025/1/23 下午3:55
+ */
+public interface SwiftPassService {
+
+    /**
+     * <一句话功能简述>
+     * <功能详细描述>支付请求
+     *
+     * @see [类、类#方法、类#成员]
+     */
+    HwcResponse pay(String ip, Integer total, String timeExpire, String description, String openId,
+                    String orderNum, PayTypeEnum status) throws IOException;
+
+    /**
+     * <一句话功能简述>
+     * <功能详细描述>订单查询
+     *
+     * @see [类、类#方法、类#成员]
+     */
+    JSONObject query(String orderNum, PayTypeEnum status) throws IOException;
+
+    void closeOrder(String outTradeNo, PayTypeEnum status) throws IOException;
+
+    /**
+     * <一句话功能简述>
+     * <功能详细描述>退款
+     *
+     * @see [类、类#方法、类#成员]
+     */
+    Map<String, String> refund(String returnNum, String orderNum, @NotNull(message = "备注不能为空") String reason,
+                               long refund, long total, PayTypeEnum payType) throws IOException;
+
+    void alipayCallback(HttpServletRequest request, HttpServletResponse response);
+
+    void returnNotify(HttpServletRequest request, HttpServletResponse response);
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/service/impl/SwiftPassServiceImpl.java b/oying-system/src/main/java/com/oying/modules/hwc/service/impl/SwiftPassServiceImpl.java
new file mode 100644
index 0000000..bc39419
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/service/impl/SwiftPassServiceImpl.java
@@ -0,0 +1,395 @@
+package com.oying.modules.hwc.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.oying.exception.BadRequestException;
+import com.oying.modules.hwc.domain.HwcResponse;
+import com.oying.modules.hwc.service.SwiftPassService;
+import com.oying.modules.hwc.utils.SignUtil;
+import com.oying.modules.hwc.utils.SignUtils;
+import com.oying.modules.hwc.utils.XmlUtils;
+import com.oying.modules.security.config.SwiftPassProperties;
+import com.oying.modules.sh.service.OrderReturnService;
+import com.oying.modules.sh.service.OrderService;
+import com.oying.utils.enums.PayTypeEnum;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+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.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author xin
+ * @description
+ * @date 2025/1/23 下午4:01
+ */
+@SuppressWarnings({"unchecked", "all"})
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SwiftPassServiceImpl implements SwiftPassService {
+
+    private final static String version = "2.0";
+    private final static String charset = "UTF-8";
+    private final static String service_pay = "pay.weixin.jspay";
+    private final static String service_query = "unified.trade.query";
+    private final static String service_refund = "unified.trade.refund";
+    private final static String service_close = "unified.trade.close";
+
+    private final SwiftPassProperties properties;
+    private final OrderService orderService;
+    private final OrderReturnService returnService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public HwcResponse pay(String ip, Integer total, String timeExpire, String description, String openId,
+                           String orderNum, PayTypeEnum status) throws IOException {
+        SortedMap<String, String> map = new TreeMap<>();
+        map.put("service", service_pay);
+        map.put("version", version);
+        map.put("charset", charset);
+        map.put("sign_type", properties.getSignType());
+        map.put("is_raw", properties.getIsRaw());
+        map.put("is_minipg", properties.getIsMinipg());
+        map.put("out_trade_no", orderNum);
+        map.put("body", description);
+        map.put("sub_openid", openId);
+        map.put("sub_appid", properties.getAppId());
+        map.put("total_fee", String.valueOf(total));
+        map.put("mch_create_ip", ip);
+        map.put("notify_url", properties.getNotifyUrl());
+        map.put("time_expire", timeExpire);
+        map.put("nonce_str", String.valueOf(new Date().getTime()));
+        switch (status) {
+            case HWC:
+                map.put("mch_id", properties.getMchId());
+                break;
+            default:
+                throw new BadRequestException("汇旺财类型错误");
+        }
+        Map<String, String> params = SignUtils.paraFilter(map);
+        StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
+        SignUtils.buildPayParams(buf, params, false);
+        String preStr = buf.toString();
+        String sign_type = map.get("sign_type");
+
+        map.put("sign", SignUtil.getSign(sign_type, preStr, properties, status));
+
+        String reqUrl = properties.getReqUrl();
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = null;
+        Map<String, String> resultMap = new HashMap<>();
+        try {
+            HttpPost httpPost = new HttpPost(reqUrl);
+            StringEntity entityParams = new StringEntity(XmlUtils.parseXML(map), "utf-8");
+            httpPost.setEntity(entityParams);
+            httpPost.setHeader("Content-Type", "text/xml;utf-8");
+            client = HttpClients.createDefault();
+            response = client.execute(httpPost);
+            if (response != null && response.getEntity() != null) {
+                resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
+                String reSign = resultMap.get("sign");
+                sign_type = resultMap.get("sign_type");
+                if (resultMap.containsKey("sign") && SignUtil.verifySign(reSign, sign_type, resultMap, properties, status)) {
+                    throw new BadRequestException("验证签名错误");
+                } else {
+                    if ("0".equals(resultMap.get("status")) && "0".equals(resultMap.get("result_code"))) {
+//                        Gson gson = new Gson();
+//                        return gson.fromJson(resultMap.get("pay_info"), HwcResponse.class);
+                        return null;
+                    } else {
+                        throw new BadRequestException(resultMap.get("err_code") + " : " + resultMap.get("err_msg") + "\n" +
+                                resultMap.get("status") + " : " + resultMap.get("message"));
+                    }
+                }
+            } else {
+                throw new BadRequestException("操作失败");
+            }
+        } catch (Exception e) {
+            log.error("请求参数:{}", params);
+            log.error("返回参数:{}", resultMap);
+            throw new BadRequestException("系统异常:" + e.getMessage());
+        } finally {
+            if (response != null) {
+                response.close();
+            }
+            if (client != null) {
+                client.close();
+            }
+        }
+    }
+
+    @Override
+    public JSONObject query(String orderNum, PayTypeEnum status) throws IOException {
+        // 参数
+        SortedMap<String, String> map = new TreeMap<>();
+        map.put("service", service_query);
+        map.put("version", version);
+        map.put("charset", charset);
+        map.put("out_trade_no", orderNum);
+        map.put("sign_type", properties.getSignType());
+        switch (status) {
+            case HWC:
+                map.put("mch_id", properties.getMchId());
+                break;
+            default:
+                throw new BadRequestException("汇旺财类型错误");
+        }
+        map.put("nonce_str", String.valueOf(new Date().getTime()));
+        // 签名
+        Map<String, String> params = SignUtils.paraFilter(map);
+        StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
+        SignUtils.buildPayParams(buf, params, false);
+        String preStr = buf.toString();
+        String sign_type = map.get("sign_type");
+        map.put("sign", SignUtil.getSign(sign_type, preStr, properties, status));
+        // 请求
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = null;
+        Map<String, String> resultMap = new HashMap<>();
+        try {
+            HttpPost httpPost = new HttpPost(properties.getReqUrl());
+            StringEntity entityParams = new StringEntity(XmlUtils.parseXML(map), "utf-8");
+            httpPost.setEntity(entityParams);
+            httpPost.setHeader("Content-Type", "text/xml;utf-8");
+            client = HttpClients.createDefault();
+            response = client.execute(httpPost);
+            if (response != null && response.getEntity() != null) {
+                resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
+                String reSign = resultMap.get("sign");
+                sign_type = resultMap.get("sign_type");
+                if (resultMap.containsKey("sign") && SignUtil.verifySign(reSign, sign_type, resultMap, properties, status)) {
+                    throw new BadRequestException("验证签名错误");
+                } else {
+                    if ("0".equals(resultMap.get("status"))) {
+                        return JSONObject.parseObject(JSONObject.toJSONString(resultMap));
+                    } else {
+
+                        throw new BadRequestException(resultMap.get("err_code") + " : " + resultMap.get("err_msg") + "\n" +
+                                resultMap.get("status") + " : " + resultMap.get("message"));
+                    }
+                }
+            } else {
+                throw new BadRequestException("操作失败!");
+            }
+        } catch (Exception e) {
+            log.error("请求参数:{}", params);
+            log.error("返回参数:{}", resultMap);
+            throw new BadRequestException("操作失败,原因:" + e.getMessage());
+        } finally {
+            if (response != null) {
+                response.close();
+            }
+            if (client != null) {
+                client.close();
+            }
+        }
+    }
+
+    @Override
+    public void closeOrder(String outTradeNo, PayTypeEnum status) throws IOException {
+        // 参数
+        SortedMap<String, String> map = new TreeMap<>();
+        map.put("service", service_close);
+        map.put("version", version);
+        map.put("charset", charset);
+        map.put("out_trade_no", outTradeNo);
+        map.put("sign_type", properties.getSignType());
+        switch (status) {
+            case HWC:
+                map.put("mch_id", properties.getMchId());
+                break;
+            default:
+                throw new BadRequestException("汇旺财类型错误");
+        }
+        map.put("nonce_str", String.valueOf(new Date().getTime()));
+        // 签名
+        Map<String, String> params = SignUtils.paraFilter(map);
+        StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
+        SignUtils.buildPayParams(buf, params, false);
+        String preStr = buf.toString();
+        String sign_type = map.get("sign_type");
+        map.put("sign", SignUtil.getSign(sign_type, preStr, properties, status));
+        // 请求
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = null;
+        Map<String, String> resultMap = new HashMap<>();
+        try {
+            HttpPost httpPost = new HttpPost(properties.getReqUrl());
+            StringEntity entityParams = new StringEntity(XmlUtils.parseXML(map), "utf-8");
+            httpPost.setEntity(entityParams);
+            httpPost.setHeader("Content-Type", "text/xml;utf-8");
+            client = HttpClients.createDefault();
+            response = client.execute(httpPost);
+            if (response != null && response.getEntity() != null) {
+                resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
+                String reSign = resultMap.get("sign");
+                sign_type = resultMap.get("sign_type");
+                if (resultMap.containsKey("sign") && SignUtil.verifySign(reSign, sign_type, resultMap, properties, status)) {
+                    throw new BadRequestException("验证签名错误");
+                } else {
+                    if (!("0".equals(resultMap.get("status")))) {
+                        throw new BadRequestException(resultMap.get("err_code") + " : " + resultMap.get("err_msg") + "\n" +
+                                resultMap.get("status") + " : " + resultMap.get("message"));
+                    }
+                }
+            } else {
+                throw new BadRequestException("操作失败!");
+            }
+        } catch (Exception e) {
+            log.error("请求参数:{}", params);
+            log.error("返回参数:{}", resultMap);
+            throw new BadRequestException("操作失败,原因:" + e.getMessage());
+        } finally {
+            if (response != null) {
+                response.close();
+            }
+            if (client != null) {
+                client.close();
+            }
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, String> refund(String returnNum, String orderNum, String reason, long refund,
+                                      long total, PayTypeEnum payType) throws IOException {
+        // 参数
+        SortedMap<String, String> map = new TreeMap<>();
+        map.put("service", service_refund);
+        map.put("version", version);
+        map.put("charset", charset);
+        map.put("sign_type", properties.getSignType());
+        map.put("out_trade_no", orderNum);
+        map.put("out_refund_no", returnNum);
+        map.put("total_fee", String.valueOf(total));
+        map.put("refund_fee", String.valueOf(refund));
+        map.put("nonce_str", String.valueOf(new Date().getTime()));
+        switch (payType) {
+            case HWC:
+                map.put("mch_id", properties.getMchId());
+                map.put("op_user_id", properties.getMchId());
+                break;
+            default:
+                throw new BadRequestException("汇旺财类型错误");
+        }
+        // 签名
+        Map<String, String> params = SignUtils.paraFilter(map);
+        StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
+        SignUtils.buildPayParams(buf, params, false);
+        String preStr = buf.toString();
+        String sign_type = map.get("sign_type");
+        map.put("sign", SignUtil.getSign(sign_type, preStr, properties, payType));
+        // 参数
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = null;
+        Map<String, String> resultMap = new HashMap<>();
+        try {
+            HttpPost httpPost = new HttpPost(properties.getReqUrl());
+            StringEntity entityParams = new StringEntity(XmlUtils.parseXML(map), "utf-8");
+            httpPost.setEntity(entityParams);
+            httpPost.setHeader("Content-Type", "text/xml;utf-8");
+            client = HttpClients.createDefault();
+            response = client.execute(httpPost);
+            if (response != null && response.getEntity() != null) {
+                resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
+                if (resultMap.containsKey("sign") && SignUtil.verifySign(resultMap.get("sign"), resultMap.get("sign_type"), resultMap, properties, payType)) {
+                    throw new BadRequestException("验证签名错误");
+                } else {
+                    if ("0".equals(resultMap.get("status"))) {
+                        return resultMap;
+                    } else {
+                        throw new BadRequestException(resultMap.get("err_code") + " : " + resultMap.get("err_msg") + "\n" +
+                                resultMap.get("status") + " : " + resultMap.get("message"));
+                    }
+                }
+            } else {
+                throw new BadRequestException("操作失败!");
+            }
+        } catch (Exception e) {
+            log.error("请求参数:{}", params);
+            log.error("返回参数:{}", resultMap);
+            throw new BadRequestException("操作失败,原因:" + e);
+        } finally {
+            if (response != null) {
+                response.close();
+            }
+            if (client != null) {
+                client.close();
+            }
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void alipayCallback(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            String resString = XmlUtils.parseRequest(request);
+            String respString = "error";
+            if (!resString.isEmpty()) {
+                Map<String, String> map = XmlUtils.toMap(resString.getBytes(), "utf-8");
+                String sign_type = map.get("sign_type");
+                String reSign = map.get("sign");
+                if (map.containsKey("sign")) {
+//                    OrderDto order = orderService.findByOrderNum(map.get("out_trade_no"));
+                    PayTypeEnum status = PayTypeEnum.find("order.getPayType()");
+                    if (SignUtil.verifySign(reSign, sign_type, map, properties, status)) {
+                        System.out.println("验证签名错误!:" + map);
+                    } else {
+                        if ("0".equals(map.get("status"))) {
+                            if ("0".equals(map.get("result_code"))) {
+                                //业务处理
+                                respString = "success";
+                                response.getWriter().write(respString);
+                            }
+                        }
+                    }
+                }
+            }
+            response.getWriter().write(respString);
+        } catch (Exception e) {
+            throw new BadRequestException("操作失败,原因:" + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void returnNotify(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            String resString = XmlUtils.parseRequest(request);
+            String respString = "error";
+            if (!resString.isEmpty()) {
+                Map<String, String> map = XmlUtils.toMap(resString.getBytes(), "utf-8");
+                String sign_type = map.get("sign_type");
+                String reSign = map.get("sign");
+                if (map.containsKey("sign")) {
+//                    ReturnOrder returnOrder = returnOrderRepository.findByReturnNum(map.get("out_refund_no"));
+                    PayTypeEnum status = PayTypeEnum.find("order.getPayType()");
+                    if (SignUtil.verifySign(reSign, sign_type, map, properties, status)) {
+                        System.out.println("验证签名错误!:" + map);
+                    } else {
+                        if ("0".equals(map.get("status"))) {
+                            if ("0".equals(map.get("result_code"))) {
+                                // 业务处理
+                                respString = "success";
+                            }
+                        }
+                    }
+                }
+            }
+            response.getWriter().write(respString);
+        } catch (Exception e) {
+            System.out.println("操作失败,原因:" + e.getMessage());
+        }
+    }
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/utils/MD5.java b/oying-system/src/main/java/com/oying/modules/hwc/utils/MD5.java
new file mode 100644
index 0000000..4a7e899
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/utils/MD5.java
@@ -0,0 +1,56 @@
+package com.oying.modules.hwc.utils;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+* 功能:MD5签名
+* 版本:3.3
+* 修改日期:2012-08-17
+* */
+public class MD5 {
+
+    /**
+     * 签名字符串
+     * @param text 需要签名的字符串
+     * @param key 密钥
+     * @param input_charset 编码格式
+     * @return 签名结果
+     */
+    public static String sign(String text, String key, String input_charset) {
+    	text = text + key;
+        return DigestUtils.md5Hex(getContentBytes(text, input_charset)).toUpperCase();
+    }
+
+    /**
+     * 签名字符串
+     * @param text 需要签名的字符串
+     * @param sign 签名结果
+     * @param key 密钥
+     * @param input_charset 编码格式
+     * @return 签名结果
+     */
+    public static boolean verify(String text, String sign, String key, String input_charset) {
+    	text = text + key;
+    	String md5Hex = DigestUtils.md5Hex(getContentBytes(text, input_charset));
+        return md5Hex.equals(sign);
+    }
+
+    /**
+     * @param content 需要签名的字符串
+     * @param charset 编码格式
+     * @return 签名结果
+     */
+    private static byte[] getContentBytes(String content, String charset) {
+        if (charset == null || charset.isEmpty()) {
+            return content.getBytes();
+        }
+        try {
+            return content.getBytes(charset);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
+        }
+    }
+
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/utils/RSAUtil.java b/oying-system/src/main/java/com/oying/modules/hwc/utils/RSAUtil.java
new file mode 100644
index 0000000..11b2b45
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/utils/RSAUtil.java
@@ -0,0 +1,104 @@
+package com.oying.modules.hwc.utils;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.security.*;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+
+/**
+ * @author zeming.fan@swiftpass.cn
+ *
+ */
+public class RSAUtil {
+
+    public enum SignatureSuite {
+        //SHA1("SHA1WithRSA"), MD5("MD5WithRSA");
+    	SHA1("SHA1WithRSA"), SHA256("SHA256WithRSA");
+        private final String suite;
+
+        SignatureSuite(String suite) {
+            this.suite = suite;
+        }
+
+        public String val() {
+            return suite;
+        }
+    }
+
+
+    private static KeyFactory getKeyFactory() {
+        try {
+            return KeyFactory.getInstance("RSA");
+        } catch (NoSuchAlgorithmException e) {
+            // 应该不会出现
+            throw new RuntimeException("初始化RSA KeyFactory失败");
+        }
+    }
+
+    public static byte[] sign(SignatureSuite suite, byte[] msgBuf, String privateKeyStr) {
+        Signature signature = null;
+        try {
+            signature = Signature.getInstance(suite.val());
+        } catch (Exception e) {
+            // 上线运行时套件一定存在
+            // 异常不往外抛
+        }
+
+        try {
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
+            PrivateKey privateKey = getKeyFactory().generatePrivate(keySpec);
+            if (signature != null) {
+                signature.initSign(privateKey);
+            }
+        } catch(Exception e) {
+            //logger.warn("解析私钥失败:{}", e.getMessage());
+            throw new RuntimeException("INVALID_PRIKEY");
+        }
+        try {
+            if (signature != null) {
+                signature.update(msgBuf);
+            }
+            if (signature != null) {
+                return signature.sign();
+            }
+        } catch (SignatureException e) {
+            // 一般不会出现
+            throw new RuntimeException(e.getMessage());
+        }
+        return msgBuf;
+    }
+
+    public static boolean verifySign(SignatureSuite suite, byte[] msgBuf, byte[] sign, String publicKeyStr) {
+        Signature signature = null;
+        try {
+            signature = Signature.getInstance(suite.val());
+        } catch (Exception e) {
+            // 上线运行时套件一定存在
+            // 异常不往外抛
+        }
+
+        try {
+            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
+            PublicKey publicKey = getKeyFactory().generatePublic(keySpec);
+            if (signature != null) {
+                signature.initVerify(publicKey);
+            }
+        } catch(Exception e) {
+            throw new RuntimeException("INVALID_PUBKEY");
+        }
+        try {
+            if (signature != null) {
+                signature.update(msgBuf);
+            }
+            if (signature != null) {
+                return signature.verify(sign);
+            }
+        } catch (SignatureException e) {
+            // 一般不会出现
+            throw new RuntimeException("签名格式不合法");
+        }
+        return false;
+    }
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtil.java b/oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtil.java
new file mode 100644
index 0000000..b2a80cb
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtil.java
@@ -0,0 +1,90 @@
+package com.oying.modules.hwc.utils;
+
+import com.oying.exception.BadRequestException;
+import com.oying.modules.security.config.SwiftPassProperties;
+import com.oying.utils.enums.PayTypeEnum;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+/**
+ * @author zeming.fan@swiftpass.cn
+ */
+@Slf4j
+public class SignUtil {
+
+    /**
+     * 请求时根据不同签名方式去生成不同的sign
+     */
+    public static String getSign(String signType, String preStr, SwiftPassProperties properties, PayTypeEnum status) {
+        if ("RSA_1_256".equals(signType)) {
+            try {
+                return SignUtil.sign(preStr, "RSA_1_256", properties.getMchPrivateKey());
+            } catch (Exception e1) {
+                log.error(e1.getMessage(), e1);
+                throw new BadRequestException(e1.getMessage());
+            }
+        } else {
+            if (status.equals(PayTypeEnum.HWC)) {
+                return MD5.sign(preStr, "&key=" + properties.getKey(), "utf-8");
+            }
+            throw new BadRequestException("汇旺财类型错误");
+        }
+    }
+
+    /**
+     * 对返回参数的验证签名
+     */
+    public static boolean verifySign(String sign, String signType, Map<String, String> resultMap, SwiftPassProperties properties, PayTypeEnum status) throws Exception {
+        if ("RSA_1_256".equals(signType)) {
+            Map<String, String> params = SignUtils.paraFilter(resultMap);
+            StringBuilder builder = new StringBuilder((params.size() + 1) * 10);
+            SignUtils.buildPayParams(builder, params, false);
+            String preStr = builder.toString();
+            return !SignUtil.verifySign(preStr, sign, "RSA_1_256", properties.getPlatPublicKey());
+        } else if ("MD5".equals(signType)) {
+            if (status.equals(PayTypeEnum.HWC)) {
+                return !SignUtils.checkParam(resultMap, properties.getKey());
+            }
+            throw new BadRequestException("汇旺财类型错误");
+        }
+        return true;
+    }
+
+    /**
+     * RSA_1_256 验证签名
+     */
+    public static boolean verifySign(String preStr, String sign, String signType, String platPublicKey) throws Exception {
+        // 调用这个函数前需要先判断是MD5还是RSA
+        // 商户的验签函数要同时支持MD5和RSA
+        RSAUtil.SignatureSuite suite;
+        if ("RSA_1_1".equals(signType)) {
+            suite = RSAUtil.SignatureSuite.SHA1;
+        } else if ("RSA_1_256".equals(signType)) {
+            suite = RSAUtil.SignatureSuite.SHA256;
+        } else {
+            throw new Exception("不支持的签名方式");
+        }
+        return RSAUtil.verifySign(suite, preStr.getBytes(StandardCharsets.UTF_8), Base64.decodeBase64(sign.getBytes(StandardCharsets.UTF_8)),
+                platPublicKey);
+    }
+
+    /**
+     * RSA_1_256生成不同的sign
+     */
+    public static String sign(String preStr, String signType, String mchPrivateKey) throws Exception {
+        RSAUtil.SignatureSuite suite;
+        if ("RSA_1_1".equals(signType)) {
+            suite = RSAUtil.SignatureSuite.SHA1;
+        } else if ("RSA_1_256".equals(signType)) {
+            suite = RSAUtil.SignatureSuite.SHA256;
+        } else {
+            throw new Exception("不支持的签名方式");
+        }
+        byte[] signBuf = RSAUtil.sign(suite, preStr.getBytes(StandardCharsets.UTF_8),
+                mchPrivateKey);
+        return new String(Base64.encodeBase64(signBuf), StandardCharsets.UTF_8);
+    }
+}
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtils.java b/oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtils.java
new file mode 100644
index 0000000..50daf80
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtils.java
@@ -0,0 +1,110 @@
+package com.oying.modules.hwc.utils;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.xml.sax.InputSource;
+
+import java.io.StringReader;
+import java.net.URLEncoder;
+import java.util.*;
+
+
+/**
+ * ClassName:SignUtils
+ * Function: 签名用的工具箱
+ * Date:     2014-6-27 下午3:22:33
+ */
+public class SignUtils {
+
+    /**
+     * <一句话功能简述>
+     * <功能详细描述>验证返回参数
+     *
+     * @see [类、类#方法、类#成员]
+     */
+    public static boolean checkParam(Map<String, String> params, String key) {
+        boolean result = false;
+        if (params.containsKey("sign")) {
+            String sign = params.get("sign");
+            params.remove("sign");
+            StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
+            SignUtils.buildPayParams(buf, params, false);
+            String preStr = buf.toString();
+            String signMD5 = MD5.sign(preStr, "&key=" + key, "utf-8");
+            result = sign.equalsIgnoreCase(signMD5);
+        }
+        return result;
+    }
+
+    /**
+     * 过滤参数
+     */
+    public static Map<String, String> paraFilter(Map<String, String> sArray) {
+        Map<String, String> result = new HashMap<>(sArray.size());
+        if (sArray.isEmpty()) {
+            return result;
+        }
+        for (String key : sArray.keySet()) {
+            String value = sArray.get(key);
+            if (value == null || value.isEmpty() || key.equalsIgnoreCase("sign")) {
+                continue;
+            }
+            result.put(key, value);
+        }
+        return result;
+    }
+
+    /**
+     * <一句话功能简述>
+     * <功能详细描述>将map转成String
+     *
+     * @see [类、类#方法、类#成员]
+     */
+    public static String payParamsToString(Map<String, String> payParams) {
+        return payParamsToString(payParams, false);
+    }
+
+    public static String payParamsToString(Map<String, String> payParams, boolean encoding) {
+        return payParamsToString(new StringBuilder(), payParams, encoding);
+    }
+
+    public static String payParamsToString(StringBuilder sb, Map<String, String> payParams, boolean encoding) {
+        buildPayParams(sb, payParams, encoding);
+        return sb.toString();
+    }
+
+    public static void buildPayParams(StringBuilder sb, Map<String, String> payParams, boolean encoding) {
+        List<String> keys = new ArrayList<>(payParams.keySet());
+        Collections.sort(keys);
+        for (String key : keys) {
+            sb.append(key).append("=");
+            if (encoding) {
+                sb.append(urlEncode(payParams.get(key)));
+            } else {
+                sb.append(payParams.get(key));
+            }
+            sb.append("&");
+        }
+        sb.setLength(sb.length() - 1);
+    }
+
+    public static String urlEncode(String str) {
+        try {
+            return URLEncoder.encode(str, "UTF-8");
+        } catch (Throwable e) {
+            return str;
+        }
+    }
+
+    public static Element readerXml(String body, String encode) throws DocumentException {
+        SAXReader reader = new SAXReader(false);
+        InputSource source = new InputSource(new StringReader(body));
+        source.setEncoding(encode);
+        Document doc = reader.read(source);
+        return doc.getRootElement();
+    }
+
+}
+
diff --git a/oying-system/src/main/java/com/oying/modules/hwc/utils/XmlUtils.java b/oying-system/src/main/java/com/oying/modules/hwc/utils/XmlUtils.java
new file mode 100644
index 0000000..783c326
--- /dev/null
+++ b/oying-system/src/main/java/com/oying/modules/hwc/utils/XmlUtils.java
@@ -0,0 +1,138 @@
+package com.oying.modules.hwc.utils;
+
+import com.oying.exception.BadRequestException;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.xml.sax.InputSource;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.util.*;
+
+/**
+ * ClassName:Xml
+ * Function: XML的工具方法
+ * Date:     2014-8-10 下午10:48:21
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class XmlUtils {
+
+    /**
+     * <一句话功能简述>
+     * <功能详细描述>request转字符串
+     *
+     * @see [类、类#方法、类#成员]
+     */
+    public static String parseRequest(HttpServletRequest request) {
+        StringBuilder body = new StringBuilder();
+        try {
+            ServletInputStream inputStream = request.getInputStream();
+            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
+            while (true) {
+                String info = br.readLine();
+                if (info == null) {
+                    break;
+                }
+                if (body.length() == 0) {
+                    body = new StringBuilder(info);
+                } else {
+                    body.append(info);
+                }
+            }
+        } catch (Exception e) {
+            throw new BadRequestException("读取数据流异常:" + e.getMessage());
+        }
+        return body.toString();
+    }
+
+    public static String parseXML(SortedMap<String, String> parameters) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<xml>");
+        Set es = parameters.entrySet();
+        for (Object e : es) {
+            Map.Entry entry = (Map.Entry) e;
+            String k = (String) entry.getKey();
+            String v = (String) entry.getValue();
+            if (null != v && !v.isEmpty() && !"appkey".equals(k)) {
+                sb.append("<").append(k).append(">").append(parameters.get(k)).append("</").append(k).append(">\n");
+            }
+        }
+        sb.append("</xml>");
+        return sb.toString();
+    }
+
+    /**
+     * 从request中获得参数Map,并返回可读的Map
+     */
+    public static SortedMap getParameterMap(HttpServletRequest request) {
+        // 参数Map
+        Map properties = request.getParameterMap();
+        // 返回值Map
+        SortedMap returnMap = new TreeMap();
+        Iterator entries = properties.entrySet().iterator();
+        Map.Entry entry;
+        String name;
+        String value = "";
+        while (entries.hasNext()) {
+            entry = (Map.Entry) entries.next();
+            name = (String) entry.getKey();
+            Object valueObj = entry.getValue();
+            if (null == valueObj) {
+                value = "";
+            } else if (valueObj instanceof String[]) {
+                String[] values = (String[]) valueObj;
+                for (String s : values) {
+                    value = s + ",";
+                }
+                value = value.substring(0, value.length() - 1);
+            } else {
+                value = valueObj.toString();
+            }
+            returnMap.put(name, value.trim());
+        }
+        returnMap.remove("method");
+        return returnMap;
+    }
+
+    /**
+     * 转XML map
+     */
+    public static Map<String, String> toMap(byte[] xmlBytes, String charset) throws Exception {
+        SAXReader reader = new SAXReader(false);
+        InputSource source = new InputSource(new ByteArrayInputStream(xmlBytes));
+        source.setEncoding(charset);
+        Document doc = reader.read(source);
+        return XmlUtils.toMap(doc.getRootElement());
+    }
+
+    /**
+     * 转MAP
+     */
+    public static Map<String, String> toMap(Element element) {
+        Map<String, String> rest = new HashMap<>();
+        List<Element> els = element.elements();
+        for (Element el : els) {
+            rest.put(el.getName().toLowerCase(), el.getTextTrim());
+        }
+        return rest;
+    }
+
+    public static String toXml(Map<String, String> params) {
+        StringBuilder buf = new StringBuilder();
+        List<String> keys = new ArrayList<>(params.keySet());
+        Collections.sort(keys);
+        buf.append("<xml>");
+        for (String key : keys) {
+            buf.append("<").append(key).append(">");
+            buf.append("<![CDATA[").append(params.get(key)).append("]]>");
+            buf.append("</").append(key).append(">\n");
+        }
+        buf.append("</xml>");
+        return buf.toString();
+    }
+}
+
diff --git a/pom.xml b/pom.xml
index 1264ba2..6005c46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -197,6 +197,13 @@
             <version>2.12.2</version>
         </dependency>
 
+        <!-- dom4j 使用安全的 2.x 版本 -->
+        <dependency>
+            <groupId>org.dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>2.1.4</version>
+        </dependency>
+
         <!-- fastjson2 -->
         <dependency>
             <groupId>com.alibaba.fastjson2</groupId>

--
Gitblit v1.9.3