فهرست منبع

增加易乾接口

周箭河 5 سال پیش
والد
کامیت
e5a326d348

BIN
libs/SADK-3.2.5.2.jar


+ 40 - 1
pom.xml

@@ -15,6 +15,7 @@
 		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 		<maven.compiler.source>1.8</maven.compiler.source>
 		<maven.compiler.target>1.8</maven.compiler.target>
+		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
 	</properties>
 
 	<dependencyManagement>
@@ -45,7 +46,18 @@
 				<artifactId>commons-beanutils</artifactId>
 				<version>1.9.2</version>
 			</dependency>
-
+			<dependency>
+				<groupId>org.springframework.cloud</groupId>
+				<artifactId>spring-cloud-dependencies</artifactId>
+				<version>${spring-cloud.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.projectlombok</groupId>
+				<artifactId>lombok</artifactId>
+				<optional>true</optional>
+			</dependency>
 		</dependencies>
 	</dependencyManagement>
 
@@ -144,6 +156,21 @@
 			<artifactId>fastjson</artifactId>
 			<version>1.2.28</version>
 		</dependency>
+		<dependency>
+			<groupId>cfca.sadk</groupId>
+			<artifactId>cfca.sadk</artifactId>
+			<version>1.0.0</version>
+			<scope>system</scope>
+			<systemPath>${project.basedir}/libs/SADK-3.2.5.2.jar</systemPath>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-freemarker</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-starter-openfeign</artifactId>
+		</dependency>
 	</dependencies>
 
 	<build>
@@ -161,6 +188,18 @@
 					</execution>
 				</executions>
 			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-resources-plugin</artifactId>
+				<configuration><encoding>UTF-8</encoding>
+					<!-- 过滤后缀为pem、pfx的证书文件 -->
+					<nonFilteredFileExtensions>
+						<nonFilteredFileExtension>cer</nonFilteredFileExtension>
+						<nonFilteredFileExtension>pem</nonFilteredFileExtension>
+						<nonFilteredFileExtension>pfx</nonFilteredFileExtension>
+					</nonFilteredFileExtensions>
+				</configuration>
+			</plugin>
 		</plugins>
 	</build>
 </project>

+ 2 - 0
src/main/java/com/ym/mec/collectfee/CollectFeeServerApplication.java

@@ -4,6 +4,7 @@ import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 
@@ -12,6 +13,7 @@ import org.springframework.context.annotation.Configuration;
 @ComponentScan(basePackages="com.ym.mec.collectfee")
 @ServletComponentScan//filter才能生效
 @Configuration
+@EnableFeignClients
 public class CollectFeeServerApplication {
 
 	public static void main(String[] args) {

+ 185 - 0
src/main/java/com/ym/mec/collectfee/controller/YqPayController.java

@@ -0,0 +1,185 @@
+package com.ym.mec.collectfee.controller;
+
+import com.ym.mec.collectfee.entity.Order;
+import com.ym.mec.collectfee.service.YqPayService;
+import com.ym.mec.collectfee.service.YqQueryService;
+import com.ym.mec.collectfee.service.impl.OrderServiceImpl;
+import com.ym.mec.collectfee.utils.GenerateNum;
+import com.ym.mec.collectfee.utils.yqpay.Msg;
+import com.ym.mec.collectfee.utils.yqpay.YqPayUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@RestController
+@RequestMapping("yqPay")
+public class YqPayController {
+
+    @Autowired
+    private YqPayService yqPayService;
+    @Autowired
+    private YqQueryService yqQueryService;
+    @Autowired
+    private OrderServiceImpl orderService;
+
+
+    /**
+     * 统一下单
+     *
+     * @return String
+     * @throws Exception
+     */
+    @RequestMapping("/toPay")
+    @Transactional
+    public Msg toPay(@ModelAttribute Order order) throws Exception {
+
+        String orderNo = GenerateNum.getInstance().GenerateOrderNo();
+        System.out.println(order);
+        orderService.insert(order);
+
+
+        /*卖家双乾商户号*/
+        String sellerNo = "0022652"; //某笔定义到某个商户
+        /*支付渠道*/
+        String payChannels = "weChatPay";
+        /*卖家双乾商户号*/
+        /*订单信息*/
+        String orderBody = "大雅乐盟培训课程";
+        /*金额*/
+        String payAmount = "50";
+        /*API支付类型1-即时支付,2-担保支付,3-预授权支付*/
+        String apiPayType = "1";
+        /*API商户订单号*/
+        String merMerOrderNo = "0855555322";
+        /*买家商户号*/
+        String buyerNo = "";
+        /*不参与优惠计算的金额*/
+        String undiscountableAmount = "";
+        /*订单标题*/
+        String orderSubject = "培训订单";
+        /*商品列表信息*/
+        String goodsDetail = "";
+        /*交易类型1—充值,0—收款*/
+        String tradeType = "0";
+
+
+        String notifyUrl = "http://47.99.212.176:9000/yqpay/notify";
+        String returnUrl = "http://47.99.212.176:9000/yqpay/notify";//前台页面通知地址(银联H5必填)
+
+        Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
+        resultMap.put("sellerNo", sellerNo); //收款商户号
+        resultMap.put("payChannels", "unionPay"); //支付方式(支付渠道)
+        resultMap.put("orderBody", orderBody); //订单信息
+        resultMap.put("payAmount", payAmount); //支付金额
+        resultMap.put("apiPayType", "1"); //*API支付类型1-即时支付,2-担保支付,3-预授权支付*/
+        resultMap.put("tradeType", tradeType); //*交易类型1—充值,0—收款*
+        resultMap.put("merMerOrderNo", orderNo); //商户订单号
+        resultMap.put("orderSubject", orderSubject); //订单标题
+        resultMap.put("returnUrl", returnUrl); //前台页面通知地址(银联H5必填)
+        resultMap.put("mchAppId", returnUrl); //填网站首页地址(银联H5必填)
+        resultMap.put("mchAppName", returnUrl); //传wap网站名(银联H5必填)
+        resultMap.put("deviceInfo", "AND_WAP"); //设置信息(银联H5必填)
+        resultMap.put("clientIp", "27.18.213.149"); //客户端ip地址(银联H5必填)
+        resultMap.put("goodsDetail", goodsDetail);
+        Map<String, Object> requestMap = new YqPayUtil(notifyUrl, resultMap).getRequestMap();
+        return yqPayService.toPay(requestMap);
+    }
+
+    /**
+     * 交易查询
+     *
+     * @param merOrderNoList 用户订单号
+     * @return
+     * @throws Exception
+     */
+    @RequestMapping("/query")
+    public String query(String merOrderNoList) throws Exception {
+        String notifyUrl = "http://47.99.212.176:9000/yqpay/notify"; //回调地址
+        Map<String, Object> resultMap = new LinkedHashMap<>();
+        resultMap.put("merOrderNoList", merOrderNoList);
+        Map<String, Object> requestMap = new YqPayUtil(notifyUrl, resultMap).getRequestMap();
+        return yqQueryService.orderQuery(requestMap);
+    }
+
+    /**
+     * 用户信息(商户)查询
+     *
+     * @param sonMerNo 子商户号
+     * @return
+     * @throws Exception
+     */
+    @RequestMapping("/queryaccount")
+    public String queryAccount(String sonMerNo) throws Exception {
+        String notifyUrl = "http://47.99.212.176:9000/yqpay/notify"; //回调地址
+        Map<String, Object> resultMap = new LinkedHashMap<>();
+        resultMap.put("merOrderNoList", sonMerNo);
+        Map<String, Object> requestMap = new YqPayUtil(notifyUrl, resultMap).getRequestMap();
+        return yqQueryService.queryAccount(requestMap);
+    }
+
+    /**
+     * 对账查询(定时任务每天对账)
+     *
+     * @return
+     */
+    public String queryBill() throws Exception {
+        String notifyUrl = ""; //回调地址
+        Map<String, Object> resultMap = new LinkedHashMap<>();
+        resultMap.put("tradeDate", ""); //交易日期
+        resultMap.put("payState", ""); //订单状态
+        resultMap.put("tradeType", ""); //交易类型,不填为全部
+        resultMap.put("channelType", ""); //通道类型,不填为全部
+        Map<String, Object> requestMap = new YqPayUtil(notifyUrl, resultMap).getRequestMap();
+        return yqQueryService.billQuery(requestMap);
+    }
+
+
+    /**
+     * 平台转账
+     *
+     * @return
+     * @throws Exception
+     */
+    @RequestMapping("/platformtransferacc")
+    public String platformTransferAcc() throws Exception {
+        String notifyUrl = ""; //回调地址
+        Map<String, Object> resultMap = new LinkedHashMap<>();
+        resultMap.put("payeeNo", ""); //收款方商户号
+        resultMap.put("payeeName", ""); //收款方姓名
+        resultMap.put("amount", ""); //金额
+        resultMap.put("merOrderNo", ""); //商户订单号
+        resultMap.put("remarks", ""); //备注
+        Map<String, Object> requestMap = new YqPayUtil(notifyUrl, resultMap).getRequestMap();
+        return yqPayService.platformTransferAcc(requestMap);
+    }
+
+
+    /**
+     * 易乾异步通知接口
+     *
+     * @param msg
+     * @return String
+     * @throws Exception
+     */
+    @RequestMapping("/notify")
+    public Object notify(Msg msg) throws Exception {
+        Map<String, Object> rqMap = new LinkedHashMap<String, Object>();
+        rqMap.put("code", msg.getCode());
+        rqMap.put("msg", msg.getMsg());
+        rqMap.put("responseType", msg.getResponseType());
+        rqMap.put("responseParameters", msg.getResponseParameters());
+        rqMap.put("sign", msg.getSign());
+        boolean rs = YqPayUtil.verify(rqMap);
+        if (rs) {
+
+        }
+        return "";
+    }
+
+
+}

+ 39 - 0
src/main/java/com/ym/mec/collectfee/controller/YqRegController.java

@@ -0,0 +1,39 @@
+package com.ym.mec.collectfee.controller;
+
+import com.ym.mec.collectfee.utils.yqpay.YqPayUtil;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Controller
+@RequestMapping("yqreg")
+public class YqRegController {
+
+    /**
+     * 用户(商户)注册
+     *
+     * @param map
+     * @return
+     * @throws Exception
+     */
+    @GetMapping("/register")
+    public String register(ModelMap map) throws Exception {
+
+        String notifyUrl = "http://47.99.212.176:9000/yqpay/notify"; //主动通知地址
+
+        Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
+        resultMap.put("type", "H5");
+        resultMap.put("returnUrl", "https://www.baidu.com");
+        resultMap.put("seraialNumber", "785679");
+        resultMap.put("merType", "per");
+
+        Map<String, Object> requestMap = new YqPayUtil(notifyUrl, resultMap).getRequestMap();
+        map.addAttribute("info", requestMap);
+        return "reg/register";
+    }
+
+}

+ 18 - 0
src/main/java/com/ym/mec/collectfee/service/YqPayService.java

@@ -0,0 +1,18 @@
+package com.ym.mec.collectfee.service;
+
+import com.ym.mec.collectfee.utils.yqpay.Msg;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.Map;
+
+@FeignClient(value = "yqPay", url = "${yq-pay.pay-host}")
+public interface YqPayService {
+    @RequestMapping(name = "统一下单",value = "/api/api/pay/toPay", method=RequestMethod.POST)
+    Msg toPay(@RequestParam Map<String, Object> map);
+
+    @RequestMapping(name = "平台转账",value = "/api/api/platformTransfer/toAcc", method=RequestMethod.POST)
+    String platformTransferAcc(@RequestParam Map<String, Object> map);
+}

+ 20 - 0
src/main/java/com/ym/mec/collectfee/service/YqQueryService.java

@@ -0,0 +1,20 @@
+package com.ym.mec.collectfee.service;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.Map;
+
+@FeignClient(value = "yqQuery", url = "${yq-pay.query-host}")
+public interface YqQueryService {
+    @RequestMapping(name = "订单查询", value = "/query/trade/tradeQuery", method = RequestMethod.POST)
+    String orderQuery(@RequestParam Map<String, Object> map);
+
+    @RequestMapping(name = "用户信息查询", value = "/query/account/queryAccount", method = RequestMethod.POST)
+    String queryAccount(@RequestParam Map<String, Object> map);
+
+    @RequestMapping(name = "对账查询", value = "/query/bill/billQuery", method = RequestMethod.POST)
+    String billQuery(@RequestParam Map<String, Object> map);
+}

+ 63 - 0
src/main/java/com/ym/mec/collectfee/utils/GenerateNum.java

@@ -0,0 +1,63 @@
+package com.ym.mec.collectfee.utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 生成订单号类
+ */
+public class GenerateNum {
+    // 使用单例模式,不允许直接创建实例
+    private GenerateNum() {}
+
+    // 创建一个空实例对象,类需要用的时候才赋值
+    private static GenerateNum g = null;
+
+    // 单例模式--懒汉模式
+    public static synchronized GenerateNum getInstance() {
+        if (g == null) {
+            g = new GenerateNum();
+        }
+        return g;
+    }
+
+    // 全局自增数
+    private static int count = 0;
+
+    // 每毫秒秒最多生成多少订单(最好是像9999这种准备进位的值)
+    private static final int total = 9999;
+
+    // 格式化的时间字符串
+    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+
+    // 获取当前时间年月日时分秒毫秒字符串
+    private static String getNowDateStr() {
+        return sdf.format(new Date());
+    }
+
+    // 记录上一次的时间,用来判断是否需要递增全局数
+    private static String now = null;
+
+    /*
+     * 生成一个订单号
+     */
+    public synchronized String GenerateOrderNo() {
+        String datastr = getNowDateStr();
+        if (datastr.equals(now)) {
+            count++;// 自增
+        } else {
+            count = 1;
+            now = datastr;
+        }
+        int countInteger = String.valueOf(total).length() - String.valueOf(count).length();// 算补位
+        String bu = "";// 补字符串
+        for (int i = 0; i < countInteger; i++) {
+            bu += "0";
+        }
+        bu += String.valueOf(count);
+        if (count >= total) {
+            count = 0;
+        }
+        return datastr + bu;
+    }
+}

+ 287 - 0
src/main/java/com/ym/mec/collectfee/utils/yqpay/CFCARAUtil.java

@@ -0,0 +1,287 @@
+package com.ym.mec.collectfee.utils.yqpay;
+
+import cfca.sadk.algorithm.common.Mechanism;
+import cfca.sadk.algorithm.common.PKIException;
+import cfca.sadk.lib.crypto.JCrypto;
+import cfca.sadk.system.FileHelper;
+import cfca.sadk.util.*;
+import cfca.sadk.x509.certificate.X509Cert;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.crypto.spec.SecretKeySpec;
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Map;
+
+
+public class CFCARAUtil {
+    private static final String deviceName = JCrypto.JSOFT_LIB;
+    private static cfca.sadk.lib.crypto.Session session = null;
+
+
+    static {
+        try {
+            JCrypto jCrypto = JCrypto.getInstance();
+            jCrypto.initialize(deviceName, null);
+            session = jCrypto.openSession(deviceName);
+        } catch (PKIException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /******* p1 *********/
+    /**
+     *
+     * p1消息签名
+     * @param :message
+     * @return
+     * @throws Exception
+     *
+     */
+    public static String signMessageByP1(String message, String pfxPath, String passWord) throws Exception{
+        File file = new ClassPathResource(pfxPath).getFile();
+        PrivateKey userPriKey = KeyUtil.getPrivateKeyFromPFX(new FileInputStream(file), passWord);
+        Signature signature = new Signature();
+        byte[] base64P7SignedData = signature.p1SignMessage(Mechanism.SHA256_RSA, message.getBytes("UTF-8"), userPriKey, session);
+        return new String(base64P7SignedData);
+    }
+
+    /**
+     * p1消息校验(公钥证书验签)
+     * @param :beforeSignedData
+     * @param :afterSignedData
+     * @param :certPath
+     * @return
+     * @throws Exception
+     */
+    public static boolean verifyMessageByP1(String beforeSignedData, String afterSignedData, String certPath) throws Exception{
+        File file = new ClassPathResource(certPath).getFile();
+        X509Cert cert = new X509Cert(new FileInputStream(file));
+        PublicKey publicKey = cert.getPublicKey();
+        Signature signature = new Signature();
+        return signature.p1VerifyMessage(Mechanism.SHA256_RSA, beforeSignedData.getBytes("UTF-8"), afterSignedData.getBytes("UTF-8"), publicKey, session);
+    }
+    /**
+     * p1消息校验(公钥证书验签)
+     * @return
+     * @throws Exception
+     */
+    public static boolean verifyMessageByP1Location(String beforeSignedData, String afterSignedData, String certPath) throws Exception{
+        X509Cert cert = new X509Cert(new FileInputStream(certPath));
+        PublicKey publicKey = cert.getPublicKey();
+        Signature signature = new Signature();
+        return signature.p1VerifyMessage(Mechanism.SHA256_RSA, beforeSignedData.getBytes("UTF-8"), afterSignedData.getBytes("UTF-8"), publicKey, session);
+    }
+
+    /**
+     *
+     * p1消息校验(公钥字符串验签)
+     * @param :beforeSignedData
+     * @param :afterSignedData
+     * @param :publicKeyStr
+     * @return
+     * @throws Exception
+     *
+     */
+    public static boolean verifyMessageByP1AndPubKey(String beforeSignedData, String afterSignedData, String publicKeyStr) throws Exception{
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKeyStr));
+        PublicKey publicKey = keyFactory.generatePublic(keySpec);
+        Signature signature = new Signature();
+        return signature.p1VerifyMessage(Mechanism.SHA256_RSA, beforeSignedData.getBytes("UTF-8"), afterSignedData.getBytes("UTF-8"), publicKey, session);
+    }
+
+    /********* RSA_PKCS ***********/
+    /**
+     *  RSA证书加密消息
+     *  RSA_PKCS公钥加密
+     * @param :message
+     * @throws Exception
+     */
+    public static String encryptMessageByRSA_PKCS(String message, String certPath) throws Exception{
+        File file = new ClassPathResource(certPath).getFile();
+        X509Cert cert = new X509Cert(new FileInputStream(file));
+        PublicKey userPubKey = cert.getPublicKey();
+        Mechanism mechanism = new Mechanism(Mechanism.RSA_PKCS);
+        byte[] encryptedData = EncryptUtil.encrypt(mechanism, userPubKey, message.getBytes("UTF-8"), session);
+        return new String(encryptedData);
+    }
+
+    /**
+     *  RSA证书加密消息
+     *  RSA_PKCS公钥字符串加密
+     * @param :message
+     * @throws Exception
+     */
+    public static String encryptMessageByRSA_PKCS_String(String message, String publicKeyStr) throws Exception{
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKeyStr));
+        PublicKey userPubKey = keyFactory.generatePublic(keySpec);
+
+//        File file = new ClassPathResource(certPath).getFile();
+//        X509Cert cert = new X509Cert(new FileInputStream(file));
+//        PublicKey userPubKey = cert.getPublicKey();
+        Mechanism mechanism = new Mechanism(Mechanism.RSA_PKCS);
+        byte[] encryptedData = EncryptUtil.encrypt(mechanism, userPubKey, message.getBytes("UTF-8"), session);
+        return new String(encryptedData);
+    }
+
+    /**
+     * RSA证书解密消息
+     * RSA_PKCS私钥解密
+     * @param :key
+     * @param :message
+     * @throws Exception
+     */
+    public static String decryptMessageByRSA_PKCS(String message, String pfxPath, String passWord) throws Exception{
+        File file = new ClassPathResource(pfxPath).getFile();
+        PrivateKey userPriKey = KeyUtil.getPrivateKeyFromPFX(new FileInputStream(file), passWord);
+        Mechanism mechanism = new Mechanism(Mechanism.RSA_PKCS);
+        byte[] dataBytes = message.getBytes("UTF-8");
+        byte[] encryptedData = EncryptUtil.decrypt(mechanism, userPriKey, dataBytes, session);
+        return new String(encryptedData);
+    }
+
+    /********* RC4 **********/
+    /**
+     * RSA证书加密消息
+     * RC4对称加密
+     * @param :message
+     * @return
+     * @throws Exception
+     */
+    public static String encryptMessageByRC4(String message, String pfxPath, String passWord) throws Exception{
+        byte[] data = FileHelper.read(pfxPath);
+        Key key = new SecretKeySpec(Base64.decode(Base64.encode(data)), "RC4");
+        Mechanism mechanism = new Mechanism(Mechanism.RC4);
+        byte[] dataBytes = message.getBytes("UTF-8");
+        byte[] encryptedData = EncryptUtil.encrypt(mechanism, key, dataBytes, session);
+        return new String(encryptedData);
+
+    }
+
+    /**
+     * RSA证书解密消息
+     * RC4对称解密
+     * @param :message
+     * @return
+     * @throws Exception
+     */
+    public static String decryptMessageByRC4(String message, String pfxPath, String passWord) throws Exception{
+        byte[] data = FileHelper.read(pfxPath);
+        Key key = new SecretKeySpec(Base64.decode(Base64.encode(data)), "RC4");
+        Mechanism mechanism = new Mechanism(Mechanism.RC4);
+        byte[] encryptedData = EncryptUtil.decrypt(mechanism, key, message.getBytes("UTF-8"), session);
+        return new String(encryptedData);
+    }
+
+    /****** p7 ******/
+    /**
+     * P7 分离式文件签名(签名)
+     */
+    public static String signData(String toBeSigned, String certPath, String certPass) throws Exception {
+        X509Cert cert = CertUtil.getCertFromPFX(certPath, certPass);
+        PrivateKey priKey = KeyUtil.getPrivateKeyFromPFX(certPath, certPass);
+        Signature signature = new Signature();
+        return new String(signature.p7SignMessageDetach(Mechanism.SHA256_RSA, toBeSigned.getBytes("UTF8"), priKey, cert, session), "UTF8");
+    }
+
+    /**
+     * P7 分离式消息校验(验签)
+     */
+    public static boolean verifySignature(String data, String signdata) throws Exception {
+        Signature signature = new Signature();
+        return signature.p7VerifyMessageDetach(data.getBytes("UTF8"), signdata.getBytes("UTF8"), session);
+    }
+
+    /**
+     * 消息数字信封(公钥加密)
+     */
+    public static String encryptData(String data, String certPath) throws Exception {
+        // X509Cert cert = CertUtil.getCertFromPFX(certPath, certPass);
+        X509Cert cert = new X509Cert(new FileInputStream(certPath));
+        X509Cert[] recvcerts = new X509Cert[]{ cert };
+        return new String(EnvelopeUtil.envelopeMessage(data.getBytes("UTF8"), Mechanism.RC4, recvcerts, session), "UTF8");
+    }
+
+    /**
+     * 数据解密
+     */
+    public static String decryptData(String encryptedData, String certPath, String certPass) throws Exception {
+        PrivateKey priKey = KeyUtil.getPrivateKeyFromPFX(certPath, certPass);
+        X509Cert cert = CertUtil.getCertFromPFX(certPath, certPass);
+        return new String(EnvelopeUtil.openEvelopedMessage(encryptedData.getBytes("UTF8"), priKey, cert, session), "UTF8");
+    }
+
+
+
+    /**
+     * 拼接字符串方法
+     * @param map
+     * @param connector
+     * @return
+     */
+    public static String joinMapValue(Map<String, Object> map, char connector)	{
+        StringBuffer b = new StringBuffer();
+        for (Map.Entry<String, Object> entry : map.entrySet()){
+            b.append(entry.getKey());
+            b.append('=');
+            if (entry.getValue() != null){
+                b.append(entry.getValue());
+            }
+            b.append(connector);
+        }
+        return b.toString().substring(0, b.length()-1);
+    }
+
+
+    public static void main(String[] args) throws Exception {
+        // 实际地址,请改为项目路径
+        String pfxPath = "D:\\certificate\\168885_test.pfx";
+        // 实际地址,请改为项目路径
+        String certPath = "D:\\certificate\\168885_test.cer";
+        String password = "123123";
+        String plaintext = "幸福是你有食物吃,睡觉的地方,有所爱的人。";
+        String plaintext1 = "幸福是你有食物吃,睡觉的地方,有所爱的人";
+
+        /******* p1 ******/
+        //签名
+        String base64P7SignedData = signMessageByP1(plaintext, pfxPath, password);
+        //验签
+        boolean verifyByp1 = verifyMessageByP1(plaintext, base64P7SignedData, certPath);
+        System.out.println("p1-cert:"+verifyByp1);
+
+        //验签
+        String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApSC4H4PvPuS9GJq9chCHq"
+                + "PHb+MK2dYRwVlU+9LJHhEA0mbmkhbSyvcakHuvrXtrBCBt5GMSU2BQeZy2IqQoZDJ"
+                + "Cn5CHufgMUpyMD7qvRo+GOg3GRC3k506ebb/Od/LL0eMAcCiOcCC7HHiPGP44VtBs"
+                + "OgqX22/BSAxyK93bnQbb4+8sc4id0io403rLjBle7vIzrNJtqftuTSQJMm/OmRDvf"
+                + "hg0asdUZYCsb3TdhRqO5hblDl/s/5b6gFTYcgPAw9qKdknqAWGqHP/J6i3GDAqedq"
+                + "7lFuDvkqSnYnWgVzpv9luWzrvXYOl2K4fvDSl9JIXHUMMz9cELEJjmq7yM+fQIDAQ"
+                + "AB";
+        boolean verifyByp1PublicKeyStr = verifyMessageByP1AndPubKey(plaintext, base64P7SignedData, publicKeyStr);
+        System.out.println("p1-PublicKeyStr:"+verifyByp1PublicKeyStr);
+
+        File file = new File("D:\\certificate\\168885_test.cer");
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate)cf.generateCertificate(new FileInputStream(file));
+        PublicKey publicKey = cert.getPublicKey();
+        String publicKeyString = Base64.toBase64String(publicKey.getEncoded());
+        System.out.println("-----------------公钥--------------------");
+        System.out.println(publicKeyString);
+        System.out.println("-----------------公钥--------------------");
+    }
+
+
+
+
+
+}

+ 375 - 0
src/main/java/com/ym/mec/collectfee/utils/yqpay/DateUtils.java

@@ -0,0 +1,375 @@
+package com.ym.mec.collectfee.utils.yqpay;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+	
+	private static String[] parsePatterns = {
+		"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", 
+		"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+		"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+	public static final String YYYY_MM_DD = "yyyyMMdd";
+	public static final String HH_SS_MM = "HHssmm";
+	public static final String YYYY_MM_DD_DEF = "yyyy-MM-dd";
+	public static final String YYYY_MM_DD_HH_MM_SS = "yyyyMMddHHmmss";
+	public static final String DATE_WEB_FORMAT = "yyyy-MM-dd HH:mm:ss";
+	public static final String DATE_WEB_FORMAT_NO_SS = "yyyy-MM-dd HH:mm";
+	public static final String E_MMM_DD_HH_MM_SS_Z_YYYY = "E MMM dd hh:mm:ss z yyyy";//日期格式 "Tue Sep 26 13:56:19 CST 2017"
+	public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyyMMddHHmmssSSS";
+
+	public static void main(String[] args) {
+		getTimeOfY0(new Date());
+		int i=0;
+	}
+
+	public static String getDate() {
+		return getDate("yyyy-MM-dd");
+	}
+
+	public static String getDate(String pattern) {
+		return DateFormatUtils.format(new Date(), pattern);
+	}
+
+
+
+	public static String formatDate(Date date, Object... pattern) {
+		if (date == null)
+			return null;
+
+		String formatDate;
+		if (pattern != null && pattern.length > 0) {
+			formatDate = DateFormatUtils.format(date, pattern[0].toString());
+		} else {
+			formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");
+		}
+		return formatDate;
+	}
+
+	public static String formatDateTime(Date date) {
+		return formatDate(date, "yyyy-MM-dd HH:mm:ss");
+	}
+
+	public static Date getTimeOfY0(Date date) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.set(Calendar.MONTH, 0);
+		cal.set(Calendar.DATE, 1);
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return  cal.getTime();
+	}
+
+	public static Date getTimeOfM0(Date date) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.set(Calendar.DATE, 1);
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return  cal.getTime();
+	}
+
+	public static Date getTimeOfD0(Date date){
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return  cal.getTime();
+	}
+
+	public static Date getTimeOfH0(Date date){
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return  cal.getTime();
+	}
+
+	public static Date getFirstTimeOfMonth(Date date){
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.set(Calendar.DAY_OF_MONTH,
+				cal.getActualMinimum(Calendar.DAY_OF_MONTH));
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return  cal.getTime();
+	}
+
+	public static Date getLastTimeOfMonth(Date date){
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.add(Calendar.MONTH, 1);
+		cal.set(Calendar.DAY_OF_MONTH,
+				cal.getActualMinimum(Calendar.DAY_OF_MONTH));
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return  cal.getTime();
+	}
+
+	/*是否为指定月份的最后一天*/
+	public static boolean isLastDayOfMonth(Date date) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
+		if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
+			return true;
+		}
+		return false;
+	}
+
+	/*是否为本月第一天*/
+	public static boolean isFirstDayOfThisMonth(Date date) {
+		if(date==null){
+			return false;
+		}
+		String now = formatDate(date,"yyyy-MM-dd");
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(new Date());
+		calendar.set(Calendar.DAY_OF_MONTH,1);
+		Date firstDate = calendar.getTime();
+		String first = formatDate(firstDate,"yyyy-MM-dd");
+
+		if(now.equals(first)){
+			return true;
+		}else {
+			return false;
+		}
+	}
+
+	public static String getYearAndMonth(Date date){
+		String ym = formatDate(date,"yyyy-MM");
+		return ym;
+	}
+
+	public static String getTime() {
+		return formatDate(new Date(), "HH:mm:ss");
+	}
+
+	public static String getDateTime() {
+		return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
+	}
+
+	public static String getYear() {
+		return formatDate(new Date(), "yyyy");
+	}
+
+	public static String getMonth() {
+		return formatDate(new Date(), "MM");
+	}
+
+	public static String getDay() {
+		return formatDate(new Date(), "dd");
+	}
+
+	public static String getHour(Date date) {
+		return formatDate(date, "HH");
+	}
+
+	public static String getWeek() {
+		return formatDate(new Date(), "E");
+	}
+	public static String getWeekByParam(Date date) {
+		return formatDate(date, "E");
+	}
+
+	public static Date parseDate(Object str) {
+		if (str == null){
+			return null;
+		}
+		try {
+			return parseDate(str.toString(), parsePatterns);
+		} catch (ParseException e) {
+			return null;
+		}
+	}
+
+	public static long pastDays(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(24*60*60*1000);
+	}
+
+	public static long pastHour(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(60*60*1000);
+	}
+
+	public static long pastMinutes(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(60*1000);
+	}
+
+    public static String formatDateTime(long timeMillis){
+		long day = timeMillis/(24*60*60*1000);
+		long hour = (timeMillis/(60*60*1000)-day*24);
+		long min = ((timeMillis/(60*1000))-day*24*60-hour*60);
+		long s = (timeMillis/1000-day*24*60*60-hour*60*60-min*60);
+		long sss = (timeMillis-day*24*60*60*1000-hour*60*60*1000-min*60*1000-s*1000);
+		return (day>0?day+",":"")+hour+":"+min+":"+s+"."+sss;
+    }
+
+	public static double getDistanceOfTwoDate(Date before, Date after) {
+		long beforeTime = before.getTime();
+		long afterTime = after.getTime();
+		return (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
+	}
+
+	/**
+	 *  日期大小比较
+	 * @param date1
+	 * @param date2
+	 * @return date2 == date1-->0
+	 * @return date2 > date1-->1
+	 * @return date2 < date1-->-1
+	 * @return -2 异常
+	 */
+	public static int dateStringCompare(Date date1,String date2) {
+		DateFormat df = new SimpleDateFormat("yyyy-MM");
+		try {
+			Date dt1 = df.parse(date2);
+			Date dt2 = df.parse(df.format(date1));;
+			if (dt1.getTime()>dt2.getTime()){
+				return  1;
+			}else if (dt1.getTime()== dt2.getTime()){
+				return  0;
+			}else {
+				return  -1;
+			}
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		return  2;
+	}
+
+	/**
+	 * 取得当月天数
+	 * */
+	public static int getCurrentMonthLastDay()
+	{
+		Calendar a = Calendar.getInstance();
+		a.set(Calendar.DATE, 1);
+		a.roll(Calendar.DATE, -1);
+		int maxDate = a.get(Calendar.DATE);
+		return maxDate;
+	}
+
+
+	/**
+	 * 字符串转换成日期
+	 * @param str
+	 * @param formatType
+	 * @return
+	 */
+	public static Date StrToDate(String str,String formatType) {
+
+		SimpleDateFormat format = new SimpleDateFormat(formatType);
+		Date date = null;
+		try {
+			//date = format.parse(new SimpleDateFormat(formatType).format(str));
+			date = format.parse(str);
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		return date;
+	}
+
+	public static List<String> getWeekend(Integer year){
+		List<String> list = new ArrayList<String>();
+		try{
+			SimpleDateFormat df=new SimpleDateFormat("yyyy-mm-dd");
+			Date date=null;
+			Calendar calendar=Calendar.getInstance();
+			int totalDay;
+			if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0){//闰年的判断规则
+				totalDay=366;
+			}else{
+				totalDay=365;
+			}
+			date=df.parse(year+"-01-01");
+			Calendar cal = Calendar.getInstance();
+			cal.setTime(date);
+			for(int i=0;i<totalDay;i++){
+				if(cal.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY||cal.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY) {
+					list.add(DateUtils.formatDate(cal.getTime(),"yyyy-MM-dd"));
+				}
+				cal.add(Calendar.DAY_OF_MONTH,1);
+			}
+		}catch(ParseException e){
+			e.printStackTrace();
+		}
+
+		return list;
+	}
+
+	/**
+	 * 使用SimpleDateFormat类对时间字符串的合法性进行校验
+	 *
+	 * @param dateStr
+	 *            将要被校验合法性的时间字符串,格式:yyyyMMddHHmmss
+	 */
+	public static Boolean checkLegalityInClassSimpleDateFormat(String dateStr,String format) {
+		SimpleDateFormat sdf = new SimpleDateFormat(format);
+		try {
+			Date realDate = sdf.parse(dateStr);
+			if (dateStr.equals(sdf.format(realDate))) {
+				return true;
+			} else {
+				return false;
+			}
+			// System.out.println(realDate.toString());
+		} catch (ParseException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}// end method - checkLegalityInClassSimpleDateFormat
+
+    /**
+     * 判断日期是否为周末
+     *
+     * @param date 入参日期
+     * @return true:为周末
+     */
+    public static boolean isWeekend(Date date) {
+        if (date == null)
+            return false;
+
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+        return cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY;
+    }
+
+    /*
+ * 计算时间差
+ */
+    public static int getTwoDayTimes(String fromDate ,String toDate) {
+        SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
+        try {
+            long from = simpleFormat.parse(fromDate).getTime();
+            long to = simpleFormat.parse(toDate).getTime();
+            int seconds = (int) ((to - from)/(1000));
+            return seconds;
+        } catch (ParseException e) {
+            e.printStackTrace();
+            return -1;
+        }
+    }
+
+}

+ 50 - 0
src/main/java/com/ym/mec/collectfee/utils/yqpay/Msg.java

@@ -0,0 +1,50 @@
+package com.ym.mec.collectfee.utils.yqpay;
+
+public class Msg {
+
+    private String code;
+    private String msg;
+    private String responseType;
+    private String responseParameters;
+    private String sign;
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public String getResponseType() {
+        return responseType;
+    }
+
+    public void setResponseType(String responseType) {
+        this.responseType = responseType;
+    }
+
+    public String getResponseParameters() {
+        return responseParameters;
+    }
+
+    public void setResponseParameters(String responseParameters) {
+        this.responseParameters = responseParameters;
+    }
+
+    public String getSign() {
+        return sign;
+    }
+
+    public void setSign(String sign) {
+        this.sign = sign;
+    }
+}

+ 48 - 0
src/main/java/com/ym/mec/collectfee/utils/yqpay/YqPayUtil.java

@@ -0,0 +1,48 @@
+package com.ym.mec.collectfee.utils.yqpay;
+
+import com.alibaba.fastjson.JSON;
+import com.ym.mec.collectfee.service.YqPayService;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class YqPayUtil {
+    private YqPayService yqPayService;
+
+    private String merNo = "0021677";
+    private static final String version = "1.1";
+    private String notifyUrl;
+    private String timestamp;
+    private String apiContent;
+    private static final String signType = "CFCA";
+    private String sign;
+    private Map<String, Object> resultMap;
+    private Map<String, Object> requestMap;
+
+    public YqPayUtil(String notifyUrl, Map<String, Object> resultMap) throws Exception {
+        Map<String, Object> rqMap = new LinkedHashMap<String, Object>();
+        rqMap.put("merNo", this.merNo);
+        rqMap.put("version", this.version);
+        rqMap.put("notifyUrl", notifyUrl);
+        rqMap.put("timestamp", DateUtils.getDateTime());
+        rqMap.put("apiContent", JSON.toJSONString(resultMap));
+        rqMap.put("signType", signType);
+        String beforeSignedData = CFCARAUtil.joinMapValue(rqMap, '&');
+        this.sign = CFCARAUtil.signMessageByP1(beforeSignedData, "certificate/yqpay.pfx", "aaa123123");
+        rqMap.put("sign", this.sign);
+        this.requestMap = rqMap;
+    }
+
+    public Map<String, Object> getRequestMap() {
+        return this.requestMap;
+    }
+
+    //验签
+    public static boolean verify(Map<String, Object> rsMap) throws Exception {
+        String sign = (String) rsMap.get("sign");
+        rsMap.remove("sign");
+        String beforeSignedData = CFCARAUtil.joinMapValue(rsMap, '&');
+        return CFCARAUtil.verifyMessageByP1(beforeSignedData, sign, "certificate/yqpay.cer");
+    }
+
+}

+ 6 - 1
src/main/resources/application.yml

@@ -50,4 +50,9 @@ mybatis:
     
 logging:
   config: classpath:logback-spring.xml
-  path: d:/var/logs
+  path: d:/var/logs
+
+yq-pay:
+  pay-host: https://qyfapi.95epay.com
+  query-host: https://qyfquery.95epay.com
+  merno: 0021677

BIN
src/main/resources/certificate/yqpay.cer


BIN
src/main/resources/certificate/yqpay.pfx


+ 24 - 0
src/main/resources/reg/register.ftl

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="zh_cn">
+<head>
+    <meta charset="UTF-8">
+    <title>register</title>
+</head>
+<body>
+<form action="https://qyfapi.95epay.com/api/api/page/registerPage" method="POST" id="sent_register">
+    <p>First merNo: <input type="text" name="merNo" value="${info.merNo}"/></p>
+    <p>Last version: <input type="text" name="version" value="${info.version}"/></p>
+    <p>Last notifyUrl: <input type="text" name="notifyUrl" value="${info.notifyUrl}"/></p>
+    <p>Last timestamp: <input type="text" name="timestamp" value="${info.timestamp}"/></p>
+    <p>Last apiContent: <input type="text" name="apiContent" value='${info.apiContent}'/></p>
+    <p>Last signType: <input type="text" name="signType" value="${info.signType}"/></p>
+    <p>Last sign: <input type="text" name="sign" value="${info.sign}"/></p>
+    <input type="submit">
+</form>
+</body>
+<script type="text/javascript">
+    window.onload = function () {
+        document.getElementById("sent_register").submit()
+    }
+</script>
+</html>