xin
2025-06-12 e9ad3ca0104dc5caac3db45374c8ae97168ee53b
支付
12 files added
1 files modified
1153 ■■■■■ changed files
oying-common/src/main/java/com/oying/utils/enums/PayReturnStateEnum.java 44 ●●●●● patch | view | raw | blame | history
oying-common/src/main/java/com/oying/utils/enums/PayStateEnum.java 52 ●●●●● patch | view | raw | blame | history
oying-common/src/main/java/com/oying/utils/enums/PayTypeEnum.java 38 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/domain/HwcResponse.java 30 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/rest/SwiftPassController.java 38 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/service/SwiftPassService.java 51 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/service/impl/SwiftPassServiceImpl.java 395 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/utils/MD5.java 56 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/utils/RSAUtil.java 104 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtil.java 90 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtils.java 110 ●●●●● patch | view | raw | blame | history
oying-system/src/main/java/com/oying/modules/hwc/utils/XmlUtils.java 138 ●●●●● patch | view | raw | blame | history
pom.xml 7 ●●●●● patch | view | raw | blame | history
oying-common/src/main/java/com/oying/utils/enums/PayReturnStateEnum.java
New file
@@ -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();
    }
}
oying-common/src/main/java/com/oying/utils/enums/PayStateEnum.java
New file
@@ -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();
    }
}
oying-common/src/main/java/com/oying/utils/enums/PayTypeEnum.java
New file
@@ -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;
    }
}
oying-system/src/main/java/com/oying/modules/hwc/domain/HwcResponse.java
New file
@@ -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;
}
oying-system/src/main/java/com/oying/modules/hwc/rest/SwiftPassController.java
New file
@@ -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);
    }
}
oying-system/src/main/java/com/oying/modules/hwc/service/SwiftPassService.java
New file
@@ -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);
}
oying-system/src/main/java/com/oying/modules/hwc/service/impl/SwiftPassServiceImpl.java
New file
@@ -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());
        }
    }
}
oying-system/src/main/java/com/oying/modules/hwc/utils/MD5.java
New file
@@ -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);
        }
    }
}
oying-system/src/main/java/com/oying/modules/hwc/utils/RSAUtil.java
New file
@@ -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;
    }
}
oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtil.java
New file
@@ -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);
    }
}
oying-system/src/main/java/com/oying/modules/hwc/utils/SignUtils.java
New file
@@ -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();
    }
}
oying-system/src/main/java/com/oying/modules/hwc/utils/XmlUtils.java
New file
@@ -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();
    }
}
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>