From e49a408a275464cfb4718f8af35604f13767f585 Mon Sep 17 00:00:00 2001 From: xiaoyong931011 <15274802129@163.com> Date: Thu, 25 Aug 2022 15:38:48 +0800 Subject: [PATCH] 20220822 --- src/main/java/cc/mrbird/febs/pay/model/Request.java | 91 +++ src/main/java/cc/mrbird/febs/pay/util/HEXUtil.java | 85 +++ src/test/java/cc/mrbird/febs/ProfitTest.java | 45 + src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java | 30 + src/main/java/cc/mrbird/febs/pay/model/AgreementSignDto.java | 15 src/main/java/cc/mrbird/febs/pay/util/JsonUtil.java | 71 ++ src/main/java/cc/mrbird/febs/pay/util/RandomUtil.java | 29 + src/main/java/cc/mrbird/febs/pay/model/AgreeMentPaySmsDto.java | 31 + src/main/java/cc/mrbird/febs/pay/model/SignTypeEnum.java | 22 src/main/java/cc/mrbird/febs/pay/util/HttpClientUtil.java | 39 + src/main/java/cc/mrbird/febs/pay/util/MD5Util.java | 44 + src/main/java/cc/mrbird/febs/pay/model/Response.java | 86 +++ src/main/java/cc/mrbird/febs/pay/service/UnipayService.java | 25 src/main/java/cc/mrbird/febs/pay/model/AgreementPayDto.java | 26 + src/main/java/cc/mrbird/febs/pay/model/CommonConst.java | 12 src/main/java/cc/mrbird/febs/pay/util/RSAUtil.java | 211 ++++++++ src/main/java/cc/mrbird/febs/pay/model/RequestParam.java | 22 src/main/java/cc/mrbird/febs/pay/service/impl/UnipayServiceImpl.java | 275 ++++++++++ src/main/java/cc/mrbird/febs/pay/controller/UnipayController.java | 12 src/main/java/cc/mrbird/febs/pay/util/SignUtil.java | 109 ++++ src/main/java/cc/mrbird/febs/pay/util/Base64Util.java | 118 ++++ src/main/java/cc/mrbird/febs/common/exception/JPException.java | 12 src/main/java/cc/mrbird/febs/pay/util/CodeUtil.java | 17 src/main/java/cc/mrbird/febs/pay/util/AESUtil.java | 71 ++ src/main/java/cc/mrbird/febs/pay/model/Algorithm.java | 14 25 files changed, 1,500 insertions(+), 12 deletions(-) diff --git a/src/main/java/cc/mrbird/febs/common/exception/JPException.java b/src/main/java/cc/mrbird/febs/common/exception/JPException.java new file mode 100644 index 0000000..ce4c79e --- /dev/null +++ b/src/main/java/cc/mrbird/febs/common/exception/JPException.java @@ -0,0 +1,12 @@ +package cc.mrbird.febs.common.exception; + +public class JPException extends RuntimeException { + + public JPException(String message) { + super(message); + } + + public JPException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java index cb72f74..2f2ff02 100644 --- a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java @@ -17,6 +17,7 @@ import cc.mrbird.febs.mall.vo.OrderDetailVo; import cc.mrbird.febs.mall.vo.OrderListVo; import cc.mrbird.febs.mall.vo.OrderRefundVo; +import cc.mrbird.febs.pay.model.AgreementSignDto; import cc.mrbird.febs.pay.model.UnipayDto; import cc.mrbird.febs.pay.service.IPayService; import cc.mrbird.febs.pay.service.UnipayService; @@ -329,6 +330,35 @@ mallMoneyFlowService.addMoneyFlow(member.getId(), orderInfo.getAmount().negate(), MoneyFlowTypeEnum.PAY.getValue(), orderInfo.getOrderNo(), FlowTypeEnum.PRIZE_SCORE.getValue()); break; + case "5": + AgreementSignDto agreementSignDto = new AgreementSignDto(); + agreementSignDto.setOrderNo(orderInfo.getOrderNo()); + unipayService.agreementSign(agreementSignDto); +// UnipayDto unipayDto = new UnipayDto(); +//// unipayDto.setAmount(new BigDecimal("0.01")); +// unipayDto.setAmount(orderInfo.getAmount()); +// unipayDto.setFrpCode("ALIPAY_H5"); +// unipayDto.setTradeMerchantNo("777180800385820"); +// unipayDto.setOrderNo(orderInfo.getOrderNo()); +// List<MallOrderItem> items = orderInfo.getItems(); +// if(CollUtil.isEmpty(items)){ +// unipayDto.setProductName("商品"); +// }else{ +// unipayDto.setProductName(items.get(0).getGoodsName()); +// } +// String agreementSignStr = ""; +// if("fail" == unipayStr){ +// throw new FebsException("支付失败"); +// }else{ +// JSONUtil.parseObj(unipayStr); +// JSONObject jsonObject = JSONUtil.parseObj(unipayStr); +// payResultStr = (String) jsonObject.get("r7_TrxNo"); +// rcResult = (String) jsonObject.get("rc_Result"); +// } + orderInfo.setPayOrderNo(payResultStr); + orderInfo.setPayMethod("支付宝支付"); +// agentProducer.sendOrderReturn(orderInfo.getId()); + break; default: } diff --git a/src/main/java/cc/mrbird/febs/pay/controller/UnipayController.java b/src/main/java/cc/mrbird/febs/pay/controller/UnipayController.java index 58321a5..2a7eada 100644 --- a/src/main/java/cc/mrbird/febs/pay/controller/UnipayController.java +++ b/src/main/java/cc/mrbird/febs/pay/controller/UnipayController.java @@ -150,12 +150,12 @@ // System.out.println(orderNo); // } -// @ApiOperation(value = "获取协议支付签约短信", notes = "获取协议支付签约短信") -// @PostMapping(value = "getAgreeMentPaySms") -// public FebsResponse getAgreeMentPaySms(@RequestBody AgreeMentPaySmsDto agreeMentPaySmsDto) { -// unipayService.getAgreeMentPaySms(agreeMentPaySmsDto); -// return new FebsResponse().success().message("申请成功"); -// } + @ApiOperation(value = "获取协议支付签约短信", notes = "获取协议支付签约短信") + @PostMapping(value = "getAgreeMentPaySms") + public FebsResponse getAgreeMentPaySms(@RequestBody AgreeMentPaySmsDto agreeMentPaySmsDto) { + unipayService.getAgreeMentPaySms(agreeMentPaySmsDto); + return new FebsResponse().success().message("申请成功"); + } // public static void main(String[] args) { // String data = "{\\\"bank_trx_no\\\":\\\"0825144603229910\\\",\\\"jp_order_no\\\":\\\"100120220825446322447537651712\\\",\\\"mch_order_no\\\":\\\"2022082514435329133\\\",\\\"order_amount\\\":0.10,\\\"order_desc\\\":\\\"测试\\\",\\\"order_status\\\":\\\"P1000\\\",\\\"pay_success_time\\\":\\\"2022-08-25 14:46:04\\\"}"; diff --git a/src/main/java/cc/mrbird/febs/pay/model/AgreeMentPaySmsDto.java b/src/main/java/cc/mrbird/febs/pay/model/AgreeMentPaySmsDto.java new file mode 100644 index 0000000..c166a1a --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/AgreeMentPaySmsDto.java @@ -0,0 +1,31 @@ +package cc.mrbird.febs.pay.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@ApiModel(value = "AgreeMentPaySmsDto", description = "汇聚支付快捷支付-协议支付短信接收参数类") +public class AgreeMentPaySmsDto { + + // 商户订单号 + private String orderNo; + // 订单金额;金额保留两位小数 + private BigDecimal orderAmount; + //创建时间 +// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private String createdTime; + // 姓名 + private String name; + //证件类型 + private String idType; + //证件号码 + private String idCardNum; + // 银行卡号 + private String bankNo; + // 手机号 + private String phone; +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/AgreementPayDto.java b/src/main/java/cc/mrbird/febs/pay/model/AgreementPayDto.java new file mode 100644 index 0000000..51308b4 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/AgreementPayDto.java @@ -0,0 +1,26 @@ +package cc.mrbird.febs.pay.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@ApiModel(value = "AgreementPayDto", description = "汇聚支付快捷支付-协议支付接收参数类") +public class AgreementPayDto { + + // 商户订单号 + private String orderNo; + // 订单金额;金额保留两位小数 + private BigDecimal orderAmount; + //订单时间 格式 yyyy-MM-dd HH:mm:ss +// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private String orderTime; + // 商品名称 + private String orderDesc; + // 银行卡号 + private String bankNo; + +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/AgreementSignDto.java b/src/main/java/cc/mrbird/febs/pay/model/AgreementSignDto.java new file mode 100644 index 0000000..cbcb103 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/AgreementSignDto.java @@ -0,0 +1,15 @@ +package cc.mrbird.febs.pay.model; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +@Data +@ApiModel(value = "AgreementSignDto", description = "汇聚支付快捷支付-协议支付签约接收参数类") +public class AgreementSignDto { + + // 商户订单号 + private String orderNo; + // 签约短信验证码 + private String smsCode; + +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/Algorithm.java b/src/main/java/cc/mrbird/febs/pay/model/Algorithm.java new file mode 100644 index 0000000..7323f6f --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/Algorithm.java @@ -0,0 +1,14 @@ +package cc.mrbird.febs.pay.model; + +/** + * 加解密运算时的逻辑算法名常量类 + * @author: chenyf + * @Date: 2018-12-15 + */ +public class Algorithm { + public final static String MD5 = "MD5"; + public final static String AES = "AES"; + public final static String RSA = "RSA"; + + +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/CommonConst.java b/src/main/java/cc/mrbird/febs/pay/model/CommonConst.java new file mode 100644 index 0000000..364a196 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/CommonConst.java @@ -0,0 +1,12 @@ +package cc.mrbird.febs.pay.model; + +/** + * 公共常量类 + * @author: chenyf + * @Date: 2018-12-15 + */ +public class CommonConst { + public static final String ENCODING_UTF_8 = "UTF-8"; + + public static final String AES_KEY_SEPARATOR = ":"; +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/Request.java b/src/main/java/cc/mrbird/febs/pay/model/Request.java new file mode 100644 index 0000000..c9ecb27 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/Request.java @@ -0,0 +1,91 @@ +package cc.mrbird.febs.pay.model; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 商户请求的VO + */ +public class Request { + private String method; + private String version; + @JSONField(jsonDirect = true) + private String data; + private String rand_str; + private String sign_type; + private String mch_id; + private String sign; + private String aes_key; + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getRand_str() { + return rand_str; + } + + public void setRand_str(String rand_str) { + this.rand_str = rand_str; + } + + public String getSign_type() { + return sign_type; + } + + public void setSign_type(String sign_type) { + this.sign_type = sign_type; + } + + public String getMch_id() { + return mch_id; + } + + public void setMch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getAes_key() { + return aes_key; + } + + public void setAes_key(String aes_key) { + this.aes_key = aes_key; + } + + /** + * 拼接aes_key + * @param aes_key + * @param iv + */ + public void joinAesKey(String aes_key, String iv){ + this.aes_key = aes_key + CommonConst.AES_KEY_SEPARATOR + iv; + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/RequestParam.java b/src/main/java/cc/mrbird/febs/pay/model/RequestParam.java new file mode 100644 index 0000000..ca43bbb --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/RequestParam.java @@ -0,0 +1,22 @@ +package cc.mrbird.febs.pay.model; + +import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +/** + * 用户的请求参数 + */ +@Data +@ApiModel(value = "RequestParam", description = "接收参数类") +public class RequestParam { + private String method; + private String version; + // @JSONField(jsonDirect=true) + private String data; + private String rand_str; + private String sign_type; + private String mch_no; + private String sign; + private String sec_key; +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/Response.java b/src/main/java/cc/mrbird/febs/pay/model/Response.java new file mode 100644 index 0000000..e08fbc6 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/Response.java @@ -0,0 +1,86 @@ +package cc.mrbird.febs.pay.model; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 响应给商户的VO + */ +public class Response { + private String biz_code; + private String biz_msg; + @JSONField(jsonDirect = true) + private String data; + private String mch_no; + private String rand_str; + private String sign; + private String sign_type; + private String sec_key; + + public String[] splitAesKey(){ + return sec_key.split(CommonConst.AES_KEY_SEPARATOR); + } + + public String getBiz_code() { + return biz_code; + } + + public void setBiz_code(String biz_code) { + this.biz_code = biz_code; + } + + public String getBiz_msg() { + return biz_msg; + } + + public void setBiz_msg(String biz_msg) { + this.biz_msg = biz_msg; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMch_no() { + return mch_no; + } + + public void setMch_no(String mch_no) { + this.mch_no = mch_no; + } + + public String getRand_str() { + return rand_str; + } + + public void setRand_str(String rand_str) { + this.rand_str = rand_str; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getSign_type() { + return sign_type; + } + + public void setSign_type(String sign_type) { + this.sign_type = sign_type; + } + + public String getSec_key() { + return sec_key; + } + + public void setSec_key(String sec_key) { + this.sec_key = sec_key; + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/model/SignTypeEnum.java b/src/main/java/cc/mrbird/febs/pay/model/SignTypeEnum.java new file mode 100644 index 0000000..916e2b9 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/model/SignTypeEnum.java @@ -0,0 +1,22 @@ +package cc.mrbird.febs.pay.model; + +public enum SignTypeEnum { + MD5("1"), + RSA("2"), + RSA2("3"); + + /** 枚举值 */ + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + private SignTypeEnum(String value) { + this.value = value; + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/service/UnipayService.java b/src/main/java/cc/mrbird/febs/pay/service/UnipayService.java index 0069323..549ced2 100644 --- a/src/main/java/cc/mrbird/febs/pay/service/UnipayService.java +++ b/src/main/java/cc/mrbird/febs/pay/service/UnipayService.java @@ -1,10 +1,33 @@ package cc.mrbird.febs.pay.service; +import cc.mrbird.febs.pay.model.AgreeMentPaySmsDto; +import cc.mrbird.febs.pay.model.AgreementPayDto; +import cc.mrbird.febs.pay.model.AgreementSignDto; import cc.mrbird.febs.pay.model.UnipayDto; -import com.sun.org.apache.xpath.internal.operations.Bool; public interface UnipayService { String unipay(UnipayDto unipayDto); + /** + * 快捷协议支付-签约短信接口 + * @param agreeMentPaySmsDto + * @return + */ + String getAgreeMentPaySms(AgreeMentPaySmsDto agreeMentPaySmsDto); + + /** + *快捷协议支付-签约接口 + * @param agreementSignDto + * @return + */ + String agreementSign(AgreementSignDto agreementSignDto); + + /** + * 快捷协议支付-无短支付接口 + * @param agreementPayDto + * @return + */ + String agreementPay(AgreementPayDto agreementPayDto); + } diff --git a/src/main/java/cc/mrbird/febs/pay/service/impl/UnipayServiceImpl.java b/src/main/java/cc/mrbird/febs/pay/service/impl/UnipayServiceImpl.java index 0aad1b6..65e9a09 100644 --- a/src/main/java/cc/mrbird/febs/pay/service/impl/UnipayServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/pay/service/impl/UnipayServiceImpl.java @@ -1,10 +1,10 @@ package cc.mrbird.febs.pay.service.impl; -import cc.mrbird.febs.common.entity.FebsResponse; -import cc.mrbird.febs.pay.model.UnipayDto; +import cc.mrbird.febs.pay.model.*; import cc.mrbird.febs.pay.service.UnipayService; -import cc.mrbird.febs.pay.util.HttpRequester; -import cc.mrbird.febs.pay.util.HttpRespons; +import cc.mrbird.febs.pay.util.*; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; @@ -13,13 +13,37 @@ import org.springframework.transaction.annotation.Transactional; import java.io.IOException; +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; import java.util.*; @Slf4j @Service public class UnipayServiceImpl implements UnipayService { + //汇聚支付平台公钥 + public static final String platformPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwSAEXsiC0IYh" + + "a6a94imKq8VfOkk7WjDRAQWMBRnoKOZeEUeMrHYiblcrqeMYXGpV13288iUOkuyKwkPXkYXyIQK8emvJIbQOhtB5bS" + + "lAbodsPgncM9Ney1GFiz+7ogBxyt58mP8AA9UHtMw7u78zZoQ1+dUWwUUowVXml3Q0cVQIDAQAB"; + //本地生成的公钥 + public static final String paySecretKey = + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJSsoLHAy4P+H76l4i6IMZ/JKKTVphvrJBSB777N8h" + + "gzN38M+66IqazRQLNjjyn2UZxm4eSfuJ4OBxCTGLJgDOw8B9l9lVo+0bzhNxsy5ae20EOlQa3h8xfCjCCU" + + "Xb8VP8yknG43Wk1WtwLX5VYWmJFTwA1I0dWQR4RlbHH2q2aVAgMBAAECgYBZNyoK4JV/tFwaTHLo12Nn7g" + + "9MssRGFpmFEN/sEKuJKBrSS9kvx+SBxuPbgg/j72LTxs0hI1NmzSYiJRL53zeBFM0wM2/D2zW0pZfogUPN" + + "7Mb3FkFOoE0CxFwn7pjjBkvAsZQJS2fZFY6cf/WYhVI+XCMzjiQWJSQUXKEfWzUflQJBAMpLzinyAsBHKe" + + "6lmAkSh/yaQX76cnxNZGmfc/gj62qM5YDzpt3+Za5Y4NSq5kYu36NAgorf2ipJPRwpgS13fK8CQQC8JKVP" + + "2YgZ39fa+xVJu3yTT48k9EnYIEfsv/Bc7AVdpaZKDeEBuGpBb0JQzuIg3Zd6yNQCzH3oBqp8l6l/fgn7Ak" + + "EAtFluvHB4yWjoVk0lRPlTaP0w5P5ssKrimVPBtPh4+a4RMayHGKSjjBLKpm6SCwHg+Q8bEqpNOqO+qmvK" + + "MXm0GwJAZd4Bk8ZYJopIOUyRLibRQIFnI78Q7HAuAUW7QtSX4yh5bMcu+Nt8zIkNAuvBC8Ju7hAmmo1V7n" + + "cNgAAtydXYWQJALLOFCjCkRgeRVL8YE8bVi4U16b8ltAN1DlbWEzui6VFy2vIga3IryesNVAOOdornyAwf" + + "1huqB2lYfuQwtrIBKg=="; public static final String notifyUrl = "http://47.111.90.145:8800/api/unipay/unipayCallBack"; + public static final String agreementPayNotifyUrl = "http://47.111.90.145:8800/api/unipay/agreementPayCallBack"; + public static final String p1MerchantNo = "888118000001971";/** 商户编号 */ + public static final String aesKey = "1234567891234567";/** 商户编号 */ @Override @Transactional @@ -27,7 +51,7 @@ String key = "2e95f6a3e11e47fa8a4386d6aefe1735";/** md5密钥商户后台-商户中心-商户设置-密钥管理获取 必填!*/ Map<String, String> map = new HashMap<String, String>(); map.put("p0_Version", "1.0");/** 版本号 */ - map.put("p1_MerchantNo", "888118000001971");/** 商户编号 */ + map.put("p1_MerchantNo", p1MerchantNo);/** 商户编号 */ map.put("p2_OrderNo", unipayDto.getOrderNo()); /**商户订单号*/ map.put("p3_Amount", unipayDto.getAmount().toString());/**订单金额*/ map.put("p4_Cur", "1"); /**交易币种 */ @@ -131,4 +155,245 @@ } + + + @Override + @Transactional + public String agreementPay(AgreementPayDto agreementPayDto) { + + String secretKey =paySecretKey; + String key = aesKey;//敏感信息加密key + + Map<String, Object> map = new HashMap<>(); + RequestParam requestParam = new RequestParam(); + requestParam.setMch_no(p1MerchantNo);// 商户编号 + requestParam.setMethod("fastPay.agreement.pay");// 固定方法名 + requestParam.setVersion("1.0");// 版本号 + requestParam.setRand_str("12345678901234567890123456789012");// 随机字符串 + requestParam.setSign_type("2");// 签名类型 + try { + requestParam.setSec_key(RSAUtil.encryptByPublicKey(platformPublicKey,aesKey));//加密密钥 + } catch (Exception e) { + e.printStackTrace(); + } + + map.put("mch_order_no",agreementPayDto.getOrderNo()); // 商户订单号 + map.put("order_amount", agreementPayDto.getOrderAmount()); // 订单金额;金额保留两位小数 + map.put("mch_req_time", agreementPayDto.getOrderTime()); // 订单时间 + map.put("order_desc", agreementPayDto.getOrderDesc()); // 订单时间 + map.put("callback_url", agreementPayNotifyUrl); // 异步通知地址 + map.put("bank_card_no", AESUtil.Aes256Encode(agreementPayDto.getBankNo(),aesKey)); // 签约银行卡号 + + requestParam.setData(JsonUtil.toString(map)); + + String signStr = null; + try { + signStr = getSortedString(requestParam); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("待签名字符串:"+signStr);// 待签名字符串 + + // 签名 + if (SignTypeEnum.RSA.getValue().equals(requestParam.getSign_type())) { + try { + signStr =(RSAUtil.sign(signStr, secretKey, false)); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + signStr = ""; + System.out.println("未预期的签名类型:" + requestParam.getSign_type()); + } + requestParam.setSign(signStr); + + // Map转json字符串 + String reqBodyJson = JSON.toJSONString(requestParam); + System.out.println(reqBodyJson); + String httpResponseJson = null; + try { + httpResponseJson = HttpClientUtil.sendHttpPost("https://api.joinpay.com/fastpay", reqBodyJson); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println(httpResponseJson); + + if(StrUtil.isNotBlank(httpResponseJson)){ + return httpResponseJson; + }else{ + return "fail"; + } + } + + public static void main(String[] args) { +// BigDecimal value = new BigDecimal("0.10858").setScale(2,BigDecimal.ROUND_HALF_UP); + BigDecimal value = new BigDecimal("1231.1").setScale(2, BigDecimal.ROUND_DOWN); + DecimalFormat decimalFormat = new DecimalFormat("0.00#"); + String strVal = decimalFormat.format(value); + System.out.println(strVal); + } + + @Override + public String getAgreeMentPaySms(AgreeMentPaySmsDto agreeMentPaySmsDto) { + String secretKey = paySecretKey; + + Map<String, Object> map = new HashMap<>(); + RequestParam requestParam = new RequestParam(); + requestParam.setMch_no(p1MerchantNo);// 商户编号 + requestParam.setMethod("fastPay.agreement.signSms");// 固定方法名 + requestParam.setVersion("1.0");// 版本号 + requestParam.setRand_str("12345678901234567890123456789012");// 随机字符串 + requestParam.setSign_type("2");// 签名类型 + try { + requestParam.setSec_key(RSAUtil.encryptByPublicKey(platformPublicKey,aesKey));//加密密钥 + } catch (Exception e) { + e.printStackTrace(); + } + + map.put("mch_order_no", "NO_"+agreeMentPaySmsDto.getOrderNo()); // 商户订单号 + map.put("order_amount", agreeMentPaySmsDto.getOrderAmount()); // 订单金额 + map.put("mch_req_time", agreeMentPaySmsDto.getCreatedTime()); // 下单时间 + map.put("payer_name",AESUtil.Aes256Encode(agreeMentPaySmsDto.getName(),aesKey)); // 姓名 + map.put("id_type", agreeMentPaySmsDto.getIdType()); // 证件类型 + map.put("id_no", AESUtil.Aes256Encode(agreeMentPaySmsDto.getIdCardNum(),aesKey)); // 证件号码 + map.put("bank_card_no", AESUtil.Aes256Encode(agreeMentPaySmsDto.getBankNo(),aesKey)); // 银行卡号 + map.put("mobile_no", AESUtil.Aes256Encode(agreeMentPaySmsDto.getPhone(), aesKey)); // 手机号 + + requestParam.setData(JsonUtil.toString(map)); + + String signStr = null; + try { + signStr = getSortedString(requestParam); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("待签名字符串:"+signStr);// 待签名字符串 + + // 签名 + if (SignTypeEnum.RSA.getValue().equals(requestParam.getSign_type())) { + try { + signStr =(RSAUtil.sign(signStr, secretKey, false)); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + signStr = ""; + System.out.println("未预期的签名类型:" + requestParam.getSign_type()); + } + requestParam.setSign(signStr); + + + // Map转json字符串 + String reqBodyJson = JSON.toJSONString(requestParam); + System.out.println(reqBodyJson); + String httpResponseJson = null; + try { + httpResponseJson = HttpClientUtil.sendHttpPost("https://api.joinpay.com/fastpay", reqBodyJson); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println(httpResponseJson); + if(StrUtil.isNotBlank(httpResponseJson)){ + cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(httpResponseJson); + System.out.println(jsonObject); + String biz_code = jsonObject.get("biz_code").toString(); + if("JS000000" == biz_code){ + return httpResponseJson; + } + return "fail"; + }else{ + return "fail"; + } + } + + @Override + public String agreementSign(AgreementSignDto agreementSignDto) { + String secretKey = paySecretKey; + + Map<String, Object> map = new HashMap<>(); + RequestParam requestParam = new RequestParam(); + requestParam.setMch_no(p1MerchantNo);// 商户编号 + requestParam.setMethod("fastPay.agreement.smsSign");// 固定方法名 + requestParam.setVersion("1.0");// 版本号 + requestParam.setRand_str("12345678901234567890123456789012");// 随机字符串 + requestParam.setSign_type("2");// 签名类型 + try { + requestParam.setSec_key(RSAUtil.encryptByPublicKey(platformPublicKey,aesKey)); + } catch (Exception e) { + e.printStackTrace(); + } + + map.put("mch_order_no", agreementSignDto.getOrderNo()); // 商户订单号 + map.put("sms_code", agreementSignDto.getSmsCode()); // 签约短信验证码 + + requestParam.setData(JsonUtil.toString(map)); + + String signStr = null; + try { + signStr = getSortedString(requestParam); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println(signStr);// 待签名字符串 + + // 签名 + if (SignTypeEnum.RSA.getValue().equals(requestParam.getSign_type())) { + try { + signStr =(RSAUtil.sign(signStr, secretKey, false)); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + signStr = ""; + System.out.println("未预期的签名类型:" + requestParam.getSign_type()); + } + requestParam.setSign(signStr); + + + // Map转json字符串 + String reqBodyJson = JSON.toJSONString(requestParam); + System.out.println(reqBodyJson); + String httpResponseJson = null; + try { + httpResponseJson = HttpClientUtil.sendHttpPost("https://api.joinpay.com/fastpay", reqBodyJson); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println(httpResponseJson); + + if(StrUtil.isNotBlank(httpResponseJson)){ + return httpResponseJson; + }else{ + return "fail"; + } + } + + protected static String getSortedString(Object obj) throws Exception { + Field[] fields = obj.getClass().getDeclaredFields(); + + Map<String, String> map = new HashMap<String, String>(); + for (int i = 0; i < fields.length; i++) { + Field filed = fields[i]; + String name = filed.getName(); + if (SignUtil.NOT_SIGN_PARAM.contains(name)) {// 不参与签名或验签的参数直接跳过 + continue; + } + filed.setAccessible(true); + map.put(name, String.valueOf(filed.get(obj))); + } + StringBuffer content = new StringBuffer(); + List<String> keys = new ArrayList(map.keySet()); + Collections.sort(keys); // 排序map + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = map.get(key); + + if (i != 0) { + content.append(SignUtil.SIGN_SEPARATOR); + } + content.append(key).append(SignUtil.SIGN_EQUAL).append(value); + } + return content.toString(); + } + } diff --git a/src/main/java/cc/mrbird/febs/pay/util/AESUtil.java b/src/main/java/cc/mrbird/febs/pay/util/AESUtil.java new file mode 100644 index 0000000..e1e69b8 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/AESUtil.java @@ -0,0 +1,71 @@ +package cc.mrbird.febs.pay.util; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.security.Security; +import java.util.Base64; + +/** + * AES加解密工具类 + * @author chenyf + * @date 2018-12-15 + */ +public class AESUtil { + // private static final Logger logger = LoggerFactory.getLogger(AesUtils.class); + public static final String ALGORITHM = "AES/ECB/PKCS5Padding"; + public static final String UTF_8 = "UTF-8"; + public static final String AES = "AES"; + public static final String BC = "BC"; + + + static { + //仅加载一次,不要多次加载,可能会导致内存溢出 + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + + /** + * 网联敏感字段AES加密 + * + * @param str 敏感字段原文 + * @param key AES密钥 必须是8位、16位、24位 + * @return + */ + public static String Aes256Encode(String str, String key) { + // if(StringUtil.isNotBlank(str)) { + try { + Cipher cipher = Cipher.getInstance(ALGORITHM, BC); + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(UTF_8), AES); // 生成加密解密需要的Key + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + byte[] result = cipher.doFinal(str.getBytes(UTF_8)); + return Base64.getEncoder().encodeToString(result); + } catch (Exception e) { + // logger.error("Aes256Encode error:", e); + return null; + } + + } + + /** + * 网联敏感字段密文解密 + * + * @param base64Str 要被解密的密文.做了base64编码 base64Str + * @param key AES密钥 必须是8位、16位、24位 + * @return 解密后的字符串 + */ + public static String Aes256Decode(String base64Str, String key) { + // if (StringUtil.isNotBlank(base64Str)) { + String result = null; + try { + Cipher cipher = Cipher.getInstance(ALGORITHM, BC); + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(UTF_8), AES); // 生成加密解密需要的Key + cipher.init(Cipher.DECRYPT_MODE, keySpec); + byte[] decoded = cipher.doFinal(Base64Util.decode(base64Str)); + result = new String(decoded, "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + + + } diff --git a/src/main/java/cc/mrbird/febs/pay/util/Base64Util.java b/src/main/java/cc/mrbird/febs/pay/util/Base64Util.java new file mode 100644 index 0000000..a2cab85 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/Base64Util.java @@ -0,0 +1,118 @@ +package cc.mrbird.febs.pay.util; + +import java.io.ByteArrayOutputStream; + +/** + * base64 format encoding & decoding + */ +public class Base64Util { + private static char[] base64EncodeChars = new char[] { 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' }; + + private static byte[] base64DecodeChars = new byte[] { -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, + -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, + -1, -1 }; + + private Base64Util(){ + } + + public static String encode(byte[] data){ + StringBuffer sb = new StringBuffer(); + int len = data.length; + int i = 0; + int b1, b2, b3; + + while (i < len) { + b1 = data[i++] & 0xff; + if (i == len) { + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[(b1 & 0x3) << 4]); + sb.append("=="); + break; + } + b2 = data[i++] & 0xff; + if (i == len) { + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[((b1 & 0x03) << 4) + | ((b2 & 0xf0) >>> 4)]); + sb.append(base64EncodeChars[(b2 & 0x0f) << 2]); + sb.append("="); + break; + } + b3 = data[i++] & 0xff; + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[((b1 & 0x03) << 4) + | ((b2 & 0xf0) >>> 4)]); + sb.append(base64EncodeChars[((b2 & 0x0f) << 2) + | ((b3 & 0xc0) >>> 6)]); + sb.append(base64EncodeChars[b3 & 0x3f]); + } + return sb.toString(); + } + + public static byte[] decode(String str){ + byte[] data = str.getBytes(); + int len = data.length; + ByteArrayOutputStream buf = new ByteArrayOutputStream(len); + int i = 0; + int b1, b2, b3, b4; + + while (i < len) { + + /* b1 */ + do { + b1 = base64DecodeChars[data[i++]]; + } while (i < len && b1 == -1); + if (b1 == -1) { + break; + } + + /* b2 */ + do { + b2 = base64DecodeChars[data[i++]]; + } while (i < len && b2 == -1); + if (b2 == -1) { + break; + } + buf.write(((b1 << 2) | ((b2 & 0x30) >>> 4))); + + /* b3 */ + do { + b3 = data[i++]; + if (b3 == 61) { + return buf.toByteArray(); + } + b3 = base64DecodeChars[b3]; + } while (i < len && b3 == -1); + if (b3 == -1) { + break; + } + buf.write((((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); + + /* b4 */ + do { + b4 = data[i++]; + if (b4 == 61) { + return buf.toByteArray(); + } + b4 = base64DecodeChars[b4]; + } while (i < len && b4 == -1); + if (b4 == -1) { + break; + } + buf.write((((b3 & 0x03) << 6) | b4)); + } + return buf.toByteArray(); + } + +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/CodeUtil.java b/src/main/java/cc/mrbird/febs/pay/util/CodeUtil.java new file mode 100644 index 0000000..638cb3f --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/CodeUtil.java @@ -0,0 +1,17 @@ +package cc.mrbird.febs.pay.util; + +/** + * 编解码工具类 + * @author chenyf + * @date 2018-12-15 + */ +public class CodeUtil { + + public static String base64Encode(byte[] value) { + return Base64Util.encode(value); + } + + public static byte[] base64Decode(String value) { + return Base64Util.decode(value); + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/HEXUtil.java b/src/main/java/cc/mrbird/febs/pay/util/HEXUtil.java new file mode 100644 index 0000000..73028b4 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/HEXUtil.java @@ -0,0 +1,85 @@ +package cc.mrbird.febs.pay.util; + +import cc.mrbird.febs.common.exception.JPException; +import cc.mrbird.febs.pay.model.CommonConst; + +/** + * 16进制工具类 + * @author chenyf + * @date 2018-12-15 + */ +public class HEXUtil { + private static final char[] DIGITS_LOWER = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private static final char[] DIGITS_UPPER = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + + public static String encode(String str){ + try{ + return encode(str.getBytes(CommonConst.ENCODING_UTF_8), true); + }catch (Exception e){ + throw new JPException("16进制转换失败", e); + } + } + + public static String encode(byte[] data, final boolean toUpperCase){ + return bytes2Hex(data, toUpperCase ? DIGITS_UPPER : DIGITS_LOWER); + } + + public static String decode(String str){ + try{ + byte[] date = hex2Bytes(str); + return new String(date, CommonConst.ENCODING_UTF_8); + }catch (Exception e){ + throw new JPException("16进制转换失败", e); + } + } + + private static String bytes2Hex(final byte[] data, final char[] toDigits) { + final int l = data.length; + final char[] out = new char[l << 1]; + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + return new String(out); + } + + private static byte[] hex2Bytes(final String data) throws Exception { + final int len = data.length(); + + if ((len & 0x01) != 0) { + throw new Exception("Odd number of characters."); + } + + final byte[] out = new byte[len >> 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; j < len; i++) { + int f = toDigit(data.charAt(j), j) << 4; + j++; + f = f | toDigit(data.charAt(j), j); + j++; + out[i] = (byte) (f & 0xFF); + } + return out; + } + + /** + * 16转化为数字 + * @param ch 16进制 + * @param index 索引 + * @return 转化结果 + * @throws Exception 转化失败异常 + */ + private static int toDigit(final char ch, final int index) throws Exception { + final int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new Exception("Illegal hexadecimal character " + ch + " at index " + index); + } + return digit; + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/HttpClientUtil.java b/src/main/java/cc/mrbird/febs/pay/util/HttpClientUtil.java new file mode 100644 index 0000000..2dcb734 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/HttpClientUtil.java @@ -0,0 +1,39 @@ +package cc.mrbird.febs.pay.util; + +/** + * 类HttpClientUtil + * + * @author Lori 2018年6月04日 下午16:10:04 + */ + +import org.apache.http.HttpEntity; +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; + +public class HttpClientUtil { + public static String sendHttpPost(String url, String body) throws Exception { + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + httpPost.addHeader("Content-Type","application/json;charset=UTF-8"); + //UrlEncodedFormEntity setEntity = new UrlEncodedFormEntity(body, HTTP.UTF_8); + StringEntity setEntity = new StringEntity(body,"utf-8"); + setEntity.setContentType("application/json"); + setEntity.setContentEncoding("UTF-8"); + httpPost.setEntity(setEntity); + + + CloseableHttpResponse response = httpClient.execute(httpPost); + //System.out.println(response.getStatusLine().getStatusCode() + "\n"); + HttpEntity entity = response.getEntity(); + String responseContent = EntityUtils.toString(entity, "UTF-8"); + //System.out.println(responseContent); + + response.close(); + httpClient.close(); + return responseContent; + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/JsonUtil.java b/src/main/java/cc/mrbird/febs/pay/util/JsonUtil.java new file mode 100644 index 0000000..406f1ab --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/JsonUtil.java @@ -0,0 +1,71 @@ +package cc.mrbird.febs.pay.util; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.PropertyNamingStrategy; +import com.alibaba.fastjson.serializer.SerializeConfig; + +import java.lang.reflect.Type; +import java.util.List; + +/** + * JSON转换工具类 + * @author chenyf + * @date 2018-12-15 + */ +public class JsonUtil { + + /** + * 将一个对像转成一个json字符串 + */ + public static final String toString(Object obj) { + return JSON.toJSONString(obj); + } + + /** + * 把json字符串转换成指定Class的对象 + * @param text + * @param clazz + * @param <T> + * @return + */ + public static <T> T toBean(String text, Class<T> clazz) { + return JSON.parseObject(text, clazz); + } + + /** + * 把json字节转换成指定Class的对象 + * @param text + * @param clazz + * @param <T> + * @return + */ + public static <T> T toBean(byte[] text, Class<T> clazz) { + return JSON.parseObject(text, clazz); + } + + /** + * 把json字符串转换成指定类型的对象 + * @param text + * @param type + * @param <T> + * @return + */ + public static <T> T toBean(String text, Type type) { + return JSON.parseObject(text, type); + } + + /** + * 把json字符串转换为指定Class的List + * @param obj + * @param clazz + * @param <T> + * @return + */ + public static final <T> List<T> toList(Object obj, Class<T> clazz) { + if (obj == null) { + return null; + } + return JSONArray.parseArray(obj.toString(), clazz); + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/MD5Util.java b/src/main/java/cc/mrbird/febs/pay/util/MD5Util.java new file mode 100644 index 0000000..290a33e --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/MD5Util.java @@ -0,0 +1,44 @@ +package cc.mrbird.febs.pay.util; + +import cc.mrbird.febs.common.exception.JPException; +import cc.mrbird.febs.pay.model.Algorithm; +import cc.mrbird.febs.pay.model.CommonConst; +import cn.hutool.core.util.StrUtil; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * MD5工具类 + * @author chenyf + * @date 2018-12-15 + */ +public class MD5Util { + + /** + * 生成16进制的MD5字符串 + * @param str + * @return + */ + public static String getMD5Hex(String str) { + return HEXUtil.encode(getMD5(str), true); + } + + public static byte[] getMD5(String str) { + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance(Algorithm.MD5); + messageDigest.reset(); + if (StrUtil.isNotEmpty(str)) { + messageDigest.update(str.getBytes(CommonConst.ENCODING_UTF_8)); + } + } catch (NoSuchAlgorithmException e) { + throw new JPException("生成MD5信息时异常", e); + } catch (UnsupportedEncodingException e) { + throw new JPException("生成MD5信息时异常", e); + } + + return messageDigest.digest(); + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/RSAUtil.java b/src/main/java/cc/mrbird/febs/pay/util/RSAUtil.java new file mode 100644 index 0000000..bf96fac --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/RSAUtil.java @@ -0,0 +1,211 @@ +package cc.mrbird.febs.pay.util; + + + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +/** + * RSA加解密的工具类 + */ +public class RSAUtil { +// private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class); + public static final String SIGNATURE_ALGORITHM_SHA1 = "SHA1withRSA"; + public static final String SIGNATURE_ALGORITHM_MD5 = "MD5withRSA"; + public static final String RSA = "RSA"; + + public static final String ANDROID_ENCRYPT_ALGORITHM = "RSA/ECB/NoPadding"; + public static final String DEFAULT_ENCRYPT_ALGORITHM = "RSA/ECB/PKCS1Padding"; + + public static final String PUBLIC_KEY = "publicKey"; + public static final String PRIVATE_KEY = "privateKey"; + + /** */ + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 116; + + /** */ + /** + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 128; + + /** + * 初始化BC提供者,RSA算法必须要先初始化BC才能进行加密解密和签名验签 + */ + private static void initProvider() { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + /** + * 生成RSA签名串 + * + * @param data 需要生成签名串的数据 + * @param privateKey 私钥 + * @return + * @throws BizException + */ + public static String sign(String data, String privateKey, boolean isSha) throws Exception { + try { + initProvider(); + byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); + byte[] keyBytes = CodeUtil.base64Decode(privateKey); + String algorithm = isSha ? SIGNATURE_ALGORITHM_SHA1 : SIGNATURE_ALGORITHM_MD5; + + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + PrivateKey priKey = KeyFactory.getInstance(RSA).generatePrivate(pkcs8KeySpec); + Signature signature = Signature.getInstance(algorithm); + signature.initSign(priKey); + signature.update(dataBytes); + return CodeUtil.base64Encode(signature.sign()); + }catch (Throwable e){ +// logger.error("==>sign err:", e); + throw new Exception("生成RSA签名失败", e); + } + } + + /** + * 验证RSA签名串 + * + * @param data 需要验签的数据 + * @param publicKey 公钥 + * @param sign 用户传过来的签名串 + * @return + * @throws BizException + */ + public static boolean verify(String data, String publicKey, String sign, boolean isSha) throws Exception { + try { + initProvider(); + byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); + byte[] signBytes = CodeUtil.base64Decode(sign); + String algorithm = isSha ? SIGNATURE_ALGORITHM_SHA1 : SIGNATURE_ALGORITHM_MD5; + PublicKey publicK = getPublicKey(publicKey); + + Signature signature = Signature.getInstance(algorithm); + signature.initVerify(publicK); + signature.update(dataBytes); + return signature.verify(signBytes); + } catch (Throwable e) { +// logger.error("==>verify err:", e); + throw new Exception("RSA验签失败", e); + } + } + + /** + * 对称密钥公钥加密 + * + * @param publicKeyStr 公钥Str + * @param content 密钥原文 + * @return 加密密文 + */ + public static String encryptByPublicKey(String publicKeyStr, String content) throws Exception { + + byte[] dataBytes = content.getBytes(StandardCharsets.UTF_8); + dataBytes = doCipher(dataBytes, publicKeyStr, true, false); + return CodeUtil.base64Encode(dataBytes); + + } + + /** + * 对称密钥密文解密 + * + * @param privateKeyStr 私钥字符串 + * @param content 对称密钥密文 + * @return 对称密钥明文 + * @throws Exception + */ + public static String decryptByPrivateKey(String privateKeyStr, String content) throws Exception{ + byte[] dataBytes = CodeUtil.base64Decode(content); + dataBytes = doCipher(dataBytes, privateKeyStr, false, false); + return new String(dataBytes, StandardCharsets.UTF_8); + + } + + private static byte[] doCipher(byte[] dataBytes, String keyStr, boolean isEncrypt, boolean isAndroid) throws Exception { + + initProvider(); + + Key key; + Cipher cipher; + int maxBlock; + + if (isEncrypt) { + maxBlock = MAX_ENCRYPT_BLOCK; + key = getPublicKey(keyStr); + if (isAndroid) { + cipher = Cipher.getInstance(ANDROID_ENCRYPT_ALGORITHM);// 如果是安卓机 + } else { + cipher = Cipher.getInstance(DEFAULT_ENCRYPT_ALGORITHM); + } + cipher.init(Cipher.ENCRYPT_MODE, key); + } else { + maxBlock = MAX_DECRYPT_BLOCK; + byte[] keyBytes = Base64.decodeBase64(keyStr); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(RSA); + Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); + cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, privateK); + } + + int offSet = 0, i = 0, inputLen = dataBytes.length; + byte[] cache; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + // 对数据分段加密/解密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > maxBlock) { + cache = cipher.doFinal(dataBytes, offSet, maxBlock); + } else { + cache = cipher.doFinal(dataBytes, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * maxBlock; + } + return out.toByteArray(); + } finally { + out.close(); + } + } + + /** + * 生成公私密钥对 + * @return + */ + public static Map<String, String> genKeyPair() throws Exception { + try { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA); + keyPairGen.initialize(1024); + KeyPair keyPair = keyPairGen.generateKeyPair(); + + Map<String, String> keyMap = new HashMap<>(2); + keyMap.put(PUBLIC_KEY, CodeUtil.base64Encode(keyPair.getPublic().getEncoded())); + keyMap.put(PRIVATE_KEY, CodeUtil.base64Encode(keyPair.getPrivate().getEncoded())); + return keyMap; + }catch (Throwable e){ + throw new Exception("生成RSA密钥对出现异常", e); + } + } + + private static PublicKey getPublicKey(String publicKey) throws Exception { + byte[] keyBytes = CodeUtil.base64Decode(publicKey); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(RSA); + return keyFactory.generatePublic(x509KeySpec); + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/RandomUtil.java b/src/main/java/cc/mrbird/febs/pay/util/RandomUtil.java new file mode 100644 index 0000000..3450ad2 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/RandomUtil.java @@ -0,0 +1,29 @@ +package cc.mrbird.febs.pay.util; + +import java.util.Random; +import java.util.UUID; + +/** + * 获取一些随机数的工具类 + * @author chenyf + * @date 2018-12-15 + */ +public class RandomUtil { + + public static String get16LenStr(){ + if(getInt(2) == 0){ + return MD5Util.getMD5Hex(UUID.randomUUID().toString()).substring(16); + }else{ + return MD5Util.getMD5Hex(UUID.randomUUID().toString()).substring(1, 17); + } + } + + public static String get32LenStr(){ + return MD5Util.getMD5Hex(UUID.randomUUID().toString()); + } + + public static int getInt(int max){ + Random random = new Random(); + return random.nextInt(max); + } +} diff --git a/src/main/java/cc/mrbird/febs/pay/util/SignUtil.java b/src/main/java/cc/mrbird/febs/pay/util/SignUtil.java new file mode 100644 index 0000000..702d515 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/pay/util/SignUtil.java @@ -0,0 +1,109 @@ +package cc.mrbird.febs.pay.util; + +import cc.mrbird.febs.common.exception.JPException; +import cc.mrbird.febs.pay.model.Request; +import cc.mrbird.febs.pay.model.Response; +import cc.mrbird.febs.pay.model.SignTypeEnum; +import cn.hutool.core.util.ObjectUtil; +import com.alipay.service.schema.util.StringUtil; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * 签名、验签的工具类 + * @author chenyf + * @date 2018-12-15 + */ +public class SignUtil { + public final static String SIGN_SEPARATOR = "&";//分隔符 + public final static String SIGN_EQUAL = "=";//等于号 + public final static String SIGN_KEY_PARAM_NAME = "key"; + public final static List<String> NOT_SIGN_PARAM = Arrays.asList(new String[]{"sign","aesKey","aes_key","sec_key"});//不参与签名/验签的参数 + + /** + * 验证签名 + * @param response + * @param key + * @return + */ + public static boolean verify(Response response, String key) throws Exception { + String signStr = getSortedString(response); + if(SignTypeEnum.MD5.getValue().equals(response.getSign_type())){ + signStr = HEXUtil.encode(genMD5Sign(signStr, key), true); + if(signStr.equals(response.getSign())){ + return true; + }else{ + return false; + } + }else if(SignTypeEnum.RSA.getValue().equals(response.getSign_type())){ + return RSAUtil.verify(signStr, key, HEXUtil.decode(response.getSign()), true); + }else{ + return false; + } + } + + /** + * 生成签名 + * @param request + * @param key + * @return + */ + public static void sign(Request request, String key) throws Exception { + if(ObjectUtil.isEmpty(request)){ + return; + }else if(StringUtil.isEmpty(request.getMch_id()) || StringUtil.isEmpty(request.getSign_type())){ + request.setSign(""); + return; + } + + if(StringUtil.isEmpty(request.getRand_str())){ + request.setRand_str(RandomUtil.get32LenStr()); + } + + String signStr = getSortedString(request); + + if(SignTypeEnum.MD5.getValue().equals(request.getSign_type())){ + signStr = HEXUtil.encode(genMD5Sign(signStr, key), true); + }else if(SignTypeEnum.RSA.getValue().equals(request.getSign_type())){ + signStr = HEXUtil.encode(RSAUtil.sign(signStr, key, true)); + }else{ + //抛出签名失败的异常 + throw new JPException("签名失败,未预期的签名类型:"+request.getSign_type()); + } + request.setSign(signStr); + } + + protected static String getSortedString(Object obj) throws Exception { + Field[] fields = obj.getClass().getDeclaredFields(); + + Map<String, String> map = new HashMap<String, String>(); + for(int i=0; i<fields.length; i++){ + Field filed = fields[i]; + String name = filed.getName(); + if(NOT_SIGN_PARAM.contains(name)){//不参与签名或验签的参数直接跳过 + continue; + } + filed.setAccessible(true); + map.put(name, String.valueOf(filed.get(obj))); + } + + StringBuffer content = new StringBuffer(); + List<String> keys = new ArrayList(map.keySet()); + Collections.sort(keys); // 排序map + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = map.get(key); + + if(i != 0){ + content.append(SIGN_SEPARATOR); + } + content.append(key).append(SIGN_EQUAL).append(value); + } + return content.toString(); + } + + private static byte[] genMD5Sign(String signStr, String key){ + return MD5Util.getMD5(signStr + SIGN_SEPARATOR + SIGN_KEY_PARAM_NAME + SIGN_EQUAL + key); + } +} diff --git a/src/test/java/cc/mrbird/febs/ProfitTest.java b/src/test/java/cc/mrbird/febs/ProfitTest.java index 816d535..0656a84 100644 --- a/src/test/java/cc/mrbird/febs/ProfitTest.java +++ b/src/test/java/cc/mrbird/febs/ProfitTest.java @@ -7,14 +7,19 @@ import cc.mrbird.febs.mall.service.IAgentService; import cc.mrbird.febs.mall.service.IMallAchieveService; import cc.mrbird.febs.mall.service.IMemberProfitService; +import cc.mrbird.febs.pay.model.AgreeMentPaySmsDto; +import cc.mrbird.febs.pay.model.AgreementPayDto; +import cc.mrbird.febs.pay.model.AgreementSignDto; import cc.mrbird.febs.pay.model.UnipayDto; import cc.mrbird.febs.pay.service.UnipayService; import cc.mrbird.febs.rabbit.consumer.AgentConsumer; +import cn.hutool.core.date.DateUtil; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.math.BigDecimal; +import java.text.DecimalFormat; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -130,4 +135,44 @@ unipayDto.setProductName("洗护套装"); unipayService.unipay(unipayDto); } + @Test + public void getAgreeMentPaySms(){ + AgreeMentPaySmsDto agreeMentPaySmsDto = new AgreeMentPaySmsDto(); + agreeMentPaySmsDto.setOrderNo("NO_2022082514435329133"); + + BigDecimal value = new BigDecimal("0.1").setScale(2, BigDecimal.ROUND_DOWN); + DecimalFormat decimalFormat = new DecimalFormat("0.00#"); + String strVal = decimalFormat.format(value); + agreeMentPaySmsDto.setOrderAmount(new BigDecimal(strVal)); + agreeMentPaySmsDto.setName("肖永"); + agreeMentPaySmsDto.setCreatedTime(DateUtil.now()); + agreeMentPaySmsDto.setIdType("1"); + agreeMentPaySmsDto.setIdCardNum("430321199310113713"); + agreeMentPaySmsDto.setBankNo("6222031901002389639"); + agreeMentPaySmsDto.setPhone("15274802129"); + unipayService.getAgreeMentPaySms(agreeMentPaySmsDto); + } + + @Test + public void agreementSign(){ + AgreementSignDto agreementSignDto = new AgreementSignDto(); + agreementSignDto.setOrderNo("NO_2022082514435329133"); + agreementSignDto.setSmsCode("464439"); + unipayService.agreementSign(agreementSignDto); + } + + @Test + public void agreementPay(){ + AgreementPayDto agreementPayDto = new AgreementPayDto(); + agreementPayDto.setOrderNo("2022082514435329133"); + + BigDecimal value = new BigDecimal("0.1").setScale(2, BigDecimal.ROUND_DOWN); + DecimalFormat decimalFormat = new DecimalFormat("0.00#"); + String strVal = decimalFormat.format(value); + agreementPayDto.setOrderAmount(new BigDecimal(strVal)); + agreementPayDto.setOrderTime(DateUtil.now()); + agreementPayDto.setOrderDesc("测试"); + agreementPayDto.setBankNo("6222031901002389639"); + unipayService.agreementPay(agreementPayDto); + } } -- Gitblit v1.9.1