package com.jeequan.jeepay.thirdparty.channel.yspay.utils;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.*;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.yspay.YspayIsvParams;
import com.jeequan.jeepay.core.utils.Base64;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.ImageUtils;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.yspay.model.ReqMethod;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.*;


@Slf4j
public class YspayKit {
    private static final String ALLCHAR = "0123456789ABCDEF";

    private static final String BASEURL = "https://qrcode.ysepay.com/gateway.do";

    // public static final String APPLYMENT_URL = "https://ouser.ysepay.com:6443";
    // public static final String APPLYMENT_URL = "https://appdev.ysepay.com";
    public static final String APPLYMENT_URL = "https://ysgate.ysepay.com";
    /**
     * 分账请求地址
     **/
    public static final String DIVISION_URL = "https://commonapi.ysepay.com/gateway.do";
    /**
     * 分账退款接口
     **/
    public static final String DIVISION_REFUND_URL = "https://openapi.ysepay.com/gateway.do";

    public static final String RES_CODE = "10000";    // 响应状态
    public static final String APPLY_CODE = "00000";    // 进件网关响应码，00000表示请求成功
    public static final String APPLY_SUB_CODE = "0000";    // 进件业务响应码，0000表示成功

    public static final String TRADE_SUCCESS = "TRADE_SUCCESS";    // 交易成功
    public static final String TRADE_PROCESS = "TRADE_PROCESS";    // 交易处理中
    public static final String WAIT_BUYER_PAY = "WAIT_BUYER_PAY";    // 交易中
    public static final String TRADE_FAILED = "TRADE_FAILED";    // 交易失败
    public static final String TRADE_ALL_REFUND = "TRADE_ALL_REFUND";    // 全部退款成功
    public static final String TRADE_PART_REFUND = "TRADE_PART_REFUND";    // 部分退款成功

    public static final String POS_CODE_S = "S";    // 成功
    public static final String POS_CODE_F = "F";    // 失败
    public static final String POS_CODE_T = "T";    // 发送超时

    private static final String REQ_TYPE_PAYMENT = "pay";   // 支付类型签名-支付
    private static final String REQ_TYPE_APPLYMENT = "apply";   // 支付类型签名-进件

    public static String payRequest(String reqUrl, String method, MchAppConfigContext mchAppConfigContext, JSONObject bizContent, String notifyUrl, String ifCode) {

        // 配置参数
        ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
        YspayIsvParams isvParams = (YspayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), ifCode);
        try {
            // 公共参数
            JSONObject reqParams = new JSONObject();
            // 接口名称
            reqParams.put("method", method);
            // 版本号
            reqParams.put("version", "3.0");
            // 回调地址
            if (StringUtils.isNotEmpty(notifyUrl)) {
                reqParams.put("notify_url", notifyUrl);
            }
            // 签名方式
            reqParams.put("sign_type", isvParams.getSignType());
            // 编码格式
            reqParams.put("charset", "utf-8");
            // 请求的时间
            reqParams.put("timestamp", DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN));
            // 通道服务商号
            reqParams.put("partner_id", isvParams.getPartnerId());
            // 请求参数
            reqParams.put("biz_content", bizContent.toJSONString());

            // 支付下单类型
            reqParams.put("reqType", REQ_TYPE_PAYMENT);
            // 生成签名
            String sign = getSign(isvParams, reqParams);
            reqParams.put("sign", sign);
            // 发起请求
            log.info("【银盛支付】发起请求：{}", reqParams);
            String response = HttpUtil.post(reqUrl, reqParams);
            log.info("【银盛支付】响应结果：{}", response);
            return response;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    public static String payRequest(String method, MchAppConfigContext mchAppConfigContext, JSONObject bizContent, String notifyUrl, String ifCode) {
        try {
            return payRequest(BASEURL, method, mchAppConfigContext, bizContent, notifyUrl, ifCode);
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    public static String divisionRequest(String reqUrl, String method, String isvNo, JSONObject bizContent, String notifyUrl, String ifCode) {

        // 配置参数
        ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
        YspayIsvParams isvParams = (YspayIsvParams) configContextQueryService.queryIsvParams(isvNo, ifCode);
        try {
            // 公共参数
            JSONObject reqParams = new JSONObject();
            // 接口名称
            reqParams.put("method", method);
            // 通道服务商号
            reqParams.put("partner_id", isvParams.getPartnerId());
            // 编码格式
            reqParams.put("charset", "utf-8");
            // 请求的时间
            reqParams.put("timestamp", DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN));
            // 签名方式
            reqParams.put("sign_type", isvParams.getSignType());
            // 版本号
            reqParams.put("version", "3.0");
            // 回调地址
            if (StringUtils.isNotEmpty(notifyUrl)) {
                reqParams.put("notify_url", notifyUrl);
            }
            // 请求参数
            reqParams.put("biz_content", bizContent.toJSONString());
            // 签名类型
            reqParams.put("reqType", REQ_TYPE_PAYMENT);

            // 生成签名
            String sign = getSign(isvParams, reqParams);
            reqParams.put("sign", sign);
            // 发起请求
            log.info("【银盛分账】发起请求：{}", reqParams);
            String response = HttpUtil.post(reqUrl, reqParams);
            log.info("【银盛分账】响应结果：{}", response);
            return response;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    public static String wxQueryRequest(String reqUrl, String notifyUrl, String method, YspayIsvParams isvParams, JSONObject bizContent) {

        try {
            // 公共参数
            JSONObject reqParams = new JSONObject();
            // 接口名称
            reqParams.put("method", method);
            // 版本号
            reqParams.put("version", "3.0");
            // 签名方式
            reqParams.put("sign_type", isvParams.getSignType());
            // 回调地址
            if (StringUtils.isNotEmpty(notifyUrl)) {
                reqParams.put("notify_url", notifyUrl);
            }
            // 编码格式
            reqParams.put("charset", "utf-8");
            // 请求的时间
            reqParams.put("timestamp", DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN));
            // 通道服务商号
            reqParams.put("partner_id", isvParams.getPartnerId());
            // 请求参数
            reqParams.put("biz_content", bizContent.toJSONString());
            // 生成签名
            String sign = getSign(isvParams, reqParams).trim();

            if ("ysepay.merchant.scan.report".equals(method) && "SM".equals(isvParams.getSignType())) {
                sign = Base64.encodeBase64String(sign.getBytes(StandardCharsets.UTF_8));
            }

            reqParams.put("sign", sign);
            // 发起请求
            log.info("【银盛支付】请求地址：{}, 发起请求：{}", reqUrl, reqParams);
            String response = HttpUtil.post(reqUrl, reqParams);
            log.info("【银盛支付】请求地址：{}, 响应结果：{}", reqUrl, response);
            return response;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    public static void setPayParams(MchAppConfigContext mchAppConfigContext, JSONObject bizContent, String payType, String ifCode) {
        bizContent.put("seller_id", mchAppConfigContext.getMchApplyment().getChannelMchNo());
        // 商户名
//        bizContent.put("seller_name", mchParams.getMercCnm());
        //        业务代码 00510030 正扫微信、二维码、银联行业码、反扫（T1/D1）
        //        正扫微信、二维码、银联行业码（D0）：00510032
        //        反扫（D0）：00510080
        if(CS.SETTLEMENT_TYPE.D0.equals(mchAppConfigContext.getMchApplyment().getSettlementType())){
            if ("1".equals(payType)) {
                // 主扫 D0
                bizContent.put("business_code", "00510032");
            } else {
                // 反扫 D0
                bizContent.put("business_code", "00510080");
            }
        }else{
            bizContent.put("business_code", "00510030");
        }
    }

    /**
     * 生成签名
     *
     * @param isvParams
     * @param reqParams
     * @return
     */
    public static String getSign(YspayIsvParams isvParams, JSONObject reqParams) {
        try {
            ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
            String certFilePath = channelCertConfigKitBean.getCertFilePath(isvParams.getPrivateKeyFile());

            String reqType = reqParams.getString("reqType");
            reqParams.remove("reqType");
            Map paramsMap = reqParams.toJavaObject(Map.class);
            String signContent = getSignContent(paramsMap);
            log.info("【银盛支付】待签名内容：{}, 证书地址：{}", signContent, certFilePath);
            String sign = null;
            if ("RSA".equals(isvParams.getSignType())) {
                if (StringUtils.isNotEmpty(reqType) && REQ_TYPE_PAYMENT.equals(reqType)) {
                    PrivateKey privateKey = SignRsaUtil.getSignCertPrivateKey(certFilePath, isvParams.getPrivateKeyPassword());
                    sign = SignRsaUtil.rsaSign(privateKey, signContent, "utf-8");
                } else {
                    sign = RSA256Util.sign(signContent, certFilePath, isvParams.getPrivateKeyPassword());
                }
            } else if ("SM".equals(isvParams.getSignType())) {
                sign = SignSmUtil.signMsgSM2(certFilePath, isvParams.getPrivateKeyPassword(), signContent);
            } else {
                log.error("未识别的签名类型");
            }
            log.info("【银盛支付】签名结果：{}", sign);
            return sign;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException("【银盛支付】签名异常：" + e.getMessage());
        }
    }

    /**
     * 遍历以及根据重新排序
     *
     * @param sortedParams
     * @return
     */
    public static String getSignContent(Map<String, String> sortedParams) {
        StringBuffer content = new StringBuffer();
        List<String> keys = new ArrayList<String>(sortedParams.keySet());
        Collections.sort(keys);
        int index = 0;
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = sortedParams.get(key);
            if (!StringUtils.isAnyEmpty(key, value)) {
                content.append((index == 0 ? "" : "&") + key + "=" + value);
                index++;
            }
        }
        return content.toString();
    }


    /**
     * 读文件
     *
     * @param file 文件路径
     * @return 文件内容
     * @throws IOException
     */
    public static final byte[] readFile(String file) throws IOException {
        int offset = 0;
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            byte[] out = new byte[in.available()];

            if (out.length < offset + in.available()) {
                throw new IOException("Illegal Argument: filepath");
            }

            byte[] buffer = new byte[1024];
            int numRead;
            while ((numRead = in.read(buffer, 0, buffer.length)) >= 0) {
                System.arraycopy(buffer, 0, out, offset, numRead);
                offset += numRead;
            }
            return out;
        } catch (Exception e) {

            throw e;

        } finally {
            if (in != null) {
                in.close();
            }
        }
    }


//---------------------------------------------------------------------------------------------------------------------------

    /**
     * 发起进件请求
     *
     * @param reqUrl
     * @param method
     * @param isvParams
     * @param bizContent
     * @return
     */
    public static String applymentRequest(String reqUrl, String method, YspayIsvParams isvParams, JSONObject bizContent) {
        return applymentRequest(reqUrl, method, isvParams, bizContent, null);
    }

    /**
     * 发起进件请求
     *
     * @param reqUrl
     * @param method
     * @param isvParams
     * @param bizContent
     * @param imgFileByteArray 要上传的文件
     * @return
     */
    public static String applymentRequest(String reqUrl, String method, YspayIsvParams isvParams, JSONObject bizContent, byte[] imgFileByteArray) {

        try {
            // 公共参数
            JSONObject reqParams = new JSONObject();
            // 接口名称
            reqParams.put("method", method);
            // 请求的时间
            reqParams.put("timeStamp", DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN));
            // 编码格式
            reqParams.put("charset", "utf-8");
            // 请求流水号
            reqParams.put("reqId", DateKit.currentTimeMillis() + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN));
            // 通道服务商号
            reqParams.put("certId", isvParams.getPartnerId());
            // 版本号
            reqParams.put("version", "1.0");

            /** 2、生成对业务参数加密的密钥*/
            StringBuilder sb = new StringBuilder();
            Random random = new Random();
            for (int i = 0; i < 16; i++) {
                sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
            }
            String key = ByteUtil.toHexString(sb.toString());

            /** 3、使用银盛公钥对密钥key进行加密，得到加密的key并设置到请求参数check中。publicFilePath为存放银盛公钥的地址*/
            ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
            String publicKeyFilePath = channelCertConfigKitBean.getCertFilePath(isvParams.getPublicKeyFile());
            String check = null;

            BufferedInputStream inputStream = FileUtil.getInputStream(publicKeyFilePath);
            byte[] publicKeyEncoded = SecureUtil.readX509Certificate(inputStream).getPublicKey().getEncoded();
            SM2 sm2 = null;
            RSA rsa = null;
            if ("SM".equals(isvParams.getSignType())) {
                sm2 = SmUtil.sm2(null, publicKeyEncoded);
                check = sm2.encryptBase64(publicKeyFilePath, KeyType.PublicKey);
            } else if ("RSA".equals(isvParams.getSignType())) {
                rsa = SecureUtil.rsa(null, publicKeyEncoded);
                check = rsa.encryptBase64(ByteUtil.hexStringToBytes(key), KeyType.PublicKey);
            }

            //加密后的密钥
            reqParams.put("check", check);

            /** 4、封装业务参数 */

            /** 5、使用生成的密钥key对业务参数进行加密，并将加密后的业务参数放入请求参数bizContent中*/
            byte[] bte = AESUtil.encrypt(JSON.toJSONBytes(bizContent, SerializerFeature.WriteNullStringAsEmpty), ByteUtil.hexStringToBytes(key));
            reqParams.put("bizContent", org.apache.tomcat.util.codec.binary.Base64.encodeBase64String(bte));


            /** 6、将请求参数进行sort排序，生成拼接的字符床，并使用接入方私钥对请求参数进行加签，并将加签的值存入请求参数sign中*/
            String sign = getSign(isvParams, reqParams);
            reqParams.put("sign", sign);

            /** 7、发送http请求获取返回结果 */
            log.info("【银盛进件】请求地址：{}，发起请求：{}，加密前bizContent：{}", APPLYMENT_URL + reqUrl, reqParams, bizContent);
            String result;
            if (imgFileByteArray == null) {
                result = HttpRequest.post(APPLYMENT_URL + reqUrl)
                        .header("Content-type", "application/json")
                        .timeout(20000)
                        .charset("UTF-8")
                        .body(reqParams.toJSONString())
                        .execute()
                        .body();
            } else {
                result = HttpUtil.createPost(APPLYMENT_URL + reqUrl)
                        .form("file", imgFileByteArray, "1.png")
                        .form(JSON.toJavaObject(reqParams, Map.class))
                        .execute().body();
            }


            byte[] res = org.apache.tomcat.util.codec.binary.Base64.decodeBase64(result);
            Map<String, String> resMap = (Map<String, String>) JSONObject.parse(new String(res, StandardCharsets.UTF_8));
            log.info("【银盛接口】响应结果：{}", JSON.toJSONString(resMap));

            /** 8、对结果进行解密，并使用银盛公钥验签*/
            if (StringUtils.isNotBlank(resMap.get("sign"))) {
                if ("SM".equals(isvParams.getSignType())) {
                    byte[] srcData = GMSignUtils.getSignDataStr1(resMap).getBytes("UTF-8");
                    boolean validateSignResult1 = sm2.verify(srcData, Base64.decodeBase64(resMap.get("sign").getBytes(StandardCharsets.UTF_8)));
                    if (!validateSignResult1) {
                        log.info("【银盛进件】响应验签失败");
                        throw new BizException("验签失败");
                    }
                } else if ("RSA".equals(isvParams.getSignType())) {
                    byte[] srcData = SignUtil.getSignDataStr1(resMap).getBytes(StandardCharsets.UTF_8);
                    Sign sign2 = cn.hutool.crypto.SignUtil.sign(SignAlgorithm.SHA256withRSA, null, publicKeyEncoded);
                    boolean validateSignResult1 = sign2.verify(srcData, Base64.decodeBase64(resMap.get("sign").getBytes(StandardCharsets.UTF_8)));
                    if (!validateSignResult1) {
                        log.info("【银盛进件】响应验签失败");
                        throw new BizException("验签失败");
                    }
                }
            } else {
                throw new BizException("验签失败,未返回加签信息,可能是银盛未配置发起方或者发起方证书类型配置有误,返回结果提示为:{}" + resMap.get("msg"));
            }

            /** 9、针对自己的业务，根据返回的结果的code 和 subCode 做接入方的业务处理*/
            if (!APPLY_CODE.equals(resMap.get("code"))) {
                throw new BizException(StringUtils.isBlank(resMap.get("msg")) ? "请求响应失败" : resMap.get("msg"));
            }
            if (!APPLY_SUB_CODE.equals(resMap.get("subCode"))) {
                if ("B510091".equals(resMap.get("subCode"))) {
                    throw new BizException(ApiCodeEnum.YS_ARTIFICIAL_AUDIT_CODE);
                }

                throw new BizException(StringUtils.isBlank(resMap.get("subMsg")) ? "业务响应失败" : resMap.get("subMsg"));
            }

            /** 10、使用上面生成的加密密钥key，解密返回的业务参数*/
            if (StringUtils.isNotBlank(resMap.get("businessData"))) {
                byte[] data_ = org.apache.tomcat.util.codec.binary.Base64.decodeBase64(resMap.get("businessData"));
                byte[] data = AESUtil.decrypt(data_, ByteUtil.hexStringToBytes(key));

                String decryptResult = new String(data, StandardCharsets.UTF_8);
                resMap.put("data", decryptResult);
                resMap.remove("businessData");
            }
            log.info("【银盛进件】解密后的完整参数为：{}", JSON.toJSONString(resMap));

            return JSON.toJSONString(resMap);
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    public static String applymentRequest(ReqMethod.Method method, YspayIsvParams isvParams, JSONObject bizContent) {
        return applymentRequest(method, isvParams, bizContent, null);
    }

    /**
     * 发起进件请求
     *
     * @param method
     * @param isvParams
     * @param bizContent
     * @param imgFileByteArray 要上传的文件
     * @return
     */
    public static String applymentRequest(ReqMethod.Method method, YspayIsvParams isvParams, JSONObject bizContent, byte[] imgFileByteArray) {
        String url = ReqMethod.getUrl(method);
        try {
            // 公共参数
            JSONObject reqParams = new JSONObject();
            // 接口名称
            reqParams.put("method", url);
            // 请求的时间
            reqParams.put("timeStamp", DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN));
            // 编码格式
            reqParams.put("charset", "utf-8");
            // 请求流水号
            reqParams.put("reqId", DateKit.currentTimeMillis() + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN));
            // 通道服务商号
            reqParams.put("certId", isvParams.getPartnerId());
            // 版本号
            reqParams.put("version", method.getVersion());

            /** 2、生成对业务参数加密的密钥*/
            StringBuilder sb = new StringBuilder();
            Random random = new Random();
            for (int i = 0; i < 16; i++) {
                sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
            }
            String key = ByteUtil.toHexString(sb.toString());

            /** 3、使用银盛公钥对密钥key进行加密，得到加密的key并设置到请求参数check中。publicFilePath为存放银盛公钥的地址*/
            ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
            String publicKeyFilePath = channelCertConfigKitBean.getCertFilePath(isvParams.getPublicKeyFile());
            String check = null;

            BufferedInputStream inputStream = FileUtil.getInputStream(publicKeyFilePath);
            byte[] publicKeyEncoded = SecureUtil.readX509Certificate(inputStream).getPublicKey().getEncoded();
            SM2 sm2 = null;
            RSA rsa = null;
            if ("SM".equals(isvParams.getSignType())) {
                sm2 = SmUtil.sm2(null, publicKeyEncoded);
                check = sm2.encryptBase64(publicKeyFilePath, KeyType.PublicKey);
            } else if ("RSA".equals(isvParams.getSignType())) {
                rsa = SecureUtil.rsa(null, publicKeyEncoded);
                check = rsa.encryptBase64(ByteUtil.hexStringToBytes(key), KeyType.PublicKey);
            }

            //加密后的密钥
            reqParams.put("check", check);

            /** 4、封装业务参数 */

            /** 5、使用生成的密钥key对业务参数进行加密，并将加密后的业务参数放入请求参数bizContent中*/
            byte[] bte = AESUtil.encrypt(JSON.toJSONBytes(bizContent, SerializerFeature.WriteNullStringAsEmpty), ByteUtil.hexStringToBytes(key));
            reqParams.put("bizContent", org.apache.tomcat.util.codec.binary.Base64.encodeBase64String(bte));


            /** 6、将请求参数进行sort排序，生成拼接的字符床，并使用接入方私钥对请求参数进行加签，并将加签的值存入请求参数sign中*/
            String sign = getSign(isvParams, reqParams);
            reqParams.put("sign", sign);

            /** 7、发送http请求获取返回结果 */
            log.info("【银盛进件】请求地址：{}，发起请求：{}，加密前bizContent：{}", url, reqParams, bizContent);

            HttpRequest httpRequest;
            if (imgFileByteArray == null) {
                httpRequest = HttpRequest.post(url)
                        .header("Content-type", "application/json")
                        .timeout(20000)
                        .charset("UTF-8")
                        .body(reqParams.toJSONString());
            } else {
                httpRequest = HttpUtil.createPost(url)
                        .form("file", imgFileByteArray, "1.png")
                        .form(JSON.toJavaObject(reqParams, Map.class));
            }

            @Cleanup HttpResponse httpResponse = httpRequest.execute();
            String result = httpResponse.body();

            byte[] res = org.apache.tomcat.util.codec.binary.Base64.decodeBase64(result);
            Map<String, String> resMap = (Map<String, String>) JSONObject.parse(new String(res, StandardCharsets.UTF_8));
            log.info("【银盛接口】响应结果：{}", JSON.toJSONString(resMap));

            /** 8、对结果进行解密，并使用银盛公钥验签*/
            if (StringUtils.isNotBlank(resMap.get("sign"))) {
                if ("SM".equals(isvParams.getSignType())) {
                    byte[] srcData = GMSignUtils.getSignDataStr1(resMap).getBytes("UTF-8");
                    boolean validateSignResult1 = sm2.verify(srcData, Base64.decodeBase64(resMap.get("sign").getBytes(StandardCharsets.UTF_8)));
                    if (!validateSignResult1) {
                        log.info("【银盛进件】响应验签失败");
                        throw new BizException("验签失败");
                    }
                } else if ("RSA".equals(isvParams.getSignType())) {
                    byte[] srcData = SignUtil.getSignDataStr1(resMap).getBytes(StandardCharsets.UTF_8);
                    Sign sign2 = cn.hutool.crypto.SignUtil.sign(SignAlgorithm.SHA256withRSA, null, publicKeyEncoded);
                    boolean validateSignResult1 = sign2.verify(srcData, Base64.decodeBase64(resMap.get("sign").getBytes(StandardCharsets.UTF_8)));
                    if (!validateSignResult1) {
                        log.info("【银盛进件】响应验签失败");
                        throw new BizException("验签失败");
                    }
                }
            } else {
                throw new BizException("验签失败,未返回加签信息,可能是银盛未配置发起方或者发起方证书类型配置有误,返回结果提示为:{}" + resMap.get("msg"));
            }

            /** 9、针对自己的业务，根据返回的结果的code 和 subCode 做接入方的业务处理*/
            if (!APPLY_CODE.equals(resMap.get("code"))) {
                throw new BizException(StringUtils.isBlank(resMap.get("msg")) ? "请求响应失败" : resMap.get("msg"));
            }
            if (!APPLY_SUB_CODE.equals(resMap.get("subCode"))) {
                if ("B510091".equals(resMap.get("subCode"))) {
                    throw new BizException(ApiCodeEnum.YS_ARTIFICIAL_AUDIT_CODE);
                }

                throw new BizException(StringUtils.isBlank(resMap.get("subMsg")) ? "业务响应失败" : resMap.get("subMsg"));
            }

            /** 10、使用上面生成的加密密钥key，解密返回的业务参数*/
            if (StringUtils.isNotBlank(resMap.get("businessData"))) {
                byte[] data_ = org.apache.tomcat.util.codec.binary.Base64.decodeBase64(resMap.get("businessData"));
                byte[] data = AESUtil.decrypt(data_, ByteUtil.hexStringToBytes(key));

                String decryptResult = new String(data, StandardCharsets.UTF_8);
                resMap.put("data", decryptResult);
                resMap.remove("businessData");
            }
            log.info("【银盛进件】解密后的完整参数为：{}", JSON.toJSONString(resMap));

            return JSON.toJSONString(resMap);
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 图片上传接口
     *
     * @param sysFlowId
     * @param isvParams
     * @param imgUrl
     * @param picType
     * @return
     */
    public static JSONObject uploadRequest(String sysFlowId, YspayIsvParams isvParams, String imgUrl, String picType) {

        try {
            if (StringUtils.isEmpty(imgUrl)) {
                return null;
            }

            JSONObject bizContent = new JSONObject();

            // meta媒体文件元素信息
            Map<String, String> metaJSONObject = new HashMap<>();

            // 下载文件
            byte[] imgFileByteArray = null;
            try {
                ByteArrayOutputStream baos = ImageUtils.compressNetPic(imgUrl, 2048 * 1024);
                imgFileByteArray = baos.toByteArray();
            } catch (IORuntimeException e) {
                log.info("银盛入网压缩图片异常, 图片链接为{}", imgUrl, e);
                throw new BizException("银盛入网压缩图片失败, " + e.getMessage());
            }

            String sha256 = DigestUtils.sha256Hex(IoUtil.toStream(imgFileByteArray));

            metaJSONObject.put("sha256", sha256);
            // 图片类型， A001-营业执照 A002-法人身份证正面(头像面) A003-法人身份证反面(国徽面) A004-结算账户正面(卡号面) A005-结算账户反面 A006-商户门头照片 A007-内景照片 A008-收银台照片 A009-手持身份证合影照片 A010-收单协议盖章页 A011-开户许可证 A012-收单协议首页 A013-非法人身份证头像面 A014-非法人身份证国徽面 B001-租赁合同 第一页 B002-租赁合同 第二页 B003-租赁合同 第三页 B004-法人/非法人手持授权书 B005-法人/非法人结算授权书 B006-租赁面积图片 B007-经营业务图片 B008-其他1 B009-其他2
            metaJSONObject.put("picType", picType);
            // 图片名称
            metaJSONObject.put("picNm", IdUtil.fastSimpleUUID());
            // 流水号
            metaJSONObject.put("sysFlowId", sysFlowId);
            //设置媒体元素信息
            bizContent.put("meta", metaJSONObject);

            // 发送请求
            String resultStr = YspayKit.applymentRequest("/openapi/file/smsc/upload", "file.smsc.upload", isvParams, bizContent, imgFileByteArray);

            JSONObject responseJson = JSON.parseObject(resultStr);
            JSONObject dataJson = responseJson.getJSONObject("data");
            // String note = dataJson.getString("note");

            JSONObject respJson = new JSONObject();
            /*respJson.put("note", note);
            respJson.put("picType", picType);*/
            return respJson;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 图片上传接口
     *
     * @param changeFlowId
     * @param isvParams
     * @param imgUrl
     * @param picType
     * @return
     */
    public static JSONObject uploadEditRequest(String changeFlowId, YspayIsvParams isvParams, String imgUrl, String picType) {

        try {
            if (StringUtils.isEmpty(imgUrl)) {
                return null;
            }

            JSONObject bizContent = new JSONObject();

            // meta媒体文件元素信息
            Map<String, String> metaJSONObject = new HashMap<>();

            // 下载文件
            byte[] imgFileByteArray = HttpUtil.downloadBytes(imgUrl);
            String sha256 = DigestUtils.sha256Hex(IoUtil.toStream(imgFileByteArray));

            metaJSONObject.put("sha256", sha256);
            // 图片类型， A001-营业执照 A002-法人身份证正面(头像面) A003-法人身份证反面(国徽面) A004-结算账户正面(卡号面) A005-结算账户反面 A006-商户门头照片 A007-内景照片 A008-收银台照片 A009-手持身份证合影照片 A010-收单协议盖章页 A011-开户许可证 A012-收单协议首页 A013-非法人身份证头像面 A014-非法人身份证国徽面 B001-租赁合同 第一页 B002-租赁合同 第二页 B003-租赁合同 第三页 B004-法人/非法人手持授权书 B005-法人/非法人结算授权书 B006-租赁面积图片 B007-经营业务图片 B008-其他1 B009-其他2
            metaJSONObject.put("picType", picType);
            // 图片名称
            metaJSONObject.put("picNm", IdUtil.fastSimpleUUID());
            // 流水号
            metaJSONObject.put("changeFlowId", changeFlowId);
            //设置媒体元素信息
            bizContent.put("meta", metaJSONObject);

            // 发送请求
            String resultStr = YspayKit.applymentRequest("/openapi/file/smsc/uploadChangePic", "file.smsc.uploadChangePic", isvParams, bizContent, imgFileByteArray);

            JSONObject responseJson = JSON.parseObject(resultStr);
            JSONObject dataJson = responseJson.getJSONObject("data");
            // String note = dataJson.getString("note");

            JSONObject respJson = new JSONObject();
            /*respJson.put("note", note);
            respJson.put("picType", picType);*/
            return respJson;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 银盛V3接口
     *
     * @param method
     * @param bizContent
     * @return
     */
    public static JSONObject reqV3(ReqMethod.Method method, YspayIsvParams isvParams, JSONObject bizContent) {
        try {
            // 公共参数
            JSONObject reqParams = new JSONObject();
            String key = initV3(method, isvParams, bizContent, reqParams);
            /** 7、发送http请求获取返回结果 */
            log.info("【银盛进件】请求地址：{}，请求参数：{}，业务参数：{}", ReqMethod.getUrl(method), reqParams, bizContent);
            String result = HttpRequest.post(ReqMethod.getUrl(method))
                    .header("Content-type", "application/json")
                    .timeout(20000)
                    .charset("UTF-8")
                    .body(reqParams.toJSONString())
                    .execute()
                    .body();
            return initResult(result, isvParams, key);
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 通用处理请求返回结果
     *
     * @param result
     * @param isvParams
     * @param key
     * @return
     */
    // 统一初始化返回参数
    private static JSONObject initResult(String result, YspayIsvParams isvParams, String key) {
        try {
            byte[] res = org.apache.tomcat.util.codec.binary.Base64.decodeBase64(result);
            Map<String, String> resMap = (Map<String, String>) JSONObject.parse(new String(res, StandardCharsets.UTF_8));
            log.info("【银盛进件】响应结果：{}", JSON.toJSONString(resMap));
            /** 8、对结果进行解密，并使用银盛公钥验签*/
            if (StringUtils.isNotBlank(resMap.get("sign"))) {
                if ("SM".equals(isvParams.getSignType())) {
                    byte[] srcData = GMSignUtils.getSignDataStr1(resMap).getBytes("UTF-8");
                    GMCertInfo verifyCertInfo = GMSignUtils.getVerifyCertInfo(isvParams.getPublicKeyFile());
                    boolean validateSignResult = GMSignUtils.verifyMsgSignSM2(verifyCertInfo, org.apache.tomcat.util.codec.binary.Base64.decodeBase64(resMap.get("sign")), srcData);
                    if (!validateSignResult) {
                        log.info("【银盛进件】响应验签失败");
                        throw new BizException("验签失败");
                    }
                } else if ("RSA".equals(isvParams.getSignType())) {
                    byte[] srcData = SignUtil.getSignDataStr1(resMap).getBytes(StandardCharsets.UTF_8);
                    boolean validateSignResult = RSA256Util.validateSign(isvParams.getPublicKeyFile(), org.apache.tomcat.util.codec.binary.Base64.decodeBase64(resMap.get("sign")), srcData);
                    if (!validateSignResult) {
                        log.info("【银盛进件】响应验签失败");
                        throw new BizException("验签失败");
                    }
                }
                if (!APPLY_CODE.equals(resMap.get("code"))) {
                    throw new BizException(StringUtils.isBlank(resMap.get("msg")) ? "请求响应失败" : resMap.get("msg"));
                }
                if (!APPLY_SUB_CODE.equals(resMap.get("subCode"))) {
                    throw new BizException(StringUtils.isBlank(resMap.get("subMsg")) ? "业务响应失败" : resMap.get("subMsg"));
                }

                /** 10、使用上面生成的加密密钥key，解密返回的业务参数*/
                if (StringUtils.isNotBlank(resMap.get("businessData"))) {
                    byte[] data_ = org.apache.tomcat.util.codec.binary.Base64.decodeBase64(resMap.get("businessData"));
                    byte[] data = AESUtil.decrypt(data_, ByteUtil.hexStringToBytes(key));
                    String decryptResult = new String(data, StandardCharsets.UTF_8);
                    log.info("【银盛】响应参数解析结果：{}", decryptResult);
                    return JSON.parseObject(decryptResult);
                }
                return null;
            } else {
                throw new BizException("验签失败,未返回加签信息,可能是银盛未配置发起方或者发起方证书类型配置有误,返回结果提示为:{}" + resMap.get("msg"));
            }
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 初始化公共参数
     *
     * @param method
     * @param isvParams
     * @param bizContent
     * @param ifCode
     * @param reqParams
     * @return
     */
    public static String initV3(ReqMethod.Method method, YspayIsvParams isvParams, JSONObject bizContent, JSONObject reqParams) {
        try {
            // 接口名称
            reqParams.put("method", method.getMethod());
            // 请求的时间
            reqParams.put("timeStamp", DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN));
            // 编码格式
            reqParams.put("charset", "utf-8");
            // 请求流水号
            reqParams.put("reqId", DateKit.currentTimeMillis() + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN));
            // 通道服务商号
            reqParams.put("certId", isvParams.getPartnerId());
            // 版本号
            reqParams.put("version", method.getVersion());
            StringBuilder sb = new StringBuilder();
            Random random = new Random();
            for (int i = 0; i < 16; i++) {
                sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
            }
            String key = ByteUtil.toHexString(sb.toString());

            /** 3、使用银盛公钥对密钥key进行加密，得到加密的key并设置到请求参数check中。publicFilePath为存放银盛公钥的地址*/
            ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
            String publicKeyFilePath = channelCertConfigKitBean.getCertFilePath(isvParams.getPublicKeyFile());
            String check = null;
            if ("SM".equals(isvParams.getSignType())) {
                check = GMSignUtils.encryptData(publicKeyFilePath, key);
            } else if ("RSA".equals(isvParams.getSignType())) {
                byte[] byte1 = RSA256Util.encrypt(publicKeyFilePath, ByteUtil.hexStringToBytes(key));
                check = org.apache.tomcat.util.codec.binary.Base64.encodeBase64String(byte1);
            }
            //加密后的密钥
            reqParams.put("check", check);
            /** 4、封装业务参数 */
            /** 5、使用生成的密钥key对业务参数进行加密，并将加密后的业务参数放入请求参数bizContent中*/
            byte[] bte = AESUtil.encrypt(JSON.toJSONBytes(bizContent, SerializerFeature.WriteNullStringAsEmpty), ByteUtil.hexStringToBytes(key));
            reqParams.put("bizContent", org.apache.tomcat.util.codec.binary.Base64.encodeBase64String(bte));
            String sign = getSign(isvParams, reqParams);
            reqParams.put("sign", sign);
            return key;
        } catch (Exception e) {
            throw new BizException("银盛签名异常");
        }
    }

    public static void main(String[] args) {
        String aa = "{\"bizContent\":\"hLAQku6C1EPL1nKuo4WUztUbTYET4tNTJ03Nda9aX52n6Aa1pWFi+Q0hL5AuGgDaZrps4a4795UWvooTMWPhvcbNWW4I161Rq/kGMEYxv+78JWKa4TbSZZclkXwsv343J48FFcE5gIuM7s46tLG6ag==\",\"certId\":\"826521773920170\",\"charset\":\"utf-8\",\"check\":\"cUZ3neRqOJlvVxaIRWQlrsH8GgKVsXRmYHv6Xa/n5rSzIx/0SyONAi5IxHm0pNqb5WfGYS+A5sFs6lFqREvevnSmeWpnOsSaaGcUYbZu8AMchNeOluJzH/j52iqH+98tl9IlOABtZk9IllFIbiKHhWSzONR2KSBcpU58MbEpJaA=\",\"method\":\"report.scan.union.appIdAddOrUpdate\",\"reqId\":\"SYB_5vpw231204151549\",\"sign\":\"QhrmcOJKjBvkFADf6K+6WjzQR0WG1iXgmUkRKoFqF1sBFuTDhVfBqda/FSmEZFA4bEJbkazeMF7NruGBzktJUyMsCruS9O5dfW/QSZ46iUVvo0iYS+2DeAfv53SlEnjXMTZaSpBr+TeF/9WhDMfBo+siWdutOjOE6lERA/05lxnqi7fftZ9Usikl+ODF97iDfg0if+CpQ3eKq+o31h1kd2hHTtiL/W7BVs0E313IqAx41VXt2aH0mUcOLuvQOKQ01ux++0gM0Ooh+jreHUiPgT9XoZI6OYFV78QA6aeTvmW8ABVXA3k2Xr0NVvvj0zpOzSpt+c7dUktBAGk0YwfVAA==\",\"timeStamp\":\"2023-12-04 15:15:49\",\"version\":\"1.0\"}";
        JSONObject data = JSON.parseObject(aa);
        String check = data.getString("check");
        String pubKeyPath = "D:\\home\\jeepay\\upload\\private\\cert\\82483d5d-fab4-42fd-a56e-4febb43c1316.cer";

        System.out.println(check);
    }
}
