20 files added
5 files modified
New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | |
| | | 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: |
| | | |
| | | } |
| | |
| | | // 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\\\"}"; |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | } |
New file |
| | |
| | | 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"; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | 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 = ":"; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
| | |
| | | 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); |
| | | |
| | | } |
| | |
| | | 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; |
| | |
| | | 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 |
| | |
| | | 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"); /**交易币种 */ |
| | |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | @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(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | 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); |
| | | } |
| | | } |