public class LklCrossPayEncryptReq extends LklCrossPaySuperReq {

    private static final long serialVersionUID = 1182200158599407155L;

    /**
     * 对称密钥
     */
    private String encKey;

    /**
     * 加密业务数据
     */
    private String encData;

    private String mac;


    public String getEncKey() {
        return encKey;
    }

    public void setEncKey(String encKey) {
        this.encKey = encKey;
    }

    public String getEncData() {
        return encData;
    }

    public void setEncData(String encData) {
        this.encData = encData;
    }

    public String getMac() {
        return mac;
    }

    public void setMac(String mac) {
        this.mac = mac;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("LklCrossPayEncryptReq{");
        sb.append("encKey='").append(encKey).append('\'');
        sb.append(", encData='").append(encData).append('\'');
        sb.append(", mac='").append(mac).append('\'');
        sb.append('}');
        return sb.toString();
    }
} public class LklCrossPayEncryptRes extends LklCrossPaySuperRes {

    private static final long serialVersionUID = -8037859172913791796L;

    private String encData;

    private String encKey;

    private String mac;

    public String getEncData() {
        return encData;
    }

    public void setEncData(String encData) {
        this.encData = encData;
    }

    public String getEncKey() {
        return encKey;
    }

    public void setEncKey(String encKey) {
        this.encKey = encKey;
    }

    public String getMac() {
        return mac;
    }

    public void setMac(String mac) {
        this.mac = mac;
    }


    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("LklCrossPayEncryptRes{");
        sb.append("super='").append(super.toString()).append('\'');
        sb.append(", encData='").append(encData).append('\'');
        sb.append(", encKey='").append(encKey).append('\'');
        sb.append(", mac='").append(mac).append('\'');
        sb.append('}');
        return sb.toString();
    }
} public class CustomAuthNotify extends SuperWebHookRequest{

	private static final long serialVersionUID = -5696880953839332079L;

   private String orderNo; //商户订单号
	
	private String payOrderId;//支付订单号
	
	private String name;//姓名
	
	private String mobile;//手机号码
	
	private String amount;//金额
	
	private String cuId;//海关
	
	private String customcomCode;//海关备案号
	
	private String cbpName;//海关备案号名称
	
	private String clientId;//身份证号
	
	private String payerMail;//邮箱
	
	private String bgUrl;//后台通知地址
	
	private String goodsFee;//商品货款金额
	
	private String taxFee;//税款金额
	
	private String bizTypeCode;//业务类型
	
	private String orderNote;//订单备注
	
	private String retCode;//
	
	private String retMsg;//

	public String getOrderNo() {
		return orderNo;
	}

	public void setOrderNo(String orderNo) {
		this.orderNo = orderNo;
	}

	public String getPayOrderId() {
		return payOrderId;
	}

	public void setPayOrderId(String payOrderId) {
		this.payOrderId = payOrderId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getMobile() {
		return mobile;
	}

	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	public String getAmount() {
		return amount;
	}

	public void setAmount(String amount) {
		this.amount = amount;
	}

	public String getCuId() {
		return cuId;
	}

	public void setCuId(String cuId) {
		this.cuId = cuId;
	}

	public String getCustomcomCode() {
		return customcomCode;
	}

	public void setCustomcomCode(String customcomCode) {
		this.customcomCode = customcomCode;
	}

	public String getCbpName() {
		return cbpName;
	}

	public void setCbpName(String cbpName) {
		this.cbpName = cbpName;
	}

	public String getClientId() {
		return clientId;
	}

	public void setClientId(String clientId) {
		this.clientId = clientId;
	}

	public String getPayerMail() {
		return payerMail;
	}

	public void setPayerMail(String payerMail) {
		this.payerMail = payerMail;
	}

	public String getBgUrl() {
		return bgUrl;
	}

	public void setBgUrl(String bgUrl) {
		this.bgUrl = bgUrl;
	}

	public String getGoodsFee() {
		return goodsFee;
	}

	public void setGoodsFee(String goodsFee) {
		this.goodsFee = goodsFee;
	}

	public String getTaxFee() {
		return taxFee;
	}

	public void setTaxFee(String taxFee) {
		this.taxFee = taxFee;
	}

	public String getBizTypeCode() {
		return bizTypeCode;
	}

	public void setBizTypeCode(String bizTypeCode) {
		this.bizTypeCode = bizTypeCode;
	}

	public String getOrderNote() {
		return orderNote;
	}

	public void setOrderNote(String orderNote) {
		this.orderNote = orderNote;
	}

	public String getRetCode() {
		return retCode;
	}

	public void setRetCode(String retCode) {
		this.retCode = retCode;
	}

	public String getRetMsg() {
		return retMsg;
	}

	public void setRetMsg(String retMsg) {
		this.retMsg = retMsg;
	}
	
	
}
 public class CustomAuthResultNotifyRes extends SuperWebHookResponse{
	private static final long serialVersionUID = -1059378608568656312L;

	private String orderNo; //商户订单号
	
	private String payOrderId;//支付订单号
	
	private String name;//姓名
	
	private String mobile;//手机号码
	
	private String amount;//金额
	
	private String cuId;//海关
	
	private String customcomCode;//海关备案号
	
	private String cbpName;//海关备案号名称
	
	private String clientId;//身份证号
	
	private String payerMail;//邮箱
	
	private String bgUrl;//后台通知地址
	
	private String goodsFee;//商品货款金额
	
	private String taxFee;//税款金额
	
	private String bizTypeCode;//业务类型
	
	private String orderNote;//订单备注

	public String getOrderNo() {
		return orderNo;
	}

	public void setOrderNo(String orderNo) {
		this.orderNo = orderNo;
	}

	public String getPayOrderId() {
		return payOrderId;
	}

	public void setPayOrderId(String payOrderId) {
		this.payOrderId = payOrderId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getMobile() {
		return mobile;
	}

	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	public String getAmount() {
		return amount;
	}

	public void setAmount(String amount) {
		this.amount = amount;
	}

	public String getCuId() {
		return cuId;
	}

	public void setCuId(String cuId) {
		this.cuId = cuId;
	}

	public String getCustomcomCode() {
		return customcomCode;
	}

	public void setCustomcomCode(String customcomCode) {
		this.customcomCode = customcomCode;
	}

	public String getCbpName() {
		return cbpName;
	}

	public void setCbpName(String cbpName) {
		this.cbpName = cbpName;
	}

	public String getClientId() {
		return clientId;
	}

	public void setClientId(String clientId) {
		this.clientId = clientId;
	}

	public String getPayerMail() {
		return payerMail;
	}

	public void setPayerMail(String payerMail) {
		this.payerMail = payerMail;
	}

	public String getBgUrl() {
		return bgUrl;
	}

	public void setBgUrl(String bgUrl) {
		this.bgUrl = bgUrl;
	}

	public String getGoodsFee() {
		return goodsFee;
	}

	public void setGoodsFee(String goodsFee) {
		this.goodsFee = goodsFee;
	}

	public String getTaxFee() {
		return taxFee;
	}

	public void setTaxFee(String taxFee) {
		this.taxFee = taxFee;
	}

	public String getBizTypeCode() {
		return bizTypeCode;
	}

	public void setBizTypeCode(String bizTypeCode) {
		this.bizTypeCode = bizTypeCode;
	}

	public String getOrderNote() {
		return orderNote;
	}

	public void setOrderNote(String orderNote) {
		this.orderNote = orderNote;
	}
	
	
	
}
 public enum LklEnv {

    SANDBOX("sandbox"),
    LIVE("live");

    LklEnv(String env) {
        this.env = env;
    }

    private String env;

    public String getEnv() {
        return env;
    }

    public void setEnv(String env) {
        this.env = env;
    }
} public class LklCommonException extends RuntimeException {

    public LklCommonException(Throwable t) {
        super(t);
    }

    public LklCommonException(String errorMessage) {
        super(errorMessage);
    }

    public LklCommonException(String errorMessage, Throwable cause) {
        super(errorMessage, cause);
    }
} public class LklEncryptException extends RuntimeException {
    public LklEncryptException(Throwable t) {
        super(t);
    }

    public LklEncryptException(String errorMessage) {
        super(errorMessage);
    }

    public LklEncryptException(String errorMessage, Throwable cause) {
        super(errorMessage, cause);
    }
} @Component
public class LklCrossPayEnv {

    @Autowired
    @Qualifier("yamlMap")
    private Map<String, Object> yamlMap;

    private static Map<String, Object> YML_MAP;

    private static final Map<LklEnv, LklCrossPayClientConfig> ENV_CONFIG = new ConcurrentHashMap<LklEnv, LklCrossPayClientConfig>();

    @PostConstruct
    public void afterPropertiesSet() throws Exception {
//        YML_MAP = ctx.getBean("yamlMap", Map.class);
        YML_MAP = (Map<String, Object>) yamlMap.get("yamlMap");
    }


    /**
     * 获取当前环境的配置信息
     *
     * @return
     */
    public static LklCrossPayClientConfig getEnvConfig() {
        Iterator<Map.Entry<LklEnv, LklCrossPayClientConfig>> it = ENV_CONFIG.entrySet().iterator();
        LklCrossPayClientConfig config = null;
        if (it.hasNext()) {
            config = it.next().getValue();
        }
        return config;

    }

    /**
     * 注册拉卡拉支付平台环境,一个应用实例只能注册一个环境
     *
     * @param env        拉卡拉环境枚举 {@link LklEnv}
     * @param merId      该环境对应的商户号
     * @param privateKey 该环境对应的私钥
     * @param publicKey  该环境对应的公钥
     */
    public synchronized void registerEnv(LklEnv env, String merId, String privateKey, String publicKey) {
        String envValue = env.getEnv();
        //一个实例只允许注册一个环境
        if (!ENV_CONFIG.isEmpty()) {
            return;
        } else {
            LklCrossPayClientConfig config = new LklCrossPayClientConfig();
            Map<String, String> envObj = (LinkedHashMap<String, String>) YML_MAP.get(envValue);
            config.setPort(String.valueOf(envObj.get("port")));
            config.setPrivateKey(privateKey);
            config.setPublicKey(publicKey);
            config.setServer(String.valueOf(envObj.get("server")));
            config.setMerId(merId);
            ENV_CONFIG.put(env, config);

        }
    }
} public class LklMsgUtil {


    /**
     * <p>
     * 加密请求参数
     * encrypt the msg sent to lakala.the des key is stored in threadlocal, which means if u want to get the right des key to decrypt lakala msg ,
     * u should guarantee the thread is the same  to the one used to send the request msg.Otherwise u should not store the des key to threadlocal
     * </p>
     *
     * @param obj      参数对象
     * @param dataHead 报文头
     * @param <T>
     * @return LklCrossPayEncryptReq 返回一个加密后的请求参数，用于向拉卡拉跨境支付平台发起请求
     * @throws LklEncryptException
     */
    public static <T> LklCrossPayEncryptReq encryptMsg(T obj, LklCrossPaySuperReq dataHead) throws LklEncryptException {
        ToolsUtil.remove();
        String publicKey = LklCrossPayEnv.getEnvConfig().getPublicKey();
        String privateKey = LklCrossPayEnv.getEnvConfig().getPrivateKey();
        String merId = LklCrossPayEnv.getEnvConfig().getMerId();
        String ts = dataHead.getTs();
        String ver = dataHead.getVer();

        //生成32位随机串
        //generate des key ,length is 32.the key is used to encrypt the biz data
        String secKey = ToolsUtil.getMerKey();
        //时间戳拼接对称密钥的hex，用响应方公钥加密，生成加密密钥密文，hex编码,生成encKey
        //the unencrypted enckey string =timestamp(yyyyMMddHHmmssssssss.refer to DateUtil.getCurrentTime())+des key
        String encKeyStr = ts + secKey;
        String encKey = null;
        try {
            //encrypted encKey
            encKey = ByteArrayUtil.byteArray2HexString(RSAUtil.encryptByPublicKey(encKeyStr.getBytes("GBK"), publicKey));
        } catch (Exception e) {
            throw new LklEncryptException("生成encKey失败", e);
        }
        Gson json = new Gson();
        String bizJson = json.toJson(obj);
        //生成encData
        String encData;
        try {
            if ("3.0.0".equals(ver.trim())) {
                //use seckey to encrypt the biz data
                encData = ByteArrayUtil.byteArray2HexString(AESCrypto.encrypt(bizJson, secKey));
            } else {
                //use seckey to encrypt the biz data
                encData = ByteArrayUtil.byteArray2HexString(DESCrypto.enCrypto(bizJson.getBytes("GBK"), secKey));
            }
        } catch (UnsupportedEncodingException e) {
            throw new LklEncryptException("加密业务参数失败", e);
        }


        //the unencrypted mac string
        String macStr = null;
        if ("1.0.0".equals(ver.trim())) {
            String reqType = dataHead.getReqType();
            String payTypeId = dataHead.getPayTypeId();
            if (null == payTypeId || "".equals(payTypeId)) {
                macStr = merId + ver + ts + reqType + encData + "";
            } else {
                macStr = merId + ver + ts + reqType + encData + payTypeId;
            }
        } else if ("2.0.0".equals(ver.trim()) || "3.0.0".equals(ver.trim())) {
            macStr = merId + ver + ts + encData;
        }
        //first encrypt mac with sha
        if ("3.0.0".equals(ver.trim())) {
            macStr = DigestUtil.Encrypt(macStr, "SHA-256");
        } else {
            macStr = DigestUtil.Encrypt(macStr, "SHA-1");
        }
        //生成MAC
        String mac = null;
        try {
            //finally encrypt mac with rsa,
            mac = ByteArrayUtil.byteArray2HexString(RSAUtil.encryptByPrivateKey(macStr.getBytes("GBK"), privateKey));
        } catch (Exception e) {
            throw new LklEncryptException(e);
        }

        //组装加密请求参数
        LklCrossPayEncryptReq req = new LklCrossPayEncryptReq();
        req.setMerId(merId);
        req.setPayTypeId(dataHead.getPayTypeId());
        req.setVer(dataHead.getVer());
        req.setTs(dataHead.getTs());
        req.setEncData(encData);
        req.setEncKey(encKey);
        req.setMac(mac);
        req.setReqType(dataHead.getReqType());

        return req;
    }

    /**
     * <p>
     * 解密拉卡拉响应报文
     * decrypt the response msg back from lakala.the des key should be the same to which used to encrypt the msg sent to lakala
     * </p>
     *
     * @param encryptRes 拉卡拉加密响应报文
     * @param <T>        拉卡拉接口业务参数,扩展自父类LklCrossPaySuperRes {@link LklCrossPaySuperRes}
     * @return
     * @throws LklEncryptException
     */
    public static <T extends LklCrossPaySuperRes> T decrypt(LklCrossPayEncryptRes encryptRes, Class<T> resClazz) throws LklEncryptException {
        T result = null;
        String retCode = encryptRes.getRetCode();
        String retMsg = encryptRes.getRetMsg();
        String merId = encryptRes.getMerId();
        String ver = encryptRes.getVer();
        String ts = encryptRes.getTs();
        String encData = encryptRes.getEncData();
        String reqType = encryptRes.getReqType();
        String payTypeId = encryptRes.getPayTypeId();
        //first get mac from the respinse msg
        String mac = encryptRes.getMac();
        //本地mac sha1字符串
        //your local mac string
        String retMacStr = null;
        //请求参数mac解密字符串sha1
        String reqMacStr = null;
        if ("1.0.0".equals(ver.trim())) {
            if (null == payTypeId || "".equals(payTypeId.trim())) {
                retMacStr = DigestUtil.Encrypt(retCode + retMsg + merId + ver + ts + reqType + encData + "", "SHA-1");
            } else {
                retMacStr = DigestUtil.Encrypt(retCode + retMsg + merId + ver + ts + reqType + encData + payTypeId, "SHA-1");
            }
        } else if ("2.0.0".equals(ver.trim())) {
            retMacStr = DigestUtil.Encrypt(retCode + retMsg + merId + ver + ts + encData, "SHA-1");
        } else if ("3.0.0".equals(ver.trim())) {
            retMacStr = DigestUtil.Encrypt(retCode + retMsg + merId + ver + ts + encData, "SHA-256");
        }
        try {
            //then generate ur local encrypted mac string
            reqMacStr = new String(RSAUtil.decryptByPublicKey(ByteArrayUtil.hexString2ByteArray(mac), LklCrossPayEnv.getEnvConfig().getPublicKey()));
        } catch (Exception e) {
            throw new LklEncryptException("mac解密失败", e);
        }
        //比较报文中mac sha1与本地mac sha是否相等
        //see if ur local mac is the same to mac from lakala
        if (!reqMacStr.equals(retMacStr)) {
            throw new LklEncryptException("mac校验失败");
        }
        //解密业务参数
        //get des key from threadlocal.
        String key = ToolsUtil.getMerKey();
        String requestData = null;

        //decrypt response msg from lakala
        try {
            if ("3.0.0".equals(ver.trim())) {
                requestData = new String(AESCrypto.decrypt(ByteArrayUtil.hexString2ByteArray(encData), key), "GBK");
            } else {
                requestData = new String(DESCrypto.deCrypt(ByteArrayUtil.hexString2ByteArray(encData), key), "GBK");
            }
        } catch (UnsupportedEncodingException e) {
            throw new LklEncryptException("业务参数解密失败", e);
        }

        Gson json = new Gson();
        result = json.fromJson(requestData, resClazz);
        ToolsUtil.remove();
        return result;
    }


    /**
     * 解密拉卡拉回调消息.
     * <b>ver=3.0.0的回调消息中会ver这个字段，其他则没有</b>
     * decrypt the webhook msg from lakala,des key should  get from the method :getMerchantKey
     *
     * @param lklCrossPayEncryptReq
     * @param resClazz
     * @param <T>
     * @return
     */
    public static <T extends SuperWebHookRequest> T decryptMsgFromLkl(LklCrossPayEncryptReq lklCrossPayEncryptReq, Class<T> resClazz) {
        T result = null;
        String ts = lklCrossPayEncryptReq.getTs();
        String encData = lklCrossPayEncryptReq.getEncData();
        String reqType = lklCrossPayEncryptReq.getReqType();
        String ver = lklCrossPayEncryptReq.getVer();
        //encrypted mac string from lakala
        String mac = lklCrossPayEncryptReq.getMac();

        //请求参数mac解密字符串sha1
        //your local mac string
        String reqMacStr = null;
        //本地mac sha1字符串
        String retMacStr = DigestUtil.Encrypt(ts + reqType + encData + "", "SHA-1");
        if ("3.0.0".equals(ver.trim())) {
            retMacStr = DigestUtil.Encrypt(ts + reqType + encData + "", "SHA-256");
        }
        try {
            reqMacStr = new String(RSAUtil.decryptByPublicKey(ByteArrayUtil.hexString2ByteArray(mac), LklCrossPayEnv.getEnvConfig().getPublicKey()));
        } catch (Exception e) {
            throw new LklEncryptException("mac解密失败", e);
        }
        if (!retMacStr.equals(reqMacStr)) {
            throw new LklEncryptException("mac校验失败");
        }
        //用响应方私钥解密加密密钥密文，比对时间戳，取后32个字符反HEX，得对称密钥
        //get encKey from the web hook msg
        String encKey = lklCrossPayEncryptReq.getEncKey();
        //get des key from the enckey
        String desKey = getMerchantKey(encKey, LklCrossPayEnv.getEnvConfig().getPrivateKey());
        ToolsUtil.remove();
        // if u guarantee the thread is the same  to the one used to send the web hook response msg then u can store the key in threadlocal.Otherwise u should not store the des key to threadlocal
        //store des key ,when u send response msg ,u can use it
        ToolsUtil.setMerKey(desKey);
        //用获得的对称密钥解密加密业务参数
        String requestData = null;
        try {
            //decrypt biz data with key
            if ("3.0.0".equals(ver.trim())) {
                requestData = new String(AESCrypto.decrypt(ByteArrayUtil.hexString2ByteArray(encData), desKey), "GBK");
            } else {
                requestData = new String(DESCrypto.deCrypt(ByteArrayUtil.hexString2ByteArray(encData), desKey), "GBK");
            }

        } catch (UnsupportedEncodingException e) {
            throw new LklEncryptException("业务参数解密失败", e);
        }
        Gson json = new Gson();
        result = json.fromJson(requestData, resClazz);
        return result;

    }

    /**
     * 加密拉卡拉webhook响应返回给拉卡拉
     * encrypt the webhook response msg sent back to lakala
     *
     * @param webHookResponse
     * @return
     */
    public static LklCrossPayEncryptRes encryptWebHookMsg(SuperWebHookResponse webHookResponse, LklCrossPayEncryptRes encryptRes) {
        String publicKey = LklCrossPayEnv.getEnvConfig().getPublicKey();
        String privateKey = LklCrossPayEnv.getEnvConfig().getPrivateKey();

        String retCode = encryptRes.getRetCode();
        String retMsg = encryptRes.getRetMsg();
        String ts = encryptRes.getTs();
        String ver = encryptRes.getVer();
        //get seckey from threadlocal
        String secKey = ToolsUtil.getMerKey();
        //unencrypted  enc key string
//        String encKeyStr = ts + secKey;
//        String encKey = null;
//        try {
//            //encrypt enc key string
//            encKey = ByteArrayUtil.byteArray2HexString(RSAUtil.encryptByPublicKey(encKeyStr.getBytes("GBK"), publicKey));
//        } catch (Exception e) {
//            throw new LklEncryptException("生成encKey失败", e);
//        }
        Gson json = new Gson();

        //生成encData
        String encData = "";
        try {
            String bizJson = json.toJson(webHookResponse);
            //encrypt biz data with the des key
            if ("3.0.0".equals(ver.trim())) {
                encData = ByteArrayUtil.byteArray2HexString(AESCrypto.encrypt(bizJson, secKey));
            } else {
                encData = ByteArrayUtil.byteArray2HexString(DESCrypto.enCrypto(bizJson.getBytes("GBK"), secKey));
            }

        } catch (UnsupportedEncodingException e) {
            throw new LklEncryptException("加密业务参数失败", e);
        }
        String reqType = encryptRes.getReqType();
        //generate mac string
        String macStr = null;
        //SHA
        if ("3.0.0".equals(ver.trim())) {
            macStr = retCode + retMsg + ts + reqType;
            macStr = DigestUtil.Encrypt(macStr, "SHA-256");
        } else {
            macStr = ts + reqType + encData;
            macStr = DigestUtil.Encrypt(macStr, "SHA-1");
        }
        //生成MAC
        String mac = null;
        try {
            //encryot mac string
            mac = ByteArrayUtil.byteArray2HexString(RSAUtil.encryptByPrivateKey(macStr.getBytes("GBK"), privateKey));
        } catch (Exception e) {
            throw new LklEncryptException(e);
        }
        encryptRes.setMac(mac);
//        encryptRes.setEncKey(encKey);
        encryptRes.setEncData(encData);
        ToolsUtil.remove();
        return encryptRes;
    }


    /**
     * 计算请求方对称密钥
     *
     * @param reqEncKey  加密密钥encKey
     * @param privateKey 私钥
     * @return
     */
    private final static String getMerchantKey(String reqEncKey, String privateKey) {

        if (reqEncKey == null || privateKey == null) return null;
        // 用响应方私钥解密加密密钥密文，比对时间戳，取后32个字符反HEX，得对称密钥
        String merKey = ""; // 商户对称密钥
        try {
            merKey = new String(RSAUtil.decryptByPrivateKey(ByteArrayUtil.hexString2ByteArray(reqEncKey), privateKey), "GBK");
        } catch (Exception e) {
            throw new LklEncryptException("解密请求方对称密钥失败", e);
        }
        return merKey.substring(merKey.length() - 32, merKey.length());
    }
    
    
    public static <T extends SuperWebHookRequest> T decryptMsgFromLklCustomAuth(LklCrossPayEncryptReq lklCrossPayEncryptReq, Class<T> resClazz) {
        T result = null;
        String ts = lklCrossPayEncryptReq.getTs();
        String encData = lklCrossPayEncryptReq.getEncData();
        String reqType = lklCrossPayEncryptReq.getReqType();
        String ver = lklCrossPayEncryptReq.getVer();
        //encrypted mac string from lakala
        String mac = lklCrossPayEncryptReq.getMac();
        String merId=lklCrossPayEncryptReq.getMerId();
        //请求参数mac解密字符串sha1
        //your local mac string
        String reqMacStr = null;
        String retMacStr=null;
        //本地mac sha1字符串
        String macStr=merId + ver + ts + encData;
        if ("3.0.0".equals(ver.trim())) {
        	retMacStr= DigestUtil.Encrypt(macStr, "SHA-256");
        }
        try {
            reqMacStr = new String(RSAUtil.decryptByPublicKey(ByteArrayUtil.hexString2ByteArray(mac), LklCrossPayEnv.getEnvConfig().getPublicKey()));
        } catch (Exception e) {
            throw new LklEncryptException("mac解密失败", e);
        }
        if (!retMacStr.equals(reqMacStr)) {
            throw new LklEncryptException("mac校验失败");
        }
        //用响应方私钥解密加密密钥密文，比对时间戳，取后32个字符反HEX，得对称密钥
        //get encKey from the web hook msg
        String encKey = lklCrossPayEncryptReq.getEncKey();
        //get des key from the enckey
        String desKey = getMerchantKey(encKey, LklCrossPayEnv.getEnvConfig().getPrivateKey());
        ToolsUtil.remove();
        // if u guarantee the thread is the same  to the one used to send the web hook response msg then u can store the key in threadlocal.Otherwise u should not store the des key to threadlocal
        //store des key ,when u send response msg ,u can use it
        ToolsUtil.setMerKey(desKey);
        //用获得的对称密钥解密加密业务参数
        String requestData = null;
        try {
            //decrypt biz data with key
            if ("3.0.0".equals(ver.trim())) {
                requestData = new String(AESCrypto.decrypt(ByteArrayUtil.hexString2ByteArray(encData), desKey), "GBK");
            } else {
                requestData = new String(DESCrypto.deCrypt(ByteArrayUtil.hexString2ByteArray(encData), desKey), "GBK");
            }

        } catch (UnsupportedEncodingException e) {
            throw new LklEncryptException("业务参数解密失败", e);
        }
        Gson json = new Gson();
        result = json.fromJson(requestData, resClazz);
        return result;

    }
    
    public static LklCrossPayEncryptRes encryptWebHookMsgCustomAuth(SuperWebHookResponse webHookResponse, LklCrossPayEncryptRes encryptRes) {
        String publicKey = LklCrossPayEnv.getEnvConfig().getPublicKey();
        String privateKey = LklCrossPayEnv.getEnvConfig().getPrivateKey();

        String retCode = encryptRes.getRetCode();
        String retMsg = encryptRes.getRetMsg();
        String ts = encryptRes.getTs();
        String ver = encryptRes.getVer();
        String merId=encryptRes.getMerId();
        //get seckey from threadlocal
        String secKey = encryptRes.getEncKey();
        //unencrypted  enc key string
//        String encKeyStr = ts + secKey;
//        String encKey = null;
//        try {
//            //encrypt enc key string
//            encKey = ByteArrayUtil.byteArray2HexString(RSAUtil.encryptByPublicKey(encKeyStr.getBytes("GBK"), publicKey));
//        } catch (Exception e) {
//            throw new LklEncryptException("生成encKey失败", e);
//        }
        Gson json = new Gson();

        //生成encData
        String encData = "";
        try {
            String bizJson = json.toJson(webHookResponse);
            //encrypt biz data with the des key
            if ("3.0.0".equals(ver.trim())) {
                encData = ByteArrayUtil.byteArray2HexString(AESCrypto.encrypt(bizJson, secKey));
            } else {
                encData = ByteArrayUtil.byteArray2HexString(DESCrypto.enCrypto(bizJson.getBytes("GBK"), secKey));
            }

        } catch (UnsupportedEncodingException e) {
            throw new LklEncryptException("加密业务参数失败", e);
        }
        String reqType = encryptRes.getReqType();
        //generate mac string
        String macStr = null;
        //SHA
        if ("3.0.0".equals(ver.trim())) {
            macStr = retCode+retMsg+merId + ver+ts + encData;
            macStr = DigestUtil.Encrypt(macStr, "SHA-256");
        } 
        //生成MAC
        String mac = null;
        try {
            //encryot mac string
            mac = ByteArrayUtil.byteArray2HexString(RSAUtil.encryptByPrivateKey(macStr.getBytes("GBK"), privateKey));
        } catch (Exception e) {
            throw new LklEncryptException(e);
        }
        encryptRes.setMac(mac);
//        encryptRes.setEncKey(encKey);
        encryptRes.setEncData(encData);
        ToolsUtil.remove();
        return encryptRes;
    }

} public interface LklWebHookIntf<T extends LklCrossPayEncryptRes, E extends LklCrossPayEncryptReq> {

    /**
     * 响应拉卡拉回调
     *
     * @param notify 拉卡拉回调参数
     * @return
     */
    T proceed(E notify);
} public interface WebHookHandler<T extends SuperWebHookRequest> {

    /**
     * 处理拉卡拉通知
     *
     * @param notifyMsg
     * @return
     * @throws LklCommonException
     */
    void handle(T notifyMsg) throws LklCommonException;
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.lakala.crossborder.client.entities.LklCrossPayEncryptReq;
import com.lakala.crossborder.client.entities.LklCrossPayEncryptRes;
import com.lakala.crossborder.client.entities.webHook.CustomAuthNotify;
import com.lakala.crossborder.client.entities.webHook.CustomAuthResultNotifyRes;
import com.lakala.crossborder.client.enums.LklEnv;
import com.lakala.crossborder.client.exception.LklCommonException;
import com.lakala.crossborder.client.exception.LklEncryptException;
import com.lakala.crossborder.client.util.LklCrossPayEnv;
import com.lakala.crossborder.client.util.LklMsgUtil;
import com.lakala.crossborder.client.util.webhook.LklWebHookIntf;
import com.lakala.crossborder.client.util.webhook.WebHookHandler;
package com.lakala.crossborder.client.util.webhook.customAuthResult;



@RestController
public class CustomAuthResultWebHook {

	private static final Logger logger = LoggerFactory.getLogger(CustomAuthResultWebHook .class);
    
    @Autowired
    private LklCrossPayEnv lklCrossPayEnv;
    
    @RequestMapping(value = "lklCustomAuthResult/handle")
	public LklCrossPayEncryptRes proceed(@RequestBody LklCrossPayEncryptReq notify) {
        //注册应用环境
        String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPg0O4rPQJL1O+jqJ4rBjFVNRAuDmBSoii9pYfPQBaescCVY0irkWWoLyfTT65TjvnPpOx+IfNzBTlB13qCEFm7algREoeUHjFgFNHiXJ2LK/R0+VWgXe5+EDFfbrFCPnmLKG3OcKDGQszP0VOf6VVTM1t56CpgaRMm1/+Tzd2TQIDAQAB";
        String privKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIbyEihcDZbLfFhVpRU0knOtlzBhuWEjLBN4zd8dVzqAq52zWR04ybgZjjtlw+R9ICeJigmw2Dt3yy8SjpyMu5rYurTg/V0rGwayEuuE7aazi50nhZ/GYFRUVGIG0BoCI+73vsUmCO5EaUIJP/E0ew1bLMV7SMPynoQ8G9jXj8rXAgMBAAECgYAmfpFNcAz0UjGzZSMFbIzGcONrCsV9/zGIkHJxzgXfC2tpPgsSuetZF/kp2nrKCCOPA74by5WzSRXt5KZH5CFzuwwqIBv5cn6AiKiVmro3AnHV+qqNtfGXwLottu0VYcBEY2IPKb7SQ4pP5I0H973hnxR4qRzMAMUmEBVMiuUR+QJBAObLUBTjd7AE4Sls3/vBsYsYLYFfmw/dRHaW6jnuNxPTfVtPTGjY3V/672Vs01/f6QdtHHpMN+2xUk+acErAJTUCQQCVrvakm5R2sQwPycSO4mZW5sHKGbBosAkcY4llISVtxzSjgkPtBPVukCdNSGTuJX7+Ci8KolilrCc2XQCkH21bAkEAlcUAXd3TEL3J5BkMLRLgBTSWaytAtAXR5OdAboGA6nPHGJcYLb31wtBTxEzfyorCbRhIb7DAZpY4pQHCty+DtQJATFwCdPztYxN03MUIof+7R4/WwpwSU4WiUDozCEU9i+A46UT2E/8YmbuuYQ2Sd67nNv/I+brSUEofgus0/YUOywJBAMPu5o9guMsAjVqvXuxbkFpblYGZO/BrwiuLGDWEj4DZFqrIDmqgA6edy3HSoZ7U69BJZLTPb9DeebEiebmJZUI=";
        lklCrossPayEnv.registerEnv(LklEnv.SANDBOX, "RZCP161200000", privKey, pubKey);