From 53fecee2436962fa5a79f373fbd75c06d6fa0ac6 Mon Sep 17 00:00:00 2001 From: zhaoyiran Date: Thu, 18 Apr 2024 15:39:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=9B=E5=95=86=E9=93=B6=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 + .../com/win/bank/enums/PaymentStatusEnum.java | 32 ++ .../com/win/bank/service/BaseBankService.java | 21 + .../com/win/bank/service/MainService.java | 27 +- .../bank/service/bocom/BocomServiceImpl.java | 28 ++ .../com/win/bank/service/cmb/ApiDemo.java | 73 ---- .../win/bank/service/cmb/CmbServiceImpl.java | 213 +++++++++- .../com/win/bank/service/cmb/DcHelper.java | 369 ------------------ .../com/win/bank/service/cmb/DeviceInfo.java | 39 -- .../win/bank/utils/BaseBankServiceUtil.java | 54 +++ .../java/com/win/bank/utils/JsonUtil.java | 20 + src/main/resources/application-dev.yml | 8 + 12 files changed, 396 insertions(+), 494 deletions(-) create mode 100644 src/main/java/com/win/bank/enums/PaymentStatusEnum.java create mode 100644 src/main/java/com/win/bank/service/BaseBankService.java create mode 100644 src/main/java/com/win/bank/service/bocom/BocomServiceImpl.java delete mode 100644 src/main/java/com/win/bank/service/cmb/ApiDemo.java delete mode 100644 src/main/java/com/win/bank/service/cmb/DcHelper.java delete mode 100644 src/main/java/com/win/bank/service/cmb/DeviceInfo.java create mode 100644 src/main/java/com/win/bank/utils/BaseBankServiceUtil.java create mode 100644 src/main/java/com/win/bank/utils/JsonUtil.java diff --git a/pom.xml b/pom.xml index e25b444..27486bb 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,12 @@ org.springframework.boot spring-boot-starter-jdbc + + + org.reflections + reflections + 0.9.12 + diff --git a/src/main/java/com/win/bank/enums/PaymentStatusEnum.java b/src/main/java/com/win/bank/enums/PaymentStatusEnum.java new file mode 100644 index 0000000..f85ca71 --- /dev/null +++ b/src/main/java/com/win/bank/enums/PaymentStatusEnum.java @@ -0,0 +1,32 @@ +package com.win.bank.enums; + +public enum PaymentStatusEnum { + + UNPROCESSED("0", "未处理"), PAYING("1", "支付中"), SUCCESS("2", "支付成功"), FAILED("3", "支付失败"); + + private final String code; + private final String description; + + PaymentStatusEnum(String code, String description) { + this.code = code; + this.description = description; + } + + public String getCode() { + return code; + } + + public String getDescription() { + return description; + } + + public static PaymentStatusEnum getByCode(String code) { + for (PaymentStatusEnum status : PaymentStatusEnum.values()) { + if (status.getCode().equals(code)) { + return status; + } + } + throw new IllegalArgumentException("Invalid PaymentStatus code: " + code); + } + +} diff --git a/src/main/java/com/win/bank/service/BaseBankService.java b/src/main/java/com/win/bank/service/BaseBankService.java new file mode 100644 index 0000000..3d3e3a7 --- /dev/null +++ b/src/main/java/com/win/bank/service/BaseBankService.java @@ -0,0 +1,21 @@ +package com.win.bank.service; + +import com.win.bank.domain.BankDO; + +public interface BaseBankService { + /** + * 支付 + * + * @param bankDO + * @return + */ + BankDO payment(BankDO bankDO); + + /** + * 查询支付结果 + * + * @param bankDO + * @return + */ + BankDO queryPaymentResult(BankDO bankDO); +} diff --git a/src/main/java/com/win/bank/service/MainService.java b/src/main/java/com/win/bank/service/MainService.java index dbb7104..956d078 100644 --- a/src/main/java/com/win/bank/service/MainService.java +++ b/src/main/java/com/win/bank/service/MainService.java @@ -4,11 +4,13 @@ import java.util.List; import javax.annotation.Resource; -import com.win.bank.service.bank.BankService; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import com.win.bank.domain.BankDO; +import com.win.bank.enums.PaymentStatusEnum; +import com.win.bank.service.bank.BankService; +import com.win.bank.utils.BaseBankServiceUtil; @Component public class MainService { @@ -16,14 +18,25 @@ public class MainService { @Resource private BankService bankService; - @Scheduled(cron = "* * * * * ?") - public void task() { - System.out.println(System.currentTimeMillis()); - - List bankDOList = bankService.listByStatus("0"); + @Scheduled(cron = "0 0/1 * * * ? ") + public void payTask() { + List bankDOList = bankService.listByStatus(PaymentStatusEnum.UNPROCESSED.getCode()); + for (BankDO bankDO : bankDOList) { + BaseBankService baseBankService = BaseBankServiceUtil.get(bankDO.getPayBankCode().toUpperCase()); + bankDO = baseBankService.payment(bankDO); + bankService.updateById(bankDO); + } + } + @Scheduled(cron = "30 0/1 * * * ? ") + public void queryPaymentResult() { + List bankDOList = bankService.listByStatus(PaymentStatusEnum.PAYING.getCode()); for (BankDO bankDO : bankDOList) { - System.out.println(bankDO); + BaseBankService baseBankService = BaseBankServiceUtil.get(bankDO.getPayBankCode().toUpperCase()); + bankDO = baseBankService.queryPaymentResult(bankDO); + if (bankDO != null) { + bankService.updateById(bankDO); + } } } } diff --git a/src/main/java/com/win/bank/service/bocom/BocomServiceImpl.java b/src/main/java/com/win/bank/service/bocom/BocomServiceImpl.java new file mode 100644 index 0000000..6c6cf87 --- /dev/null +++ b/src/main/java/com/win/bank/service/bocom/BocomServiceImpl.java @@ -0,0 +1,28 @@ +package com.win.bank.service.bocom; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import com.win.bank.domain.BankDO; +import com.win.bank.service.BaseBankService; + +/** + * 交通银行 + */ +@Service +public class BocomServiceImpl implements BaseBankService { + private static final Logger logger = LoggerFactory.getLogger(BocomServiceImpl.class); + + @Override + public BankDO payment(BankDO bankDO) { + logger.debug("交通银行 payment"); + return bankDO; + } + + @Override + public BankDO queryPaymentResult(BankDO bankDO) { + logger.debug("交通银行 queryPaymentResult"); + return bankDO; + } +} diff --git a/src/main/java/com/win/bank/service/cmb/ApiDemo.java b/src/main/java/com/win/bank/service/cmb/ApiDemo.java deleted file mode 100644 index 508525f..0000000 --- a/src/main/java/com/win/bank/service/cmb/ApiDemo.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.win.bank.service.cmb; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.Security; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Random; - -import org.bouncycastle.crypto.CryptoException; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -/** - * 招商银行银企直联国密免前置/SaaS对接示例,本示例仅供参考,不保证各种异常场景运行,请勿直接使用,如有错漏请联系对接人员。运行时,请使用所获取的测试资源替换 用户编号、公私钥、对称密钥、服务商编号等信息。 - * - * @author cmb.firmbank - * @date 2023/7/20 - */ -public class ApiDemo { - - private static final int BOUND_START = 1000000; - private static final int BOUND_END = 9000000; - // 测试地址,生产需要替换 - private static String url = "http://192.168.0.142:8080/cdcserver/api/v2"; - // private static String url = "http://cdctest.cmburl.cn:80/cdcserver/api/v2"; - // 生产地址 - // private static String url = "https://cdc.cmbchina.com/cdcserver/api/v2"; - // 银行公钥,生产需要替换 - private static String publicKey = "BNsIe9U0x8IeSe4h/dxUzVEz9pie0hDSfMRINRXc7s1UIXfkExnYECF4QqJ2SnHxLv3z/99gsfDQrQ6dzN5lZj0="; - // 生产环境银行公钥 - // private static String publicKey = - // "BEynMEZOjNpwZIiD9jXtZSGr3Ecpwn7r+m+wtafXHb6VIZTnugfuxhcKASq3hX+KX9JlHODDl9/RDKQv4XLOFak="; - // 客户私钥,生产需要替换 - private static String privateKey = "NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E="; - // 对称密钥,生产需要替换 - private static String symKey = "VuAzSWQhsoNqzn0K"; - - // 企业网银用户号,生产需要替换 - private static String uid = "N002986522"; - - private static Random random = new Random(); - - private ApiDemo() {} - - public static void main(String[] args) throws GeneralSecurityException, IOException, CryptoException { - // 装载BC库,必须在应用的启动类中调用此函数 - Security.addProvider(new BouncyCastleProvider()); - System.setProperty("sun.net.http.retryPost", "false"); - - // 业务接口名,这里是查询业务模式接口,生产请替换为对应接口名 - String funcode = "BB1PAYQR"; - // 准备接口数据,生产请替换为具体接口请求报文,包含所需的请求字段 - String currentDatetime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); - String reqid = new SimpleDateFormat("yyyyMMddHHmmssSSSS").format(new Date()) + (BOUND_START + random.nextInt(BOUND_END)); - String data = - "{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"00002\"}],\"bb1payopx1\":[{\"ccyNbr\":\"10\",\"crtAcc\":\"6214831150131511\",\"crtNam\":\"吴极客\",\"dbtAcc\":\"755936045010501\",\"nusAge\":\"用途\",\"bnkFlg\":\"Y\",\"trsAmt\":\"1.05\",\"yurRef\":\"2024041709400601\"}]},\"head\":{\"funcode\":\"BB1PAYOP\",\"userid\":\"N002986522\"}}}"; - - // String data = - // "{\"request\":{\"body\":{\"bb1payqrx1\":[{\"busCod\":\"N02030\",\"yurRef\":\"2024041709400602\"}]},\"head\":{\"funcode\":\"BB1PAYQR\",\"userid\":\"N002986522\"}}}"; - System.out.println(data); - DcHelper dchelper = new DcHelper(url, uid, privateKey, publicKey, symKey); - // 添加设备信息,请根据文档指引填写真实的信息,参数按顺序分别为:出口IP(无需)、MAC地址、CPU-ID、计算机名、主板ID、主板厂商 - // DeviceInfo deviceInfo = new DeviceInfo(null, "9822EFF12C6B", "AABBCCDDEEFFGGHH", "XXX", "XXX", "XXX"); - // String response = dchelper.sendRequest(data, funcode); - // process("Api请求1成功,响应报文:\r\n" + response); - // String response2 = dchelper.sendRequest(data, funcode, deviceInfo); - // process("Api请求2成功,响应报文:\r\n" + response2); - } - - private static void process(String response) { - - } -} diff --git a/src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java b/src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java index c6bab1d..7e9ea2e 100644 --- a/src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java +++ b/src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java @@ -1,17 +1,62 @@ package com.win.bank.service.cmb; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.*; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.win.bank.domain.BankDO; +import com.win.bank.enums.PaymentStatusEnum; +import com.win.bank.service.BaseBankService; +import com.win.bank.utils.JsonUtil; +/** + * 招商银行 + */ @Service -public class CmbServiceImpl { +public class CmbServiceImpl implements BaseBankService { + + // 企业网银用户号 + @Value("${cmb.uid}") + private String uid; + + @Value("${cmb.url}") + private String url; + + // 业务模式编号 + @Value("${cmb.busMod}") + private String busMod; - public String payment(BankDO bankDO) { - String data = "{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"00002\"}]},\"head\":{\"funcode\":\"BB1PAYOP\",\"userid\":\"N002986522\"}}}"; + // 算法,固定为国密算法 + private static final String alg = "SM"; + private static final int CONNECT_TIMEOUT = 15000; + private static final int READ_TIMEOUT = 60000; + private static final int STATUS_OK = 200; + private static final String SUCCESS_CODE = "SUC0000"; + private static final Logger logger = LoggerFactory.getLogger(CmbServiceImpl.class); + + @Override + public BankDO payment(BankDO bankDO) { + String funCode = "BB1PAYOP"; + String data = "{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"" + busMod + "\"}]},\"head\":{\"funcode\":\"" + funCode + "\",\"userid\":\"" + uid + "\"}}}"; JsonObject requestJson = new Gson().fromJson(data, JsonObject.class); JsonArray bb1payopx1 = new JsonArray(); JsonObject info = new JsonObject(); @@ -21,11 +66,167 @@ public class CmbServiceImpl { info.addProperty("ccyNbr", "10"); info.addProperty("trsAmt", bankDO.getAmount().toString()); info.addProperty("nusAge", bankDO.getPurpose()); - info.addProperty("bnkFlg", "Y"); + String bnkFlg = "Y"; + if (!StringUtils.isEmpty(bankDO.getRcvBankName()) && !bankDO.getRcvBankName().contains("招商")) { + bnkFlg = "N"; + } + info.addProperty("bnkFlg", bnkFlg); info.addProperty("yurRef", bankDO.getId().toString()); bb1payopx1.add(info); requestJson.getAsJsonObject("request").getAsJsonObject("body").add("bb1payopx1", bb1payopx1); - System.out.println(requestJson); - return null; + logger.debug("发送给招商银行的数据:" + JsonUtil.formatJson(requestJson.toString())); + String response; + try { + response = sendRequest(requestJson.toString(), funCode); + } catch (Exception e) { + throw new RuntimeException(e); + } + logger.debug("收到招商银行的数据:" + JsonUtil.formatJson(response)); + JsonObject responseJson = new Gson().fromJson(response, JsonObject.class); + JsonObject headJson = responseJson.getAsJsonObject("response").getAsJsonObject("head"); + if (SUCCESS_CODE.equals(headJson.get("resultcode").getAsString())) { + JsonObject resultJson = responseJson.getAsJsonObject("response").getAsJsonObject("body").getAsJsonArray("bb1payopz1").get(0).getAsJsonObject(); + String errCode = resultJson.get("errCod").getAsString(); + if (SUCCESS_CODE.equals(errCode)) { + bankDO.setStatus(PaymentStatusEnum.PAYING.getCode()); + } else { + bankDO.setStatus(PaymentStatusEnum.FAILED.getCode()); + bankDO.setMessage(resultJson.get("errTxt").getAsString()); + } + } else { + bankDO.setStatus(PaymentStatusEnum.FAILED.getCode()); + bankDO.setMessage(headJson.get("resultmsg").getAsString()); + } + return bankDO; + } + + @Override + public BankDO queryPaymentResult(BankDO bankDO) { + String funCode = "BB1PAYQR"; + String yurRef = bankDO.getId().toString(); + String data = "{\"request\":{\"body\":{\"bb1payqrx1\":[{\"busCod\":\"N02030\",\"yurRef\":\"" + yurRef + "\"}]},\"head\":{\"funcode\":\"" + funCode + "\",\"userid\":\"" + uid + "\"}}}"; + logger.debug("发送给招商银行的数据" + JsonUtil.formatJson(data)); + String response; + try { + response = sendRequest(data, funCode); + } catch (Exception e) { + throw new RuntimeException(e); + } + logger.debug("收到招商银行的数据:" + JsonUtil.formatJson(response)); + JsonObject responseJson = new Gson().fromJson(response, JsonObject.class); + JsonObject headJson = responseJson.getAsJsonObject("response").getAsJsonObject("head"); + if (SUCCESS_CODE.equals(headJson.get("resultcode").getAsString())) { + JsonObject resultJson = responseJson.getAsJsonObject("response").getAsJsonObject("body").getAsJsonArray("bb1payqrz1").get(0).getAsJsonObject(); + String reqSts = resultJson.get("reqSts").getAsString(); + if ("FIN".equals(reqSts)) { + if ("S".equals(resultJson.get("rtnFlg").getAsString())) { + bankDO.setStatus(PaymentStatusEnum.SUCCESS.getCode()); + bankDO.setSerialNumber(resultJson.get("trxSeq").getAsString()); + } else { + bankDO.setStatus(PaymentStatusEnum.FAILED.getCode()); + bankDO.setMessage(resultJson.get("rtnNar").getAsString()); + } + } else { + return null; + } + } else { + bankDO.setMessage(headJson.get("resultmsg").getAsString()); + } + return bankDO; + } + + public String sendRequest(String data, String funcode) throws Exception { + HashMap map = new HashMap<>(); + map.put("UID", uid); + map.put("ALG", alg); + map.put("DATA", URLEncoder.encode(data, StandardCharsets.UTF_8.displayName())); + map.put("FUNCODE", funcode); + String response = httpPost(url, map); + if (response.startsWith("CDCServer:")) { + throw new IOException("访问目标地址 " + url + " 失败:" + response); + } + return response; + } + + private static String httpPost(String httpUrl, Map param) throws IOException, GeneralSecurityException { + HttpURLConnection connection = null; + String result; + try { + URL url = new URL(httpUrl); + SSLContext sslcontext; + sslcontext = SSLContext.getInstance("SSL"); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore)null); + X509TrustManager defaultTm = null; + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + defaultTm = (X509TrustManager)tm; + break; + } + } + sslcontext.init(null, new TrustManager[] {defaultTm}, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); + + connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("POST"); + connection.setConnectTimeout(CONNECT_TIMEOUT); + connection.setReadTimeout(READ_TIMEOUT); + connection.setInstanceFollowRedirects(true); + + connection.setDoOutput(true); + connection.setDoInput(true); + + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + try (OutputStream os = connection.getOutputStream()) { + os.write(createLinkString(param).getBytes()); + if (connection.getResponseCode() != STATUS_OK) { + InputStream is = connection.getErrorStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + StringBuilder sbf = new StringBuilder(); + String temp; + while ((temp = br.readLine()) != null) { + sbf.append(temp); + sbf.append("\r\n"); + } + + result = sbf.toString(); + br.close(); + is.close(); + } else { + InputStream is = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + StringBuilder sbf = new StringBuilder(); + String temp; + while ((temp = br.readLine()) != null) { + sbf.append(temp); + } + result = sbf.toString(); + br.close(); + is.close(); + } + } + } finally { + if (connection != null) { + connection.disconnect(); + } + } + return result; + } + + private static String createLinkString(Map params) { + ArrayList keys = new ArrayList<>(params.keySet()); + Collections.sort(keys); + + StringBuilder prestr = new StringBuilder(); + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); + if (i == keys.size() - 1) { + prestr.append(key).append("=").append(value); + } else { + prestr.append(key).append("=").append(value).append("&"); + } + } + return prestr.toString(); } } diff --git a/src/main/java/com/win/bank/service/cmb/DcHelper.java b/src/main/java/com/win/bank/service/cmb/DcHelper.java deleted file mode 100644 index a0698ed..0000000 --- a/src/main/java/com/win/bank/service/cmb/DcHelper.java +++ /dev/null @@ -1,369 +0,0 @@ -package com.win.bank.service.cmb; - -import java.io.*; -import java.math.BigInteger; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.util.*; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.*; - -import org.bouncycastle.asn1.*; -import org.bouncycastle.crypto.CryptoException; -import org.bouncycastle.crypto.params.ECDomainParameters; -import org.bouncycastle.crypto.params.ECPrivateKeyParameters; -import org.bouncycastle.crypto.params.ECPublicKeyParameters; -import org.bouncycastle.crypto.params.ParametersWithID; -import org.bouncycastle.crypto.signers.SM2Signer; -import org.bouncycastle.jce.ECNamedCurveTable; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECParameterSpec; -import org.bouncycastle.math.ec.ECPoint; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -/** - * 招商银行银企直联国密免前置/SaaS对接示例,本示例仅供参考,不保证各种异常场景运行,请勿直接使用,如有错漏请联系对接人员。运行时,请使用所获取的测试资源替换 用户编号、公私钥、对称密钥、服务商编号等信息。 - * - * @author cmb.firmbank - * @date 2023/7/20 - */ -public class DcHelper { - - private static final int LENGTH_32 = 32; - private static final int USERID_LEN = 16; - private static final int CONNECT_TIMEOUT = 15000; - private static final int READ_TIMEOUT = 60000; - private static final int STATUS_OK = 200; - private static Base64.Encoder encoder = Base64.getEncoder(); - private static Base64.Decoder decoder = Base64.getDecoder(); - - // 请求URL - private final String url; - // 企业网银用户号 - private final String uid; - // 国密算法向量,根据用户号生成 - private final byte[] userId; - // 算法,固定为国密算法 - private final String alg; - // 客户私钥 - private final byte[] privateKey; - // 银行公钥 - private final byte[] publicKey; - // 协商的对称密钥 - private final byte[] symKey; - - private static final Logger logger = LoggerFactory.getLogger(DcHelper.class); - - public DcHelper(String url, String uid, String privateKey, String publicKey, String symKey) { - this.url = url; - this.uid = uid; - this.userId = getUserId(uid); - this.alg = "SM"; - this.privateKey = decoder.decode(privateKey); - this.publicKey = decoder.decode(publicKey); - this.symKey = symKey.getBytes(StandardCharsets.UTF_8); - } - - public String sendRequest(String data, String funcode) throws IOException, CryptoException, GeneralSecurityException { - // 增加设备信息 - JsonObject requestJson = new Gson().fromJson(data, JsonObject.class); - // requestJson.getAsJsonObject("request").add("exthdr", deviceInfo.toJson()); - // 对请求报文做排序 - String source = recursiveKeySort(requestJson); - // 生成签名 - // byte[] signature = cmbSM2SignWithSM3(userId, privateKey, source.getBytes(StandardCharsets.UTF_8)); - // 替换签名字段 - // requestJson.getAsJsonObject("signature").addProperty("sigdat", new String(encoder.encode(signature), - // StandardCharsets.UTF_8)); - - // 对数据进行对称加密 - // String request = requestJson.toString(); - // byte[] encryptRequest = cmbSM4Crypt(symKey, userId, request.getBytes(StandardCharsets.UTF_8), 1); - // String encryptedRequest = new String(encoder.encode(encryptRequest), StandardCharsets.UTF_8); - logger.debug(data); - // 发送请求 - HashMap map = new HashMap<>(); - map.put("UID", uid); - map.put("ALG", alg); - map.put("DATA", URLEncoder.encode(data, StandardCharsets.UTF_8.displayName())); - // map.put("DATA", URLEncoder.encode(encryptedRequest, StandardCharsets.UTF_8.displayName())); - map.put("FUNCODE", funcode); - String response = httpPost(url, map); - // logger.debug(response); - printJson(data); - if (response.startsWith("CDCServer:")) { - throw new IOException("访问目标地址 " + url + " 失败:" + response); - } - - // 返回结果解密 - // response = new String((cmbSM4Crypt(symKey, userId, decoder.decode(response), 2)), StandardCharsets.UTF_8); - - // 验证签名是否正确 - // JsonObject responseJson = new Gson().fromJson(response, JsonObject.class); - // JsonObject signatureJson = responseJson.getAsJsonObject("signature"); - // String responseSignature = signatureJson.get("sigdat").getAsString(); - // signatureJson.addProperty("sigdat", "__signature_sigdat__"); - // responseJson.add("signature", signatureJson); - // String responseSorted = recursiveKeySort(responseJson); - // boolean verify = cmbSM2VerifyWithSM3(userId, publicKey, responseSorted.getBytes(StandardCharsets.UTF_8), - // decoder.decode(responseSignature)); - // if (!verify) { - // throw new IOException("响应报文的签名无效"); - // } - printJson(response); - return response; - } - - private static String recursiveKeySort(JsonObject json) { - StringBuilder appender = new StringBuilder(); - appender.append("{"); - Iterator keys = new TreeSet<>(json.keySet()).iterator(); - boolean isFirstEle = true; - while (keys.hasNext()) { - if (!isFirstEle) { - appender.append(","); - } - String key = keys.next(); - Object val = json.get(key); - if (val instanceof JsonObject) { - appender.append("\"").append(key).append("\":"); - appender.append(recursiveKeySort((JsonObject)val)); - } else if (val instanceof JsonArray) { - JsonArray jarray = (JsonArray)val; - appender.append("\"").append(key).append("\":["); - boolean isFirstArrEle = true; - for (int i = 0; i < jarray.size(); i++) { - if (!isFirstArrEle) { - appender.append(","); - } - Object obj = jarray.get(i); - if (obj instanceof JsonObject) { - appender.append(recursiveKeySort((JsonObject)obj)); - } else { - appender.append(obj.toString()); - } - isFirstArrEle = false; - } - appender.append("]"); - } else { - String value = val.toString(); - appender.append("\"").append(key).append("\":").append(value); - } - isFirstEle = false; - } - appender.append("}"); - return appender.toString(); - } - - private static byte[] getUserId(String uid) { - return (uid + "0000000000000000").substring(0, USERID_LEN).getBytes(); - } - - private static String httpPost(String httpUrl, Map param) throws IOException, GeneralSecurityException { - HttpURLConnection connection = null; - String result; - try { - URL url = new URL(httpUrl); - SSLContext sslcontext; - sslcontext = SSLContext.getInstance("SSL"); - TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init((KeyStore)null); - X509TrustManager defaultTm = null; - for (TrustManager tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - defaultTm = (X509TrustManager)tm; - break; - } - } - sslcontext.init(null, new TrustManager[] {defaultTm}, new java.security.SecureRandom()); - HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); - - connection = (HttpURLConnection)url.openConnection(); - connection.setRequestMethod("POST"); - connection.setConnectTimeout(CONNECT_TIMEOUT); - connection.setReadTimeout(READ_TIMEOUT); - connection.setInstanceFollowRedirects(true); - - connection.setDoOutput(true); - connection.setDoInput(true); - - connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - try (OutputStream os = connection.getOutputStream()) { - os.write(createLinkString(param).getBytes()); - if (connection.getResponseCode() != STATUS_OK) { - InputStream is = connection.getErrorStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); - StringBuilder sbf = new StringBuilder(); - String temp; - while ((temp = br.readLine()) != null) { - sbf.append(temp); - sbf.append("\r\n"); - } - - result = sbf.toString(); - br.close(); - is.close(); - } else { - InputStream is = connection.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); - StringBuilder sbf = new StringBuilder(); - String temp; - while ((temp = br.readLine()) != null) { - sbf.append(temp); - } - result = sbf.toString(); - br.close(); - is.close(); - } - } - } finally { - if (connection != null) { - connection.disconnect(); - } - } - return result; - } - - private static String createLinkString(Map params) { - ArrayList keys = new ArrayList<>(params.keySet()); - Collections.sort(keys); - - StringBuilder prestr = new StringBuilder(); - for (int i = 0; i < keys.size(); i++) { - String key = keys.get(i); - String value = params.get(key); - if (i == keys.size() - 1) { - prestr.append(key).append("=").append(value); - } else { - prestr.append(key).append("=").append(value).append("&"); - } - } - return prestr.toString(); - } - - // 以下是加解密相关的函数 - - private static byte[] cmbSM2SignWithSM3(byte[] id, byte[] privkey, byte[] msg) throws IOException, CryptoException { - if (privkey == null || msg == null) { - throw new CryptoException("CMBSM2SignWithSM3 input error"); - } - ECPrivateKeyParameters privateKey = encodePrivateKey(privkey); - SM2Signer signer = new SM2Signer(); - ParametersWithID parameters = new ParametersWithID(privateKey, id); - signer.init(true, parameters); - signer.update(msg, 0, msg.length); - return decodeDERSignature(signer.generateSignature()); - } - - private static ECPrivateKeyParameters encodePrivateKey(byte[] value) { - BigInteger d = new BigInteger(1, value); - ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1"); - ECDomainParameters ecParameters = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed()); - return new ECPrivateKeyParameters(d, ecParameters); - } - - private static byte[] decodeDERSignature(byte[] signature) throws IOException { - ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(signature)); - ASN1Sequence primitive = (ASN1Sequence)stream.readObject(); - Enumeration enumeration = primitive.getObjects(); - BigInteger intR = enumeration.nextElement().getValue(); - BigInteger intS = enumeration.nextElement().getValue(); - byte[] bytes = new byte[LENGTH_32 * 2]; - byte[] r = format(intR.toByteArray()); - byte[] s = format(intS.toByteArray()); - System.arraycopy(r, 0, bytes, 0, LENGTH_32); - System.arraycopy(s, 0, bytes, LENGTH_32, LENGTH_32); - return bytes; - } - - private static byte[] format(byte[] value) { - if (value.length == LENGTH_32) { - return value; - } else { - byte[] bytes = new byte[LENGTH_32]; - if (value.length > LENGTH_32) { - System.arraycopy(value, value.length - LENGTH_32, bytes, 0, LENGTH_32); - } else { - System.arraycopy(value, 0, bytes, LENGTH_32 - value.length, value.length); - } - return bytes; - } - } - - private static byte[] cmbSM4Crypt(byte[] key, byte[] iv, byte[] input, int mode) throws GeneralSecurityException { - SecretKeySpec spec = new SecretKeySpec(key, "SM4"); - IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); - Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME); - cipher.init(mode, spec, ivParameterSpec); - return cipher.doFinal(input); - } - - private static boolean cmbSM2VerifyWithSM3(byte[] id, byte[] pubkey, byte[] msg, byte[] signature) throws IOException { - - if (pubkey == null || msg == null || signature == null) { - throw new IllegalArgumentException("CMBSM2VerifyWithSM3 input error"); - } - ECPublicKeyParameters publicKey = encodePublicKey(pubkey); - SM2Signer signer = new SM2Signer(); - ParametersWithID parameters = new ParametersWithID(publicKey, id); - signer.init(false, parameters); - signer.update(msg, 0, msg.length); - return signer.verifySignature(encodeDERSignature(signature)); - } - - private static ECPublicKeyParameters encodePublicKey(byte[] value) { - byte[] x = new byte[LENGTH_32]; - byte[] y = new byte[LENGTH_32]; - System.arraycopy(value, 1, x, 0, LENGTH_32); - System.arraycopy(value, LENGTH_32 + 1, y, 0, LENGTH_32); - BigInteger intX = new BigInteger(1, x); - BigInteger intY = new BigInteger(1, y); - ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1"); - ECPoint intQ = spec.getCurve().createPoint(intX, intY); - ECDomainParameters ecParameters = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed()); - return new ECPublicKeyParameters(intQ, ecParameters); - } - - private static byte[] encodeDERSignature(byte[] signature) throws IOException { - byte[] r = new byte[LENGTH_32]; - byte[] s = new byte[LENGTH_32]; - System.arraycopy(signature, 0, r, 0, LENGTH_32); - System.arraycopy(signature, LENGTH_32, s, 0, LENGTH_32); - ASN1EncodableVector vector = new ASN1EncodableVector(); - vector.add(new ASN1Integer(new BigInteger(1, r))); - vector.add(new ASN1Integer(new BigInteger(1, s))); - return (new DERSequence(vector)).getEncoded(); - } - - private void printJson(String jsonStr) { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(SerializationFeature.INDENT_OUTPUT); - - try { - // 原始的JSON字符串 - // String jsonStr = "{\"name\": \"Alice\", \"age\": 30, \"city\": \"Beijing\"}"; - - // 格式化打印JSON字符串 - Object json = mapper.readValue(jsonStr, Object.class); - String formattedJsonStr = mapper.writeValueAsString(json); - // System.out.println(formattedJsonStr); - logger.debug(formattedJsonStr); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/com/win/bank/service/cmb/DeviceInfo.java b/src/main/java/com/win/bank/service/cmb/DeviceInfo.java deleted file mode 100644 index fc25a66..0000000 --- a/src/main/java/com/win/bank/service/cmb/DeviceInfo.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.win.bank.service.cmb; - -import com.google.gson.JsonObject; - -/** - * 设备信息 - */ -public class DeviceInfo { - - private String ipVal; - private String macVal; - private String cpuInfo; - private String computerName; - private String mainBoardId; - private String mainBoardName; - - public DeviceInfo(String ipVal, String macVal, String cpuInfo, String computerName, String mainBoardId, String mainBoardName) { - this.ipVal = ipVal; - this.macVal = macVal; - this.cpuInfo = cpuInfo; - this.computerName = computerName; - this.mainBoardId = mainBoardId; - this.mainBoardName = mainBoardName; - } - - public JsonObject toJson() { - JsonObject deviceInfo = new JsonObject(); - if (ipVal != null) { - deviceInfo.addProperty("ipType", ipVal.indexOf(':') != -1 ? "6" : "4"); - deviceInfo.addProperty("ipVal", ipVal); - } - deviceInfo.addProperty("macVal", macVal); - deviceInfo.addProperty("cpuInfo", cpuInfo); - deviceInfo.addProperty("cmpName", computerName); - deviceInfo.addProperty("mbId", mainBoardId); - deviceInfo.addProperty("mbMf", mainBoardName); - return deviceInfo; - } -} diff --git a/src/main/java/com/win/bank/utils/BaseBankServiceUtil.java b/src/main/java/com/win/bank/utils/BaseBankServiceUtil.java new file mode 100644 index 0000000..dc58427 --- /dev/null +++ b/src/main/java/com/win/bank/utils/BaseBankServiceUtil.java @@ -0,0 +1,54 @@ +package com.win.bank.utils; + +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.annotation.PostConstruct; + +import org.reflections.Reflections; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import com.win.bank.service.BaseBankService; + +@Component +public class BaseBankServiceUtil implements ApplicationContextAware { + + ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = applicationContext; + } + + private static Map map = new HashMap<>(); + + public static BaseBankService get(String code) { + return map.get(code); + } + + @PostConstruct + public void init() { + String packageName = "com.win.bank.service"; + Reflections reflections = new Reflections(packageName); + Set> subTypes = reflections.getSubTypesOf(BaseBankService.class); + + for (Class clazz : subTypes) { + if (!Modifier.isAbstract(clazz.getModifiers()) && !clazz.isInterface()) { + String key = clazz.getPackageName().substring(clazz.getPackageName().lastIndexOf(".") + 1).toUpperCase(); + BaseBankService instance = null; + try { + instance = (BaseBankService)context.getBean(Class.forName(clazz.getName())); + map.put(key, instance); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + } + +} diff --git a/src/main/java/com/win/bank/utils/JsonUtil.java b/src/main/java/com/win/bank/utils/JsonUtil.java new file mode 100644 index 0000000..cb48eb6 --- /dev/null +++ b/src/main/java/com/win/bank/utils/JsonUtil.java @@ -0,0 +1,20 @@ +package com.win.bank.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; + +public class JsonUtil { + + public static String formatJson(String jsonStr) { + try { + ObjectMapper mapper = new ObjectMapper(); + Object json = mapper.readValue(jsonStr, Object.class); + ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); + String prettyJson = '\n' + writer.writeValueAsString(json); + return prettyJson; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index f336f5b..51d47cc 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -31,3 +31,11 @@ logging: level: com.win: debug org.springframework: warn + +###################### 以下是银行配置 ###################### + +# 招商银行 +cmb: + uid: N002986522 + busMod: 00002 + url: http://192.168.0.142:8080/cdcserver/api/v2 \ No newline at end of file