33333333333
This commit is contained in:
@@ -128,6 +128,7 @@ public enum TrdPayTypeEnum {
|
|||||||
* 支付宝 8 微信 4
|
* 支付宝 8 微信 4
|
||||||
*/
|
*/
|
||||||
V11("http://bgxa.peiqi.zhifusg.com","/Pay_Index.html","/Pay_Trade_query.html","/api/pay/trd/notify/v11","OK"),
|
V11("http://bgxa.peiqi.zhifusg.com","/Pay_Index.html","/Pay_Trade_query.html","/api/pay/trd/notify/v11","OK"),
|
||||||
|
V12("https://cashapi.sandpay.com.cn","/gateway/trade","/Pay_Trade_query.html","/api/pay/trd/notify/v11","OK"),
|
||||||
|
|
||||||
;
|
;
|
||||||
private final String gatewayUrl;
|
private final String gatewayUrl;
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.ruoyi.cai.domain.PayTrdConfig;
|
||||||
|
import com.ruoyi.cai.pay.PayManager;
|
||||||
|
import com.ruoyi.cai.pay.PayOrderInfoDTO;
|
||||||
|
import com.ruoyi.cai.pay.PayReturnResp;
|
||||||
|
import com.ruoyi.cai.service.OrderLogsService;
|
||||||
|
import com.ruoyi.cai.trdpay.PayTrdService;
|
||||||
|
import com.ruoyi.cai.trdpay.TrdPayTypeEnum;
|
||||||
|
import com.ruoyi.cai.trdpay.dto.NotifyResp;
|
||||||
|
import com.ruoyi.cai.trdpay.handle.v12.SandPayClient;
|
||||||
|
import com.ruoyi.cai.trdpay.handle.v12.SandPayConfig;
|
||||||
|
import com.ruoyi.cai.trdpay.handle.v12.SandpayConfigUtil;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.ServletUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class PayTrdV12Service implements PayTrdService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderLogsService orderLogsService;
|
||||||
|
@Autowired
|
||||||
|
private PayManager payManager;
|
||||||
|
|
||||||
|
private SandPayClient sandPayClient = null;
|
||||||
|
|
||||||
|
public synchronized void init(String accessMid){
|
||||||
|
if(sandPayClient == null){
|
||||||
|
// 初始化sandpay参数(全局设置一次)
|
||||||
|
SandPayConfig config = new SandPayConfig();
|
||||||
|
// 接入商户号
|
||||||
|
config.setAccessMid(accessMid);
|
||||||
|
// 加签 加密参数设置
|
||||||
|
config.setEncryptType(SandpayConfigUtil.AES_STR);
|
||||||
|
config.setPrivateKeyPath("D:\\mmm\\6888806128148.pfx");//存放目录:src\main\resources\cert\prod
|
||||||
|
config.setPrivateKeyPassword("926645");
|
||||||
|
config.setSandPublicKeyPath("D:\\mmm\\sand_pro.cer");//存放目录:src\main\resources\cert\prod
|
||||||
|
// 请求版本号
|
||||||
|
config.setVersion("4.0.0");
|
||||||
|
sandPayClient = new SandPayClient(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TrdPayTypeEnum getType() {
|
||||||
|
return TrdPayTypeEnum.V12;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayReturnResp createOrderAli(PayOrderInfoDTO payOrderInfoDTO, PayTrdConfig payTrdConfig,boolean wx) {
|
||||||
|
init(payTrdConfig.getMchId());
|
||||||
|
TrdPayTypeEnum type = getType();
|
||||||
|
JSONObject bizData = new JSONObject();
|
||||||
|
bizData.put("mid","6888806128148");
|
||||||
|
bizData.put("outOrderNo",payOrderInfoDTO.getOrderNo());
|
||||||
|
bizData.put("description","知予-"+payOrderInfoDTO.getSubject());
|
||||||
|
bizData.put("goodsClass","99");
|
||||||
|
bizData.put("amount", payOrderInfoDTO.getPrice());
|
||||||
|
JSONArray payExtra = new JSONArray();
|
||||||
|
if(wx){
|
||||||
|
bizData.put("funcCodeList", new String[]{"02010005"});
|
||||||
|
/**
|
||||||
|
* subAppId : wxe78b5fdab7ddebbc
|
||||||
|
* ghOriId : gh_40fd947b7d3f
|
||||||
|
* pathUrl : pages/zf/index?
|
||||||
|
* miniProgramType : 0
|
||||||
|
*/
|
||||||
|
JSONObject exPayExtra = new JSONObject();
|
||||||
|
exPayExtra.put("funcCode","02010005");
|
||||||
|
exPayExtra.put("subAppId","wxe78b5fdab7ddebbc");
|
||||||
|
exPayExtra.put("ghOriId","gh_40fd947b7d3f");
|
||||||
|
exPayExtra.put("pathUrl","pages/zf/index?");
|
||||||
|
exPayExtra.put("miniProgramType","0");
|
||||||
|
payExtra.add(exPayExtra);
|
||||||
|
}else{
|
||||||
|
bizData.put("funcCodeList", new String[]{"02020004"});
|
||||||
|
}
|
||||||
|
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
|
||||||
|
JSONObject payerInfo = new JSONObject();
|
||||||
|
payerInfo.put("frontUrl", notifyUrl);
|
||||||
|
payerInfo.put("payExtra",payExtra);
|
||||||
|
bizData.put("payerInfo",payerInfo);
|
||||||
|
bizData.put("notifyUrl", notifyUrl);
|
||||||
|
JSONObject reqReserved = new JSONObject();
|
||||||
|
reqReserved.put("sourceIp", ServletUtils.getClientIP());
|
||||||
|
bizData.put("sdCashierType", "SDK");
|
||||||
|
bizData.put("metaOption", "[{\"s\":\"Android\",\"n\":\"\",\"id\":\"\",\"sc\":\"\"},{\"s\":\"IOS\",\"n\":\"\",\"id\":\"\",\"sc\":\"\"}]");
|
||||||
|
bizData.put("reqReserved",reqReserved);
|
||||||
|
|
||||||
|
String gatewayUrl = getGatewayUrl(payTrdConfig);
|
||||||
|
String createOrderUrl = gatewayUrl + type.getCreateOrderUrl();
|
||||||
|
JSONObject responseJson = sandPayClient.execute(createOrderUrl, bizData);
|
||||||
|
if(responseJson == null || !"accept".equals(responseJson.getString("resultStatus"))){
|
||||||
|
orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl+JSON.toJSONString(responseJson), com.alibaba.fastjson2.JSONObject.from(responseJson), false, type, getStepName(wx));
|
||||||
|
log.info("第三方支付失败 V12 统一支付失败失败 url={} params={} body={}, payTrdConfig={}", createOrderUrl, JSON.toJSONString(bizData), JSON.toJSONString(responseJson), JSON.toJSONString(payTrdConfig));
|
||||||
|
throw new ServiceException("调用支付失败");
|
||||||
|
}else{
|
||||||
|
orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl+JSON.toJSONString(responseJson), com.alibaba.fastjson2.JSONObject.from(responseJson), true, type, getStepName(wx));
|
||||||
|
}
|
||||||
|
return PayReturnResp.createH5(responseJson.getString("cashierUrl"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotifyResp getNotifyResp(Map<String, String> sourceData) {
|
||||||
|
String mchOrderNo = sourceData.get("orderid");
|
||||||
|
String payOrderId = sourceData.get("transaction_id");
|
||||||
|
String status = sourceData.get("returncode");
|
||||||
|
NotifyResp resp = new NotifyResp();
|
||||||
|
resp.setOrderNo(mchOrderNo);
|
||||||
|
resp.setTrdOrderNo(payOrderId);
|
||||||
|
resp.setPayTypeEnum(getType());
|
||||||
|
resp.setSourceData(sourceData);
|
||||||
|
resp.setSuccess("00".equals(status));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public com.alibaba.fastjson2.JSONObject queryOrder(String orderNo, PayTrdConfig payTrdConfig) {
|
||||||
|
throw new RuntimeException("暂时不支持订单查询");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public com.alibaba.fastjson2.JSONObject resetOrder(String orderNo, PayTrdConfig payTrdConfig, boolean updateData) {
|
||||||
|
throw new RuntimeException("暂时不支持订单充值");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -68,7 +68,7 @@ public class PayTrdV7Service implements PayTrdService {
|
|||||||
boolean success = checkSuccess(jsonObject);
|
boolean success = checkSuccess(jsonObject);
|
||||||
orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl+JSON.toJSONString(map), jsonObject, success, type, getStepName(wx));
|
orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl+JSON.toJSONString(map), jsonObject, success, type, getStepName(wx));
|
||||||
if(!success){
|
if(!success){
|
||||||
log.info("第三方支付失败 V7 统一支付失败失败 url={} params={} body={}, payTrdConfig={}, typeEnum={}", createOrderUrl,JSON.toJSONString(map), body, JSON.toJSONString(payTrdConfig), JSON.toJSONString(jsonObject));
|
log.info("第三方支付失败 V7 统一支付失败失败 url={} params={} body={}, payTrdConfig={}", createOrderUrl,JSON.toJSONString(map), body, JSON.toJSONString(payTrdConfig));
|
||||||
throw new ServiceException("调用支付失败");
|
throw new ServiceException("调用支付失败");
|
||||||
}
|
}
|
||||||
String payUrl = jsonObject.getJSONObject("data").getString("payUrl");
|
String payUrl = jsonObject.getJSONObject("data").getString("payUrl");
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
|
||||||
|
public class AESUtils {
|
||||||
|
private static final String KEY_ALGORITHM = "AES";
|
||||||
|
private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
|
||||||
|
|
||||||
|
public AESUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encrypt(byte[] plainBytes, byte[] keyBytes, String IV) throws Exception {
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||||
|
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
|
||||||
|
if (StringUtils.isNotBlank(IV)) {
|
||||||
|
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
|
||||||
|
cipher.init(1, secretKey, ips);
|
||||||
|
} else {
|
||||||
|
cipher.init(1, secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipher.doFinal(plainBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt(byte[] encryptedBytes, byte[] keyBytes, String IV) throws Exception {
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||||
|
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
|
||||||
|
if (StringUtils.isNotBlank(IV)) {
|
||||||
|
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
|
||||||
|
cipher.init(2, secretKey, ips);
|
||||||
|
} else {
|
||||||
|
cipher.init(2, secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipher.doFinal(encryptedBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
public static final Logger logger = LoggerFactory.getLogger(FileUtils.class);
|
||||||
|
|
||||||
|
public FileUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputStream loadFile(String fileName) {
|
||||||
|
if (StringUtils.isNotBlank(fileName)) {
|
||||||
|
return loadFromAbsoluteFile(fileName);
|
||||||
|
// return absolutePathStart(fileName) ? loadFromAbsoluteFile(fileName) : loadFromClasspathFile(fileName);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputStream loadFromAbsoluteFile(String fileName) {
|
||||||
|
try {
|
||||||
|
File f = new File(fileName);
|
||||||
|
return !f.exists() ? null : new FileInputStream(f);
|
||||||
|
} catch (Throwable var3) {
|
||||||
|
logger.warn("load file[" + fileName + "] fail", var3);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean absolutePathStart(String path) {
|
||||||
|
File[] files = File.listRoots();
|
||||||
|
File[] var2 = files;
|
||||||
|
int var3 = files.length;
|
||||||
|
|
||||||
|
for(int var4 = 0; var4 < var3; ++var4) {
|
||||||
|
File file = var2[var4];
|
||||||
|
if (path.startsWith(file.getPath())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputStream loadFromClasspathFile(String fileName) {
|
||||||
|
try {
|
||||||
|
// return ClassLoader.getSystemResourceAsStream(fileName);
|
||||||
|
return FileUtils.class.getClassLoader().getResourceAsStream(fileName);
|
||||||
|
} catch (Throwable var2) {
|
||||||
|
logger.warn("load file[" + fileName + "] fail", var2);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputStream loadFromRelativeFile(String fileName) {
|
||||||
|
String userDir = System.getProperty("user.dir");
|
||||||
|
String realFilePath = addSeparator(userDir) + fileName;
|
||||||
|
return loadFromAbsoluteFile(realFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String addSeparator(String dir) {
|
||||||
|
if (!dir.endsWith(File.separator)) {
|
||||||
|
dir = dir + File.separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
public class HttpClientUtils {
|
||||||
|
public static final Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
|
||||||
|
|
||||||
|
public HttpClientUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String sendPost(String url, String param, int connectTimeout, int readTimeout) {
|
||||||
|
PrintWriter out = null;
|
||||||
|
BufferedReader in = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL realUrl = new URL(url);
|
||||||
|
URLConnection conn = realUrl.openConnection();
|
||||||
|
conn.setRequestProperty("Content-Type", "application/json");
|
||||||
|
conn.setRequestProperty("accept", "*/*");
|
||||||
|
conn.setRequestProperty("connection", "Keep-Alive");
|
||||||
|
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
conn.setConnectTimeout(connectTimeout);
|
||||||
|
conn.setReadTimeout(readTimeout);
|
||||||
|
out = new PrintWriter(conn.getOutputStream());
|
||||||
|
out.print(param);
|
||||||
|
out.flush();
|
||||||
|
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String line = null;
|
||||||
|
|
||||||
|
while((line = in.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
String var10 = sb.toString();
|
||||||
|
return var10;
|
||||||
|
} catch (Exception var19) {
|
||||||
|
throw new SandPayException("请求服务器失败,url:" + url + " err:" + var19.getMessage(), var19);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (out != null) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in != null) {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
} catch (IOException var18) {
|
||||||
|
logger.error("发送POST方法的请求,关闭流异常:", var18);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class RSAUtils {
|
||||||
|
|
||||||
|
private static final String ALGORITHM = "RSA/ECB/PKCS1Padding";
|
||||||
|
private static final int KEY_LENGTH = 2048;
|
||||||
|
private static final int RESERVE_SIZE = 11;
|
||||||
|
|
||||||
|
public RSAUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encryptToBase64Text(String plainTextBytes, PublicKey publicKey) throws Exception {
|
||||||
|
byte[] aesKeyBytes = plainTextBytes.getBytes();
|
||||||
|
byte[] encryptKeyBytes = encrypt(aesKeyBytes, publicKey);
|
||||||
|
return Base64.getEncoder().encodeToString(encryptKeyBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decryptBase64ToText(String encText, PrivateKey privateKey) throws Exception {
|
||||||
|
byte[] encTextBytes = Base64.getDecoder().decode(encText);
|
||||||
|
byte[] contentBytes = decrypt(encTextBytes, privateKey);
|
||||||
|
String decryptText = new String(contentBytes, StandardCharsets.UTF_8);
|
||||||
|
return decryptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encrypt(byte[] plainTextBytes, PublicKey publicKey) throws Exception {
|
||||||
|
boolean keyByteSize = true;
|
||||||
|
boolean encryptBlockSize = true;
|
||||||
|
int nBlock = plainTextBytes.length / 245;
|
||||||
|
if (plainTextBytes.length % 245 != 0) {
|
||||||
|
++nBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||||
|
cipher.init(1, publicKey);
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(nBlock * 256);
|
||||||
|
|
||||||
|
for(int offset = 0; offset < plainTextBytes.length; offset += 245) {
|
||||||
|
int inputLen = plainTextBytes.length - offset;
|
||||||
|
if (inputLen > 245) {
|
||||||
|
inputLen = 245;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] decryptedBlock = cipher.doFinal(plainTextBytes, offset, inputLen);
|
||||||
|
byteArrayOutputStream.write(decryptedBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
byteArrayOutputStream.flush();
|
||||||
|
byteArrayOutputStream.close();
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt(byte[] cipherTextBytes, PrivateKey privateKey) throws Exception {
|
||||||
|
boolean keyByteSize = true;
|
||||||
|
boolean decryptBlockSize = true;
|
||||||
|
int nBlock = cipherTextBytes.length / 256;
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||||
|
cipher.init(2, privateKey);
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(nBlock * 245);
|
||||||
|
|
||||||
|
for(int offset = 0; offset < cipherTextBytes.length; offset += 256) {
|
||||||
|
int inputLen = cipherTextBytes.length - offset;
|
||||||
|
if (inputLen > 256) {
|
||||||
|
inputLen = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] decryptedBlock = cipher.doFinal(cipherTextBytes, offset, inputLen);
|
||||||
|
byteArrayOutputStream.write(decryptedBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
byteArrayOutputStream.flush();
|
||||||
|
byteArrayOutputStream.close();
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateSign(String content, PrivateKey privateKey, String signType, String charset) throws Exception {
|
||||||
|
Signature signature = null;
|
||||||
|
if (!"RSA".equals(signType) && !"SHA256WithRSA".equals(signType)) {
|
||||||
|
throw new SandPayException("不是支持的签名类型 : signType=" + signType);
|
||||||
|
} else {
|
||||||
|
signature = Signature.getInstance("SHA256WithRSA");
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
if (StringUtils.isBlank(charset)) {
|
||||||
|
signature.update(content.getBytes());
|
||||||
|
} else {
|
||||||
|
signature.update(content.getBytes(charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] signed = signature.sign();
|
||||||
|
return Base64.getEncoder().encodeToString(signed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @Description: 验签
|
||||||
|
* @param content 响应报文bizData
|
||||||
|
* @param sign 响应报文中的sign
|
||||||
|
* @param signType 响应报文中的signType
|
||||||
|
* @param publicKey 杉德公钥
|
||||||
|
* @param charset 编码格式
|
||||||
|
* @return: boolean
|
||||||
|
*/
|
||||||
|
public static boolean verifySign(String content, String sign, String signType, PublicKey publicKey, String charset) throws Exception {
|
||||||
|
Signature signature = Signature.getInstance(signType);
|
||||||
|
signature.initVerify(publicKey);
|
||||||
|
if (StringUtils.isBlank(charset)) {
|
||||||
|
signature.update(content.getBytes());
|
||||||
|
} else {
|
||||||
|
signature.update(content.getBytes(charset));
|
||||||
|
}
|
||||||
|
return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicKey getPublicKeyByStr(String publicKeyStr) throws Exception {
|
||||||
|
return loadPublicKey(new ByteArrayInputStream(publicKeyStr.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey getPrivateKeyByStr(String priKeyStr) throws Exception {
|
||||||
|
return loadPrivateKey(new ByteArrayInputStream(priKeyStr.getBytes()), (String)null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey loadPrivateKey(InputStream ins, String password) throws Exception {
|
||||||
|
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||||
|
char[] nPassword;
|
||||||
|
if (password != null && !password.trim().equals("")) {
|
||||||
|
nPassword = password.toCharArray();
|
||||||
|
} else {
|
||||||
|
nPassword = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ks.load(ins, nPassword);
|
||||||
|
Enumeration<String> enumas = ks.aliases();
|
||||||
|
String keyAlias = null;
|
||||||
|
if (enumas.hasMoreElements()) {
|
||||||
|
keyAlias = (String)enumas.nextElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (PrivateKey)ks.getKey(keyAlias, nPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static PublicKey getSandPublicKey(String sandPublicKeyPath) throws Exception {
|
||||||
|
InputStream publicIns = FileUtils.loadFile(sandPublicKeyPath);
|
||||||
|
if (Objects.isNull(publicIns)) {
|
||||||
|
throw new SandPayException("获取公钥失败");
|
||||||
|
} else {
|
||||||
|
return RSAUtils.loadPublicKey(publicIns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static PublicKey loadPublicKey(InputStream ins) throws Exception {
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
X509Certificate oCert = (X509Certificate)cf.generateCertificate(ins);
|
||||||
|
PublicKey publicKey = oCert.getPublicKey();
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
|
||||||
|
public class RandomV12Util {
|
||||||
|
|
||||||
|
public static String genRandomString(int length) {
|
||||||
|
return RandomUtil.randomString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",length);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ClassName : SandPayClient
|
||||||
|
* @Description : 加密、签名、验签
|
||||||
|
**/
|
||||||
|
@Slf4j
|
||||||
|
public class SandPayClient {
|
||||||
|
|
||||||
|
private SandpayConfigUtil sandpayConfigUtil = new SandpayConfigUtil();
|
||||||
|
|
||||||
|
public SandPayClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SandPayClient(SandPayConfig sandPayConfig) {
|
||||||
|
if (sandPayConfig != null) {
|
||||||
|
try {
|
||||||
|
this.sandpayConfigUtil.setBaseUrl(sandPayConfig.getBaseUrl());
|
||||||
|
this.sandpayConfigUtil.setAccessMid(sandPayConfig.getAccessMid());
|
||||||
|
this.sandpayConfigUtil.setMid(sandPayConfig.getMid());
|
||||||
|
this.sandpayConfigUtil.setPlMid(sandPayConfig.getPlMid());
|
||||||
|
this.sandpayConfigUtil.setVersion(sandPayConfig.getVersion());
|
||||||
|
// this.sandpayConfigUtil.setCertNo(sandPayConfig.getCertNo());//预留
|
||||||
|
String privateKeyPath = sandPayConfig.getPrivateKeyPath();
|
||||||
|
String sandPublicKeyPath = sandPayConfig.getSandPublicKeyPath();
|
||||||
|
if (!StringUtils.isBlank(privateKeyPath) || !StringUtils.isBlank(sandPublicKeyPath)) {
|
||||||
|
this.sandpayConfigUtil.setPrivateKey(this.sandpayConfigUtil.getPrivateKey(sandPayConfig.getPrivateKeyPath(), sandPayConfig.getPrivateKeyPassword()));
|
||||||
|
this.sandpayConfigUtil.setSandPublicKey(this.sandpayConfigUtil.getSandPublicKey(sandPayConfig.getSandPublicKeyPath()));
|
||||||
|
this.sandpayConfigUtil.setEncryptType(sandPayConfig.getEncryptType());
|
||||||
|
this.sandpayConfigUtil.setSignType(sandPayConfig.getSignType());
|
||||||
|
Integer connectTimeout = sandPayConfig.getConnectTimeout();
|
||||||
|
if (Objects.nonNull(connectTimeout)) {
|
||||||
|
this.sandpayConfigUtil.setConnectTimeout(connectTimeout);
|
||||||
|
}
|
||||||
|
Integer readTimeout = sandPayConfig.getReadTimeout();
|
||||||
|
if (Objects.nonNull(readTimeout)) {
|
||||||
|
this.sandpayConfigUtil.setReadTimeout(readTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception var6) {
|
||||||
|
log.error(var6.getMessage());
|
||||||
|
throw new SandPayException("初始化SandPayClient异常", var6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject execute(String url, JSONObject bizData) {
|
||||||
|
return (JSONObject) this.requestCoreReturnObject(url, bizData, JSONObject.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject execute(String url, JSONObject bizData, String encryptType) {
|
||||||
|
return (JSONObject) this.requestCoreReturnObject(url, bizData, JSONObject.class, encryptType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T requestCoreReturnObject(String url, Object bizRequest, Class<T> returnClass, String encryptType) {
|
||||||
|
String bizRequestJson = JSON.toJSONString(bizRequest);
|
||||||
|
JSONObject sandPayCommonRequest = this.buildRequest(bizRequestJson, encryptType);
|
||||||
|
log.info("请求Sand报文:{}", sandPayCommonRequest);
|
||||||
|
String responseStr = HttpClientUtils.sendPost(url, JSON.toJSONString(sandPayCommonRequest), this.sandpayConfigUtil.getConnectTimeout(), this.sandpayConfigUtil.getReadTimeout());
|
||||||
|
log.info("Sand返回报文:{}", responseStr);
|
||||||
|
return this.handleResponse(responseStr, returnClass, encryptType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T requestCoreReturnObject(String url, Object bizRequest, Class<T> returnClass) throws SandPayException {
|
||||||
|
String encryptType = this.sandpayConfigUtil.getEncryptType();
|
||||||
|
return this.requestCoreReturnObject(url, bizRequest, returnClass, encryptType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 加密、签名接口请求报文并组装公共请求报文
|
||||||
|
* @param content 接口请求报文明文
|
||||||
|
* @param encryptType 加密方式
|
||||||
|
* @return: JSONObject 公共请求报文
|
||||||
|
*/
|
||||||
|
public JSONObject buildRequest(String content, String encryptType) throws SandPayException {
|
||||||
|
String signType = "RSA";
|
||||||
|
String encryptKey;
|
||||||
|
String bizData;
|
||||||
|
String sign;
|
||||||
|
if (StringUtils.isBlank(content)) {
|
||||||
|
throw new SandPayException("签名内容为空 : content=" + content);
|
||||||
|
} else if (!"AES".equals(encryptType)) {
|
||||||
|
throw new SandPayException("不是支持的加密类型 : encryptType=" + encryptType);
|
||||||
|
} else {
|
||||||
|
//1.生成一个16位的随机字符串aesKey,该字符串仅包含大小写字母及数字。
|
||||||
|
String aesKey = RandomV12Util.genRandomString(16);
|
||||||
|
//2.将随机字符串转为byte数组aesKeyBytes,编码格式为UTF_8。
|
||||||
|
//3.将请求报文中明文bizData域转为byte数组,编码格式为UTF_8,并用aesKeyBytes用AES算法(AES/ECB/PKCS5Padding)对其加密,并对结果进行base64转码,得到加密报文体bizData。
|
||||||
|
bizData = this.aesEncrypt(content, aesKey);
|
||||||
|
//4.将随机字符串byte数组aesKeyBytes使用杉德公钥进行RSA算法(RSA/ECB/PKCS1Padding)加密,并将结果进行base64转码即得到encryptKey。
|
||||||
|
encryptKey = rsaEncrypt(aesKey, this.sandpayConfigUtil.getSandPublicKey());
|
||||||
|
//5.将加密报文体使用商户私钥进行RSA算法(SHA256WithRSA)签名,得到sign。
|
||||||
|
sign = this.generateSign(bizData, signType, this.sandpayConfigUtil.getPrivateKey());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//6.组装公共请求报文并返回
|
||||||
|
JSONObject commonRequest = new JSONObject();
|
||||||
|
commonRequest.put("accessMid", this.sandpayConfigUtil.getAccessMid());
|
||||||
|
commonRequest.put("encryptType", encryptType);
|
||||||
|
commonRequest.put("encryptKey", encryptKey);
|
||||||
|
// commonRequest.put("certNo", this.sandpayConfigUtil.getCertNo());//预留
|
||||||
|
commonRequest.put("version", this.sandpayConfigUtil.getVersion());
|
||||||
|
commonRequest.put("timestamp", this.getTimestamp());
|
||||||
|
commonRequest.put("signType", signType);
|
||||||
|
commonRequest.put("sign", sign);
|
||||||
|
commonRequest.put("bizData", bizData);
|
||||||
|
return commonRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
//解析响应报文
|
||||||
|
public JSONObject parseResponse(String content, String encryptType) throws SandPayException {
|
||||||
|
return (JSONObject) this.handleResponse(content, JSONObject.class, encryptType);
|
||||||
|
}
|
||||||
|
|
||||||
|
//验签回调
|
||||||
|
public boolean verifySign(String data, String sign, String signType, String charset) throws SandPayException {
|
||||||
|
try {
|
||||||
|
return RSAUtils.verifySign(data, sign, signType, this.sandpayConfigUtil.getSandPublicKey(), charset);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
throw new SandPayException("验证回调签名异常", var6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理响应报文
|
||||||
|
private <T> T handleResponse(String responseStr, Class<T> returnClass, String encryptType) throws SandPayException {
|
||||||
|
JSONObject commonResponse = (JSONObject) JSON.parseObject(responseStr, JSONObject.class);
|
||||||
|
String respCode = (String) commonResponse.get("respCode");
|
||||||
|
if (respCode == null) {
|
||||||
|
throw new SandPayException("处理返回报文失败,原始报文" + responseStr);
|
||||||
|
} else {
|
||||||
|
String bizData;
|
||||||
|
if (!"success".equals(respCode)) {
|
||||||
|
bizData = (String) commonResponse.get("respDesc");
|
||||||
|
throw new SandPayException(respCode, bizData, responseStr);
|
||||||
|
} else {
|
||||||
|
bizData = commonResponse.get("bizData").toString();
|
||||||
|
String sign = (String) commonResponse.get("sign");
|
||||||
|
String signType = (String) commonResponse.get("signType");
|
||||||
|
this.verifySign(bizData, sign, signType, this.sandpayConfigUtil.getSandPublicKey());
|
||||||
|
if (!"AES".equals(encryptType)) {
|
||||||
|
throw new SandPayException("不是支持的加密类型: encryptType=" + encryptType);
|
||||||
|
} else {
|
||||||
|
String encryptKey = (String) commonResponse.get("encryptKey");
|
||||||
|
String respAesKey = this.rsaDecrypt(encryptKey, this.sandpayConfigUtil.getPrivateKey());
|
||||||
|
bizData = this.aesDecrypt(bizData, respAesKey);
|
||||||
|
|
||||||
|
if (returnClass == String.class) {
|
||||||
|
return (T) bizData;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return JSON.parseObject(bizData, returnClass);
|
||||||
|
} catch (Exception var11) {
|
||||||
|
throw new SandPayException("解析报文失败," + responseStr, var11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//AES加密
|
||||||
|
private String aesEncrypt(String content, String aesKey) {
|
||||||
|
byte[] aesKeyBytes = aesKey.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] encryptValueBytes = AESUtils.encrypt(content.getBytes(StandardCharsets.UTF_8), aesKeyBytes, (String) null);
|
||||||
|
return Base64.getEncoder().encodeToString(encryptValueBytes);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
throw new SandPayException("AES加密异常", var5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//RSA加密
|
||||||
|
public static String rsaEncrypt(String content, PublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
byte[] aesKeyBytes = content.getBytes();
|
||||||
|
byte[] encryptKeyBytes = RSAUtils.encrypt(aesKeyBytes, publicKey);
|
||||||
|
return Base64.getEncoder().encodeToString(encryptKeyBytes);
|
||||||
|
} catch (Exception var4) {
|
||||||
|
throw new SandPayException("RSA加密异常", var4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//RSA解密响应参数
|
||||||
|
private String rsaDecrypt(String content, PrivateKey privateKey) {
|
||||||
|
byte[] decryptKeyBytes = Base64.getDecoder().decode(content);
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] contentBytes = RSAUtils.decrypt(decryptKeyBytes, privateKey);
|
||||||
|
String decryptKey = new String(contentBytes, StandardCharsets.UTF_8);
|
||||||
|
return decryptKey;
|
||||||
|
} catch (Exception var6) {
|
||||||
|
throw new SandPayException("RSA解密随机加密串失败", var6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//AES解密响应参数
|
||||||
|
private String aesDecrypt(String content, String aesKey) {
|
||||||
|
byte[] decryptDataBase64 = Base64.getDecoder().decode(content);
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] decryptDataBytes = AESUtils.decrypt(decryptDataBase64, aesKey.getBytes(StandardCharsets.UTF_8), (String) null);
|
||||||
|
String decryptData = new String(decryptDataBytes);
|
||||||
|
return decryptData;
|
||||||
|
} catch (Exception var6) {
|
||||||
|
throw new SandPayException("AES解密返回参数失败", var6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//签名
|
||||||
|
public String generateSign(String content, String signType, PrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
if ("RSA".equals(signType)) {
|
||||||
|
signType = "SHA256WithRSA";
|
||||||
|
}
|
||||||
|
|
||||||
|
return RSAUtils.generateSign(content, privateKey, signType, "UTF-8");
|
||||||
|
} catch (Exception var5) {
|
||||||
|
throw new SandPayException("请求签名异常", var5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 验签响应报文
|
||||||
|
* @param content 响应报文中的bizData
|
||||||
|
* @param sign 响应报文中的sign
|
||||||
|
* @param signType 响应报文中的signType
|
||||||
|
* @param publicKey 杉德公钥
|
||||||
|
* @return: boolean 验签结果
|
||||||
|
*/
|
||||||
|
private boolean verifySign(String content, String sign, String signType, PublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
if ("RSA".equals(signType)) {
|
||||||
|
signType = "SHA256WithRSA";
|
||||||
|
}
|
||||||
|
return RSAUtils.verifySign(content, sign, signType, publicKey, "UTF-8");
|
||||||
|
} catch (Exception var6) {
|
||||||
|
throw new SandPayException("验证响应报文签名异常", var6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//生成时间戳 yyyy-MM-dd HH:mm:ss
|
||||||
|
private String getTimestamp() {
|
||||||
|
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
||||||
|
return df.format(new Date(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//请求时间 yyyyMMddHHmmss
|
||||||
|
public String getCurrentTime() {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||||
|
sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
||||||
|
return sdf.format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取当前时间*小时后的时间 yyyyMMddHHmmss
|
||||||
|
public String getTimeOutTime(int hour) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.add(Calendar.HOUR, hour);
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||||
|
sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
||||||
|
return sdf.format(calendar.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
//生成订单号
|
||||||
|
public String getOutOrderNo() {
|
||||||
|
return this.getOutOrderNo("", 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOutOrderNo(String prefix, int length) {
|
||||||
|
if (prefix == null) {
|
||||||
|
prefix = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < 25) {
|
||||||
|
length = 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmsss");
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
||||||
|
String currentTime = df.format(new Date(System.currentTimeMillis()));
|
||||||
|
String outOrderNo = prefix.concat(currentTime);
|
||||||
|
int randomLen = length - outOrderNo.length();
|
||||||
|
outOrderNo = prefix.concat(currentTime).concat(RandomV12Util.genRandomString(randomLen));
|
||||||
|
return outOrderNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SandpayConfigUtil getSandpayConfigUtil() {
|
||||||
|
return this.sandpayConfigUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SandPayConfig {
|
||||||
|
private String baseUrl;
|
||||||
|
private String version = "4.0.0";
|
||||||
|
private String accessMid;
|
||||||
|
private String plMid;
|
||||||
|
private String mid;
|
||||||
|
private PrivateKey privateKey;
|
||||||
|
private String privateKeyPassword;
|
||||||
|
private String privateKeyPath;
|
||||||
|
private String certNo;
|
||||||
|
private PublicKey sandPublicKey;
|
||||||
|
private String sandPublicKeyPath;
|
||||||
|
private String signType = "RSA";
|
||||||
|
private String encryptType = "AES";
|
||||||
|
private int connectTimeout = 5000;
|
||||||
|
private int readTimeout = 5000;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
|
|
||||||
|
public class SandPayException extends RuntimeException {
|
||||||
|
private String respCode;
|
||||||
|
private String respDesc;
|
||||||
|
private String responseMsg;
|
||||||
|
|
||||||
|
public SandPayException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SandPayException(String message) {
|
||||||
|
super(message);
|
||||||
|
this.respDesc = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SandPayException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.respDesc = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SandPayException(String respCode, String respDesc, String responseMsg) {
|
||||||
|
super(respCode + ":" + respDesc);
|
||||||
|
this.respCode = respCode;
|
||||||
|
this.respDesc = respDesc;
|
||||||
|
this.responseMsg = responseMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return this.toJSON().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject toJSON() {
|
||||||
|
JSONObject sandResp = new JSONObject();
|
||||||
|
sandResp.put("respCode", this.getRespCode());
|
||||||
|
sandResp.put("respDesc", this.getRespDesc());
|
||||||
|
if (this.getResponseMsg() != null && this.getResponseMsg().length() > 0) {
|
||||||
|
JSONObject responseMsg = (JSONObject) JSON.parseObject(this.getResponseMsg(), JSONObject.class);
|
||||||
|
sandResp.put("responseMsg", responseMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sandResp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRespCode() {
|
||||||
|
return this.respCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRespDesc() {
|
||||||
|
return this.respDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResponseMsg() {
|
||||||
|
return this.responseMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
package com.ruoyi.cai.trdpay.handle.v12;
|
||||||
|
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class SandpayConfigUtil {
|
||||||
|
public static final Logger logger = LoggerFactory.getLogger(SandpayConfigUtil.class);
|
||||||
|
private String baseUrl;
|
||||||
|
private String version;
|
||||||
|
private String accessMid;
|
||||||
|
private String plMid;
|
||||||
|
private String mid;
|
||||||
|
private PrivateKey privateKey;
|
||||||
|
private String certNo;
|
||||||
|
private PublicKey sandPublicKey;
|
||||||
|
private String signType = "SHA256WithRSA";
|
||||||
|
private String encryptType = "AES";
|
||||||
|
private int connectTimeout = 10000;
|
||||||
|
private int readTimeout = 10000;
|
||||||
|
public static final String CHARSET = "UTF-8";
|
||||||
|
public static final String AES_STR = "AES";
|
||||||
|
public static final String RSA_STR = "RSA";
|
||||||
|
public static final String SHA_256_WITH_RSA = "SHA256WithRSA";
|
||||||
|
public static final String SUCCESS_STR = "success";
|
||||||
|
public static final String RESP_CODE_STR = "respCode";
|
||||||
|
public static final String RESP_DESC_STR = "respDesc";
|
||||||
|
public static final String BIZ_DATA_STR = "bizData";
|
||||||
|
public static final String SIGN_STR = "sign";
|
||||||
|
public static final String SIGN_TYPE_STR = "signType";
|
||||||
|
public static final String ENCRYPT_TYPE_STR = "encryptType";
|
||||||
|
public static final String ENCRYPT_KEY_STR = "encryptKey";
|
||||||
|
|
||||||
|
public SandpayConfigUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrivateKey getPrivateKey(String privateKeyPath, String privateKeyPassword) throws Exception {
|
||||||
|
logger.debug("商户私钥路径 => "+privateKeyPath);
|
||||||
|
logger.debug("商户私钥密码 => "+privateKeyPassword);
|
||||||
|
InputStream privateIns = FileUtils.loadFile(privateKeyPath);
|
||||||
|
if (Objects.isNull(privateIns)) {
|
||||||
|
throw new SandPayException("获取私钥失败");
|
||||||
|
} else {
|
||||||
|
return RSAUtils.loadPrivateKey(privateIns, privateKeyPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublicKey getSandPublicKey(String sandPublicKeyPath) throws Exception {
|
||||||
|
logger.debug("杉德公钥路径 => "+ sandPublicKeyPath);
|
||||||
|
InputStream publicIns = FileUtils.loadFile(sandPublicKeyPath);
|
||||||
|
if (Objects.isNull(publicIns)) {
|
||||||
|
throw new SandPayException("获取公钥失败");
|
||||||
|
} else {
|
||||||
|
return RSAUtils.loadPublicKey(publicIns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return this.baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBaseUrl(String baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessMid() {
|
||||||
|
return this.accessMid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessMid(String accessMid) {
|
||||||
|
this.accessMid = accessMid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlMid() {
|
||||||
|
return this.plMid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlMid(String plMid) {
|
||||||
|
this.plMid = plMid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMid() {
|
||||||
|
return this.mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMid(String mid) {
|
||||||
|
this.mid = mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrivateKey getPrivateKey() {
|
||||||
|
return this.privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(PrivateKey privateKey) {
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCertNo() {
|
||||||
|
return this.certNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCertNo(String certNo) {
|
||||||
|
this.certNo = certNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublicKey getSandPublicKey() {
|
||||||
|
return this.sandPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSandPublicKey(PublicKey sandPublicKey) {
|
||||||
|
this.sandPublicKey = sandPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignType() {
|
||||||
|
return this.signType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignType(String signType) {
|
||||||
|
this.signType = signType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncryptType() {
|
||||||
|
return this.encryptType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncryptType(String encryptType) {
|
||||||
|
this.encryptType = encryptType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectTimeout() {
|
||||||
|
return this.connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnectTimeout(int connectTimeout) {
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReadTimeout() {
|
||||||
|
return this.readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadTimeout(int readTimeout) {
|
||||||
|
this.readTimeout = readTimeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user