拉卡拉 入网电子合同申请 相关业务逻辑,修改加密方法
This commit is contained in:
parent
40beddaaa5
commit
8eb9cf3ce6
@ -41,6 +41,7 @@ public class LklLedgerEc implements Serializable {
|
|||||||
private String ec_status;
|
private String ec_status;
|
||||||
private Long ec_apply_id;
|
private Long ec_apply_id;
|
||||||
private String result_url;
|
private String result_url;
|
||||||
|
private String notify_url;
|
||||||
private Integer status;
|
private Integer status;
|
||||||
private Date created_at;
|
private Date created_at;
|
||||||
private Date updated_at;
|
private Date updated_at;
|
||||||
|
|||||||
@ -14,6 +14,9 @@ import org.springframework.stereotype.Component;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结巴分词工具类
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class JiebaUtils {
|
public class JiebaUtils {
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||||
|
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||||
|
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||||
|
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.suisung.mall.common.utils;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class RSAUtil {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用私钥对数据进行 SHA256withRSA 签名
|
||||||
|
*
|
||||||
|
* @param data 待签名的数据
|
||||||
|
* @param privateKey 私钥(Base64编码)
|
||||||
|
* @return 签名结果(Base64编码)
|
||||||
|
*/
|
||||||
|
public static String signSHA256withRSA(String data, String privateKey) {
|
||||||
|
try {
|
||||||
|
// 解码私钥
|
||||||
|
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
|
||||||
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||||
|
|
||||||
|
// 生成私钥对象
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
PrivateKey priKey = keyFactory.generatePrivate(keySpec);
|
||||||
|
|
||||||
|
// 初始化签名
|
||||||
|
Signature signature = Signature.getInstance("SHA256withRSA");
|
||||||
|
signature.initSign(priKey);
|
||||||
|
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
// 生成签名并编码为Base64
|
||||||
|
byte[] signBytes = signature.sign();
|
||||||
|
return Base64.getEncoder().encodeToString(signBytes);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
logger.error("不支持的加密算法: {}", e.getMessage(), e);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
logger.error("私钥格式错误: {}", e.getMessage(), e);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
logger.error("无效的私钥: {}", e.getMessage(), e);
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
logger.error("签名处理异常: {}", e.getMessage(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("签名过程发生未知异常: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用公钥验证 SHA256withRSA 签名
|
||||||
|
*
|
||||||
|
* @param data 原始数据
|
||||||
|
* @param sign 签名结果(Base64编码)
|
||||||
|
* @param publicKey 公钥(Base64编码)
|
||||||
|
* @return 验证结果
|
||||||
|
*/
|
||||||
|
public static boolean verifySHA256withRSA(String data, String sign, String publicKey) {
|
||||||
|
try {
|
||||||
|
// 解码公钥
|
||||||
|
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
|
||||||
|
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
||||||
|
|
||||||
|
// 生成公钥对象
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
PublicKey pubKey = keyFactory.generatePublic(keySpec);
|
||||||
|
|
||||||
|
// 初始化签名验证
|
||||||
|
Signature signature = Signature.getInstance("SHA256withRSA");
|
||||||
|
signature.initVerify(pubKey);
|
||||||
|
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
byte[] signBytes = Base64.getDecoder().decode(sign);
|
||||||
|
return signature.verify(signBytes);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
logger.error("不支持的加密算法: {}", e.getMessage(), e);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
logger.error("公钥格式错误: {}", e.getMessage(), e);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
logger.error("无效的公钥: {}", e.getMessage(), e);
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
logger.error("签名验证异常: {}", e.getMessage(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("验证过程发生未知异常: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 RSA 密钥对
|
||||||
|
*
|
||||||
|
* @param keySize 密钥长度,推荐 2048
|
||||||
|
* @return 密钥对
|
||||||
|
*/
|
||||||
|
public static KeyPair generateKeyPair(int keySize) {
|
||||||
|
KeyPairGenerator keyPairGen = null;
|
||||||
|
try {
|
||||||
|
keyPairGen = KeyPairGenerator.getInstance("RSA");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
keyPairGen.initialize(keySize);
|
||||||
|
return keyPairGen.generateKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取公钥的 Base64 编码字符串
|
||||||
|
*/
|
||||||
|
public static String getPublicKeyString(KeyPair keyPair) {
|
||||||
|
PublicKey publicKey = keyPair.getPublic();
|
||||||
|
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取私钥的 Base64 编码字符串
|
||||||
|
*/
|
||||||
|
public static String getPrivateKeyString(KeyPair keyPair) {
|
||||||
|
PrivateKey privateKey = keyPair.getPrivate();
|
||||||
|
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例用法
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
// 生成密钥对
|
||||||
|
KeyPair keyPair = generateKeyPair(2048);
|
||||||
|
String publicKey = getPublicKeyString(keyPair);
|
||||||
|
String privateKey = getPrivateKeyString(keyPair);
|
||||||
|
|
||||||
|
// 待签名数据
|
||||||
|
String data = "Hello, RSA Signature!";
|
||||||
|
|
||||||
|
// 签名
|
||||||
|
String signature = signSHA256withRSA(data, privateKey);
|
||||||
|
System.out.println("签名结果: " + signature);
|
||||||
|
|
||||||
|
// 验证
|
||||||
|
boolean isValid = verifySHA256withRSA(data, signature, publicKey);
|
||||||
|
System.out.println("验证结果: " + isValid);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,6 @@ package com.suisung.mall.pay.utils;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.lkl.laop.sdk.Config2;
|
import com.lkl.laop.sdk.Config2;
|
||||||
import com.lkl.laop.sdk.LKLSDK;
|
import com.lkl.laop.sdk.LKLSDK;
|
||||||
import com.lkl.laop.sdk.auth.PrivateKeySigner;
|
|
||||||
import com.suisung.mall.common.exception.ApiException;
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import com.suisung.mall.common.utils.I18nUtil;
|
import com.suisung.mall.common.utils.I18nUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -76,6 +75,7 @@ public class LakalaUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 body 请求数据
|
* 获取 body 请求数据
|
||||||
*
|
*
|
||||||
@ -130,7 +130,6 @@ public class LakalaUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名验证
|
* 签名验证
|
||||||
*
|
*
|
||||||
@ -155,9 +154,9 @@ public class LakalaUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名校验
|
* 签名校验
|
||||||
|
*
|
||||||
* @param authorization
|
* @param authorization
|
||||||
* @param reqBody
|
* @param reqBody
|
||||||
* @param lklNotifyCerPath
|
* @param lklNotifyCerPath
|
||||||
@ -176,11 +175,9 @@ public class LakalaUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder preSignData = new StringBuilder();
|
String preSignDataStr = timestamp + "\n" +
|
||||||
preSignData.append(timestamp).append("\n")
|
nonceStr + "\n" +
|
||||||
.append(nonceStr).append("\n")
|
reqBody + "\n";
|
||||||
.append(reqBody).append("\n");
|
|
||||||
String preSignDataStr = preSignData.toString();
|
|
||||||
log.debug("拉卡拉签名明文内容:{}", preSignDataStr);
|
log.debug("拉卡拉签名明文内容:{}", preSignDataStr);
|
||||||
if (verify(lklNotifyCer, preSignDataStr.getBytes(StandardCharsets.UTF_8), signature)) {
|
if (verify(lklNotifyCer, preSignDataStr.getBytes(StandardCharsets.UTF_8), signature)) {
|
||||||
log.debug("验签成功");
|
log.debug("验签成功");
|
||||||
@ -237,4 +234,5 @@ public class LakalaUtil {
|
|||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -211,9 +211,14 @@ public class EsignContractServiceImpl extends BaseServiceImpl<EsignContractMappe
|
|||||||
|
|
||||||
//发起接口请求
|
//发起接口请求
|
||||||
EsignHttpResponse createByDocTemplate = EsignHttpHelper.doCommHttp(serverUrl, apiAddr, requestType, jsonParams, header, debug);
|
EsignHttpResponse createByDocTemplate = EsignHttpHelper.doCommHttp(serverUrl, apiAddr, requestType, jsonParams, header, debug);
|
||||||
if (createByDocTemplate.getStatus() != 200 || createByDocTemplate.getBody() == null) {
|
log.debug("发起合同签署流程返回消息:{},{}", createByDocTemplate.getStatus(), createByDocTemplate.getBody());
|
||||||
log.error("e签宝请求失败,返回状态码:{}", createByDocTemplate.getStatus());
|
if (createByDocTemplate.getStatus() != 200) {
|
||||||
return Pair.of(false, "e签宝请求失败,状态码:" + createByDocTemplate.getStatus());
|
if (createByDocTemplate.getBody() != null) {
|
||||||
|
JSONObject resBody = JSONUtil.parseObj(createByDocTemplate.getBody());
|
||||||
|
log.error("e签宝请求失败,返回状态码:{}, {}", createByDocTemplate.getStatus(), resBody.getStr("message"));
|
||||||
|
return Pair.of(false, "e签宝请求失败,{}" + resBody.getStr("message"));
|
||||||
|
}
|
||||||
|
return Pair.of(false, "e签宝请求失败!");
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONObject jsonObject = JSONUtil.parseObj(createByDocTemplate.getBody());
|
JSONObject jsonObject = JSONUtil.parseObj(createByDocTemplate.getBody());
|
||||||
|
|||||||
@ -30,6 +30,12 @@ public class LakalaController extends BaseControllerImpl {
|
|||||||
@Resource
|
@Resource
|
||||||
private LakalaApiService lakalaPayService;
|
private LakalaApiService lakalaPayService;
|
||||||
|
|
||||||
|
@ApiOperation(value = "测试案例", notes = "测试案例")
|
||||||
|
@RequestMapping(value = "/testcase", method = RequestMethod.POST)
|
||||||
|
public Object testcase(@RequestBody JSONObject paramsJSON) {
|
||||||
|
return lakalaPayService.applyLedgerMerEc(paramsJSON.getStr("mchMobile"));
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation(value = "本地文件转base64", notes = "本地文件转base64")
|
@ApiOperation(value = "本地文件转base64", notes = "本地文件转base64")
|
||||||
@RequestMapping(value = "/file2base64", method = RequestMethod.POST)
|
@RequestMapping(value = "/file2base64", method = RequestMethod.POST)
|
||||||
public String file2Base64(@RequestParam("file") MultipartFile file) throws IOException {
|
public String file2Base64(@RequestParam("file") MultipartFile file) throws IOException {
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import com.suisung.mall.common.modules.lakala.LklBanks;
|
|||||||
import com.suisung.mall.common.service.impl.BaseControllerImpl;
|
import com.suisung.mall.common.service.impl.BaseControllerImpl;
|
||||||
import com.suisung.mall.shop.lakala.service.LklBanksService;
|
import com.suisung.mall.shop.lakala.service.LklBanksService;
|
||||||
import com.suisung.mall.shop.lakala.service.impl.LklTkServiceImpl;
|
import com.suisung.mall.shop.lakala.service.impl.LklTkServiceImpl;
|
||||||
|
import com.suisung.mall.shop.lakala.utils.LakalaUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.data.util.Pair;
|
import org.springframework.data.util.Pair;
|
||||||
@ -42,7 +43,7 @@ public class LklTkController extends BaseControllerImpl {
|
|||||||
public String decryptLklTkData(@RequestParam(name = "data") String data,
|
public String decryptLklTkData(@RequestParam(name = "data") String data,
|
||||||
@RequestParam(name = "key") String key) {
|
@RequestParam(name = "key") String key) {
|
||||||
|
|
||||||
return lklTkService.decryptNotifyData(key, data);
|
return LakalaUtil.decryptNotifyData(key, data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,20 +23,16 @@ import com.suisung.mall.common.api.CommonResult;
|
|||||||
import com.suisung.mall.common.constant.CommonConstant;
|
import com.suisung.mall.common.constant.CommonConstant;
|
||||||
import com.suisung.mall.common.exception.ApiException;
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import com.suisung.mall.common.feignService.ShopService;
|
import com.suisung.mall.common.feignService.ShopService;
|
||||||
|
import com.suisung.mall.common.modules.lakala.LklLedgerEc;
|
||||||
import com.suisung.mall.common.modules.lakala.LklLedgerMember;
|
import com.suisung.mall.common.modules.lakala.LklLedgerMember;
|
||||||
import com.suisung.mall.common.modules.lakala.LklLedgerMerReceiverBind;
|
import com.suisung.mall.common.modules.lakala.LklLedgerMerReceiverBind;
|
||||||
import com.suisung.mall.common.modules.lakala.LklLedgerReceiver;
|
import com.suisung.mall.common.modules.lakala.LklLedgerReceiver;
|
||||||
import com.suisung.mall.common.modules.store.ShopMchEntry;
|
import com.suisung.mall.common.modules.store.ShopMchEntry;
|
||||||
import com.suisung.mall.common.modules.store.ShopStoreBase;
|
import com.suisung.mall.common.modules.store.ShopStoreBase;
|
||||||
import com.suisung.mall.common.utils.I18nUtil;
|
import com.suisung.mall.common.utils.*;
|
||||||
import com.suisung.mall.common.utils.StringUtils;
|
|
||||||
import com.suisung.mall.common.utils.UploadUtil;
|
|
||||||
import com.suisung.mall.shop.esign.service.EsignContractFillingFileService;
|
import com.suisung.mall.shop.esign.service.EsignContractFillingFileService;
|
||||||
import com.suisung.mall.shop.esign.service.EsignContractService;
|
import com.suisung.mall.shop.esign.service.EsignContractService;
|
||||||
import com.suisung.mall.shop.lakala.service.LakalaApiService;
|
import com.suisung.mall.shop.lakala.service.*;
|
||||||
import com.suisung.mall.shop.lakala.service.LklLedgerMemberService;
|
|
||||||
import com.suisung.mall.shop.lakala.service.LklLedgerMerReceiverBindService;
|
|
||||||
import com.suisung.mall.shop.lakala.service.LklLedgerReceiverService;
|
|
||||||
import com.suisung.mall.shop.lakala.utils.LakalaUtil;
|
import com.suisung.mall.shop.lakala.utils.LakalaUtil;
|
||||||
import com.suisung.mall.shop.store.service.ShopMchEntryService;
|
import com.suisung.mall.shop.store.service.ShopMchEntryService;
|
||||||
import com.suisung.mall.shop.store.service.ShopStoreBaseService;
|
import com.suisung.mall.shop.store.service.ShopStoreBaseService;
|
||||||
@ -45,6 +41,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.data.util.Pair;
|
import org.springframework.data.util.Pair;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
@ -53,6 +51,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -119,6 +118,10 @@ public class LakalaApiServiceImpl implements LakalaApiService {
|
|||||||
@Resource
|
@Resource
|
||||||
private ShopStoreBaseService shopStoreBaseService;
|
private ShopStoreBaseService shopStoreBaseService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private LklLedgerEcService lklLedgerEcService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化 拉卡拉SDK
|
* 初始化 拉卡拉SDK
|
||||||
*
|
*
|
||||||
@ -493,6 +496,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
|
|||||||
reqData.put("acct_type_code", shopMchEntry.getAccount_type());//57 对公、 58 对私
|
reqData.put("acct_type_code", shopMchEntry.getAccount_type());//57 对公、 58 对私
|
||||||
reqData.put("acct_no", shopMchEntry.getAccount_number());
|
reqData.put("acct_no", shopMchEntry.getAccount_number());
|
||||||
reqData.put("acct_name", shopMchEntry.getAccount_holder_name());
|
reqData.put("acct_name", shopMchEntry.getAccount_holder_name());
|
||||||
|
reqData.put("remark", "申请入网电子合同");
|
||||||
|
|
||||||
String domain = projectDomain;
|
String domain = projectDomain;
|
||||||
if (isProd()) {
|
if (isProd()) {
|
||||||
@ -538,16 +542,66 @@ public class LakalaApiServiceImpl implements LakalaApiService {
|
|||||||
ecParams.put("D11", signDate);
|
ecParams.put("D11", signDate);
|
||||||
ecParams.put("D12", signDate);
|
ecParams.put("D12", signDate);
|
||||||
ecParams.put("E1", "桂平发发网络有限公司");
|
ecParams.put("E1", "桂平发发网络有限公司");
|
||||||
ecParams.put("E2", "平台商户入驻合同协议");
|
ecParams.put("E2", "商户入驻小发同城平台合同协议");
|
||||||
ecParams.put("E3", "0");
|
ecParams.put("E3", "0");
|
||||||
ecParams.put("E4", "70");
|
ecParams.put("E4", "70");
|
||||||
ecParams.put("E5", "桂平发发网络有限公司");
|
ecParams.put("E5", "桂平发发网络有限公司");
|
||||||
ecParams.put("E7", signDate);
|
ecParams.put("E7", signDate);
|
||||||
ecParams.put("E8", shopMchEntry.getAccount_holder_name());
|
ecParams.put("E8", shopMchEntry.getAccount_holder_name());
|
||||||
|
|
||||||
reqData.put("ec_content_parameters", ecParams);
|
|
||||||
|
|
||||||
return null;
|
// 注:该字段是字符串,不是json
|
||||||
|
reqData.put("ec_content_parameters", ecParams.toString());
|
||||||
|
|
||||||
|
JSONObject reqBody = new JSONObject();
|
||||||
|
reqBody.put("req_time", DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyyMMddHHmmss"));
|
||||||
|
reqBody.put("version", "3.0");
|
||||||
|
reqBody.put("req_data", reqData);
|
||||||
|
|
||||||
|
|
||||||
|
String reqUrl = serverUrl + "/sit/api/v3/mms/open_api/ec/apply";
|
||||||
|
if (isProd()) {
|
||||||
|
reqUrl = serverUrl + "/api/v3/mms/open_api/ec/apply";
|
||||||
|
}
|
||||||
|
|
||||||
|
String privateKey = LakalaUtil.getResourceFile(priKeyPath, false, true);
|
||||||
|
String authorization = LakalaUtil.genAuthorization(privateKey, appId, serialNo, reqBody.toString());
|
||||||
|
|
||||||
|
JSONObject header = new JSONObject();
|
||||||
|
header.put("Authorization", authorization);
|
||||||
|
|
||||||
|
ResponseEntity<JSONObject> response = RestTemplateHttpUtil.sendPostBodyBackEntity(reqUrl, header, reqBody, JSONObject.class);
|
||||||
|
if (ObjectUtil.isEmpty(response) || response.getStatusCode() != HttpStatus.OK) {
|
||||||
|
return Pair.of(false, "商家入网申请电子合同:请求失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject respBody = response.getBody();
|
||||||
|
if (ObjectUtil.isNotEmpty(respBody) && !lklSuccessCode.equals(respBody.getStr("code"))) {
|
||||||
|
String errMsg = StrUtil.isBlank(respBody.getStr("msg")) ? "返回状态有误" : respBody.getStr("msg");
|
||||||
|
return Pair.of(false, "商家入网申请电子合同失败:" + errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject respData = respBody.getJSONObject("resp_data");
|
||||||
|
if (respBody.getJSONObject("resp_data") == null) {
|
||||||
|
return Pair.of(false, "商家入网申请电子合同失败:返回数据有误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商家入网申请电子合同处理数据
|
||||||
|
// 先写入本地数据库表中
|
||||||
|
LklLedgerEc record = new LklLedgerEc();
|
||||||
|
record.setMch_id(shopMchEntry.getId());
|
||||||
|
record.setMch_mobile(shopMchEntry.getLogin_mobile());
|
||||||
|
record.setReq_params(reqBody.toString());
|
||||||
|
record.setNotify_url(retUrl);
|
||||||
|
record.setEc_apply_id(respData.getLong("ec_apply_id"));
|
||||||
|
record.setResult_url(respData.getStr("result_url"));
|
||||||
|
record.setResp_body(respBody.toString());
|
||||||
|
Boolean success = lklLedgerEcService.saveOrUpdateByMchId(record);
|
||||||
|
if (!success) {
|
||||||
|
return Pair.of(false, "商家入网申请电子合同失败:本地数据保存失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Pair.of(true, "商家入网申请电子合同成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import com.suisung.mall.shop.lakala.mapper.LklLedgerReceiverMapper;
|
|||||||
import com.suisung.mall.shop.lakala.service.LakalaApiService;
|
import com.suisung.mall.shop.lakala.service.LakalaApiService;
|
||||||
import com.suisung.mall.shop.lakala.service.LklLedgerMemberService;
|
import com.suisung.mall.shop.lakala.service.LklLedgerMemberService;
|
||||||
import com.suisung.mall.shop.lakala.service.LklLedgerReceiverService;
|
import com.suisung.mall.shop.lakala.service.LklLedgerReceiverService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ import javax.annotation.Resource;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class LklLedgerReceiverServiceImpl extends BaseServiceImpl<LklLedgerReceiverMapper, LklLedgerReceiver> implements LklLedgerReceiverService {
|
public class LklLedgerReceiverServiceImpl extends BaseServiceImpl<LklLedgerReceiverMapper, LklLedgerReceiver> implements LklLedgerReceiverService {
|
||||||
|
|
||||||
@ -207,13 +209,16 @@ public class LklLedgerReceiverServiceImpl extends BaseServiceImpl<LklLedgerRecei
|
|||||||
public Boolean innerApplyLedgerReceiver(String merCupNo, Long platformId) {
|
public Boolean innerApplyLedgerReceiver(String merCupNo, Long platformId) {
|
||||||
JSONArray buildApplyLedgerReceiverReqParams = buildApplyLedgerReceiverReqParams(platformId);
|
JSONArray buildApplyLedgerReceiverReqParams = buildApplyLedgerReceiverReqParams(platformId);
|
||||||
if (CollectionUtil.isEmpty(buildApplyLedgerReceiverReqParams)) {
|
if (CollectionUtil.isEmpty(buildApplyLedgerReceiverReqParams)) {
|
||||||
|
log.error("没有平台信息或代理商信息");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int success = 0;
|
int success = 0;
|
||||||
for (JSONObject reqParam : buildApplyLedgerReceiverReqParams.jsonIter()) {
|
for (JSONObject reqParam : buildApplyLedgerReceiverReqParams.jsonIter()) {
|
||||||
|
log.debug("申请分账接收方参数:{}", reqParam.toString());
|
||||||
CommonResult result = lakalaApiService.applyLedgerReceiver(reqParam);
|
CommonResult result = lakalaApiService.applyLedgerReceiver(reqParam);
|
||||||
if (result == null || result.getCode() != 200) {
|
if (result == null || result.getCode() != 200) {
|
||||||
|
log.error("申请分账接收方出错:{}", result.getMsg());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,18 +41,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.*;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -104,108 +93,6 @@ public class LklTkServiceImpl {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RedisService redisService;
|
private RedisService redisService;
|
||||||
|
|
||||||
/**
|
|
||||||
* 拉卡拉使用私钥加密数据
|
|
||||||
*/
|
|
||||||
public static String encryptNotifyData(String priKey, String data) {
|
|
||||||
ByteArrayOutputStream out = null;
|
|
||||||
try {
|
|
||||||
logger.debug("拉卡拉tk加密私钥:{}", priKey);
|
|
||||||
logger.debug("拉卡拉tk待加密data数据:{}", data);
|
|
||||||
|
|
||||||
// 解码私钥
|
|
||||||
byte[] keyBytes = Base64.getDecoder().decode(priKey);
|
|
||||||
|
|
||||||
// 生成私钥对象
|
|
||||||
PrivateKey privateKey = KeyFactory.getInstance("RSA")
|
|
||||||
.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
|
|
||||||
|
|
||||||
// 初始化加密器
|
|
||||||
Cipher cipher = Cipher.getInstance("RSA");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
|
||||||
|
|
||||||
// 分段加密
|
|
||||||
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
|
|
||||||
out = new ByteArrayOutputStream();
|
|
||||||
for (int offset = 0; offset < dataBytes.length; offset += 117) {
|
|
||||||
int blockSize = Math.min(117, dataBytes.length - offset);
|
|
||||||
byte[] encryptedBlock = cipher.doFinal(dataBytes, offset, blockSize);
|
|
||||||
out.write(encryptedBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编码为Base64字符串
|
|
||||||
String encryptDataStr = Base64.getEncoder().encodeToString(out.toByteArray());
|
|
||||||
logger.debug("拉卡拉tk数据加密结果:{}", encryptDataStr);
|
|
||||||
return encryptDataStr;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("私钥加密失败", e);
|
|
||||||
} finally {
|
|
||||||
closeQuietly(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 安全关闭流
|
|
||||||
*/
|
|
||||||
private static void closeQuietly(ByteArrayOutputStream out) {
|
|
||||||
if (out != null) {
|
|
||||||
try {
|
|
||||||
out.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 忽略异常
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 拉卡拉异步通知数据公钥解密
|
|
||||||
*
|
|
||||||
* @param pubKey Base64公钥
|
|
||||||
* @param data Base64数据
|
|
||||||
* @return 解密字符串
|
|
||||||
*/
|
|
||||||
public String decryptNotifyData(String pubKey, String data) {
|
|
||||||
ByteArrayOutputStream out = null;
|
|
||||||
try {
|
|
||||||
logger.debug("拉卡拉tk解密公钥:{}", pubKey);
|
|
||||||
logger.debug("拉卡拉tk待解密data数据:{}", data);
|
|
||||||
Base64.Decoder decoder = Base64.getDecoder();
|
|
||||||
byte[] keyBytes = decoder.decode(pubKey.getBytes(StandardCharsets.UTF_8));
|
|
||||||
byte[] dataBytes = decoder.decode(data.getBytes(StandardCharsets.UTF_8));
|
|
||||||
Cipher cipher = Cipher.getInstance("RSA");
|
|
||||||
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes));
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
|
||||||
out = new ByteArrayOutputStream();
|
|
||||||
byte[] cache;
|
|
||||||
for (int i = 0, offset = 0, length = dataBytes.length; length - offset > 0; i++, offset = i * 128) {
|
|
||||||
if (length - offset > 128) {
|
|
||||||
cache = cipher.doFinal(dataBytes, offset, 128);
|
|
||||||
} else {
|
|
||||||
cache = cipher.doFinal(dataBytes, offset, length - offset);
|
|
||||||
}
|
|
||||||
out.write(cache, 0, cache.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
String decodedDataStr = out.toString("UTF-8");
|
|
||||||
logger.debug("拉卡拉tk数据解密结果:{}", decodedDataStr);
|
|
||||||
|
|
||||||
return decodedDataStr;
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
logger.error("Base64解码失败: {}", e.getMessage(), e);
|
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
|
||||||
logger.error("RSA算法初始化失败: {}", e.getMessage(), e);
|
|
||||||
} catch (InvalidKeySpecException | InvalidKeyException e) {
|
|
||||||
logger.error("私钥格式或类型错误: {}", e.getMessage(), e);
|
|
||||||
} catch (BadPaddingException | IllegalBlockSizeException e) {
|
|
||||||
logger.error("解密数据块大小或填充错误: {}", e.getMessage(), e);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("解密过程中出现未知异常: {}", e.getMessage(), e);
|
|
||||||
} finally {
|
|
||||||
closeQuietly(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String buildLklTkUrl(String urlPath) {
|
protected String buildLklTkUrl(String urlPath) {
|
||||||
return tkServerUrl + urlPath;
|
return tkServerUrl + urlPath;
|
||||||
@ -610,8 +497,8 @@ public class LklTkServiceImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 公钥解密出来的数据
|
// 公钥解密出来的数据
|
||||||
String notifyPubKey = LakalaUtil.getResourceFileWithEndSeq(notifyPubKeyPath, false);
|
String notifyPubKey = LakalaUtil.getResourceFile(notifyPubKeyPath, false, false);
|
||||||
String data = decryptNotifyData(notifyPubKey, srcData);
|
String data = LakalaUtil.decryptNotifyData(notifyPubKey, srcData);
|
||||||
if (StrUtil.isBlank(data)) {
|
if (StrUtil.isBlank(data)) {
|
||||||
return new JSONObject().set("code", "500").set("message", "密文解密出错!");
|
return new JSONObject().set("code", "500").set("message", "密文解密出错!");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,23 +13,28 @@ import com.lkl.laop.sdk.Config2;
|
|||||||
import com.lkl.laop.sdk.LKLSDK;
|
import com.lkl.laop.sdk.LKLSDK;
|
||||||
import com.suisung.mall.common.exception.ApiException;
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import com.suisung.mall.common.utils.I18nUtil;
|
import com.suisung.mall.common.utils.I18nUtil;
|
||||||
|
import com.suisung.mall.common.utils.RSAUtil;
|
||||||
|
import com.suisung.mall.common.utils.StringUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.BufferedReader;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.*;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.cert.*;
|
import java.security.cert.*;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class LakalaUtil {
|
public class LakalaUtil {
|
||||||
@ -58,10 +63,17 @@ public class LakalaUtil {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String getResourceFile(String fileName) {
|
public static String getResourceFile(String fileName) {
|
||||||
return getResourceFileWithEndSeq(fileName, true);
|
return getResourceFile(fileName, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getResourceFileWithEndSeq(String fileName, boolean keepLineBreaks) {
|
/**
|
||||||
|
* 获取配置文件内容
|
||||||
|
*
|
||||||
|
* @param fileName recource 文件夹下的路径,如:palyKey/wx/lakala_public_key.cer
|
||||||
|
* @param keepLineBreaks 保留换行符
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getResourceFile(String fileName, boolean keepLineBreaks, boolean stripPemHeaders) {
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
try (InputStream inputStream = new ClassPathResource(fileName).getInputStream();
|
try (InputStream inputStream = new ClassPathResource(fileName).getInputStream();
|
||||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||||
@ -71,6 +83,9 @@ public class LakalaUtil {
|
|||||||
while ((line = bufferedReader.readLine()) != null) {
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
stringBuilder.append(line).append(endSeq);
|
stringBuilder.append(line).append(endSeq);
|
||||||
}
|
}
|
||||||
|
if (stripPemHeaders) {
|
||||||
|
return stripPemHeaders(stringBuilder.toString());
|
||||||
|
}
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// 记录异常信息
|
// 记录异常信息
|
||||||
@ -79,6 +94,76 @@ public class LakalaUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉卡拉使用私钥加密数据
|
||||||
|
*/
|
||||||
|
public static String encryptNotifyData(String priKey, String data) {
|
||||||
|
ByteArrayOutputStream out = null;
|
||||||
|
try {
|
||||||
|
log.debug("拉卡拉tk加密私钥:{}", priKey);
|
||||||
|
log.debug("拉卡拉tk待加密data数据:{}", data);
|
||||||
|
|
||||||
|
// 解码私钥
|
||||||
|
byte[] keyBytes = java.util.Base64.getDecoder().decode(priKey);
|
||||||
|
|
||||||
|
// 生成私钥对象
|
||||||
|
PrivateKey privateKey = KeyFactory.getInstance("RSA")
|
||||||
|
.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
|
||||||
|
|
||||||
|
// 初始化加密器
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
||||||
|
|
||||||
|
// 分段加密
|
||||||
|
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
|
||||||
|
out = new ByteArrayOutputStream();
|
||||||
|
for (int offset = 0; offset < dataBytes.length; offset += 117) {
|
||||||
|
int blockSize = Math.min(117, dataBytes.length - offset);
|
||||||
|
byte[] encryptedBlock = cipher.doFinal(dataBytes, offset, blockSize);
|
||||||
|
out.write(encryptedBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编码为Base64字符串
|
||||||
|
String encryptDataStr = java.util.Base64.getEncoder().encodeToString(out.toByteArray());
|
||||||
|
log.debug("拉卡拉tk数据加密结果:{}", encryptDataStr);
|
||||||
|
return encryptDataStr;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("私钥加密失败", e);
|
||||||
|
} finally {
|
||||||
|
closeQuietly(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全关闭流
|
||||||
|
*/
|
||||||
|
private static void closeQuietly(ByteArrayOutputStream out) {
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略异常
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String stripPemHeaders(String key) {
|
||||||
|
if (key == null || key.isEmpty()) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义正则表达式:匹配标签行、任意数量连字符和空白字符
|
||||||
|
// 1. 包含BEGIN或END的标签行(无论连字符数量)
|
||||||
|
// 2. 单独的连字符(不在标签行中的)
|
||||||
|
// 3. 空白字符(空格、制表符、换行等)
|
||||||
|
Pattern pattern = Pattern.compile(
|
||||||
|
"-*BEGIN[^-]*-*|-*END[^-]*-*|-|\\s");
|
||||||
|
|
||||||
|
// 执行替换
|
||||||
|
Matcher matcher = pattern.matcher(key);
|
||||||
|
return matcher.replaceAll("");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 body 请求数据
|
* 获取 body 请求数据
|
||||||
*
|
*
|
||||||
@ -133,7 +218,6 @@ public class LakalaUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名验证
|
* 签名验证
|
||||||
*
|
*
|
||||||
@ -158,7 +242,6 @@ public class LakalaUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名校验
|
* 签名校验
|
||||||
*
|
*
|
||||||
@ -239,4 +322,85 @@ public class LakalaUtil {
|
|||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接拉卡拉header中的Authorization字段
|
||||||
|
* 参考文档:https://o.lakala.com/#/home/document/detail?id=33
|
||||||
|
*
|
||||||
|
* @param privateKey
|
||||||
|
* @param appId
|
||||||
|
* @param serialNo
|
||||||
|
* @param reqBody
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String genAuthorization(String privateKey, String appId, String serialNo, String reqBody) {
|
||||||
|
if (StrUtil.isBlank(privateKey) || StrUtil.isBlank(appId) || StrUtil.isBlank(serialNo) || StrUtil.isBlank(reqBody)) {
|
||||||
|
log.error("生产拉卡拉签名时缺少参数");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//待签字符串格式:${appid}\n+${serialNo}\n+${timeStamp}\n+${nonceStr}\n+${body}\n
|
||||||
|
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||||
|
String nonceStr = StringUtils.genRandomNumber(12);
|
||||||
|
String builder = appId + "\n" +
|
||||||
|
serialNo + "\n" +
|
||||||
|
timestamp + "\n" +
|
||||||
|
nonceStr + "\n" +
|
||||||
|
reqBody + "\n";
|
||||||
|
|
||||||
|
String signature = RSAUtil.signSHA256withRSA(builder, privateKey);
|
||||||
|
|
||||||
|
return String.format("LKLAPI-SHA256withRSA appid=\"%s\",serial_no=\"%s\",timestamp=\"%s\",nonce_str=\"%s\",signature=\"%s\"",
|
||||||
|
appId, serialNo, timestamp, nonceStr, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉卡拉异步通知数据公钥解密
|
||||||
|
*
|
||||||
|
* @param pubKey Base64公钥
|
||||||
|
* @param data Base64数据
|
||||||
|
* @return 解密字符串
|
||||||
|
*/
|
||||||
|
public static String decryptNotifyData(String pubKey, String data) {
|
||||||
|
ByteArrayOutputStream out = null;
|
||||||
|
try {
|
||||||
|
log.debug("拉卡拉tk解密公钥:{}", pubKey);
|
||||||
|
log.debug("拉卡拉tk待解密data数据:{}", data);
|
||||||
|
java.util.Base64.Decoder decoder = java.util.Base64.getDecoder();
|
||||||
|
byte[] keyBytes = decoder.decode(pubKey.getBytes(StandardCharsets.UTF_8));
|
||||||
|
byte[] dataBytes = decoder.decode(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
|
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes));
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||||
|
out = new ByteArrayOutputStream();
|
||||||
|
byte[] cache;
|
||||||
|
for (int i = 0, offset = 0, length = dataBytes.length; length - offset > 0; i++, offset = i * 128) {
|
||||||
|
if (length - offset > 128) {
|
||||||
|
cache = cipher.doFinal(dataBytes, offset, 128);
|
||||||
|
} else {
|
||||||
|
cache = cipher.doFinal(dataBytes, offset, length - offset);
|
||||||
|
}
|
||||||
|
out.write(cache, 0, cache.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
String decodedDataStr = out.toString("UTF-8");
|
||||||
|
log.debug("拉卡拉tk数据解密结果:{}", decodedDataStr);
|
||||||
|
|
||||||
|
return decodedDataStr;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.error("Base64解码失败: {}", e.getMessage(), e);
|
||||||
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||||
|
log.error("RSA算法初始化失败: {}", e.getMessage(), e);
|
||||||
|
} catch (InvalidKeySpecException | InvalidKeyException e) {
|
||||||
|
log.error("私钥格式或类型错误: {}", e.getMessage(), e);
|
||||||
|
} catch (BadPaddingException | IllegalBlockSizeException e) {
|
||||||
|
log.error("解密数据块大小或填充错误: {}", e.getMessage(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("解密过程中出现未知异常: {}", e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
closeQuietly(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,7 +144,7 @@ sf-express:
|
|||||||
#拉卡拉进件配置
|
#拉卡拉进件配置
|
||||||
lakala:
|
lakala:
|
||||||
#服务地址
|
#服务地址
|
||||||
server_url: https://test.wsmsd.cn/sit
|
server_url: https://test.wsmsd.cn
|
||||||
#应用Id
|
#应用Id
|
||||||
app_id: OP00000003
|
app_id: OP00000003
|
||||||
#商户证书序列号
|
#商户证书序列号
|
||||||
|
|||||||
@ -144,7 +144,7 @@ sf-express:
|
|||||||
#拉卡拉进件配置
|
#拉卡拉进件配置
|
||||||
lakala:
|
lakala:
|
||||||
#服务地址
|
#服务地址
|
||||||
server_url: https://test.wsmsd.cn/sit
|
server_url: https://test.wsmsd.cn
|
||||||
#应用Id
|
#应用Id
|
||||||
app_id: OP00000003
|
app_id: OP00000003
|
||||||
#商户证书序列号
|
#商户证书序列号
|
||||||
@ -164,7 +164,7 @@ lakala:
|
|||||||
# 拉卡拉拓客进件配置
|
# 拉卡拉拓客进件配置
|
||||||
tk:
|
tk:
|
||||||
#服务地址
|
#服务地址
|
||||||
server_url: https://test.wsmsd.cn
|
server_url: https://test.wsmsd.cn/sit
|
||||||
client_id: lsycs
|
client_id: lsycs
|
||||||
client_secret: XPa1HB5d55Ig0qV8
|
client_secret: XPa1HB5d55Ig0qV8
|
||||||
user_no: 29153396
|
user_no: 29153396
|
||||||
|
|||||||
@ -171,7 +171,7 @@ lakala:
|
|||||||
# #终端号码,M0780629(B2B收银台) M0780798(专业化扫码)
|
# #终端号码,M0780629(B2B收银台) M0780798(专业化扫码)
|
||||||
# term_no: M0780798
|
# term_no: M0780798
|
||||||
#服务地址
|
#服务地址
|
||||||
server_url: https://test.wsmsd.cn/sit
|
server_url: https://test.wsmsd.cn
|
||||||
#应用Id
|
#应用Id
|
||||||
app_id: OP00000003
|
app_id: OP00000003
|
||||||
#商户证书序列号
|
#商户证书序列号
|
||||||
|
|||||||
@ -148,7 +148,7 @@ sf-express:
|
|||||||
#拉卡拉进件配置
|
#拉卡拉进件配置
|
||||||
lakala:
|
lakala:
|
||||||
#服务地址
|
#服务地址
|
||||||
server_url: https://test.wsmsd.cn/sit
|
server_url: https://test.wsmsd.cn
|
||||||
#应用Id
|
#应用Id
|
||||||
app_id: OP00000003
|
app_id: OP00000003
|
||||||
#商户证书序列号
|
#商户证书序列号
|
||||||
|
|||||||
@ -148,7 +148,7 @@ sf-express:
|
|||||||
#拉卡拉进件配置
|
#拉卡拉进件配置
|
||||||
lakala:
|
lakala:
|
||||||
#服务地址
|
#服务地址
|
||||||
server_url: https://test.wsmsd.cn/sit
|
server_url: https://test.wsmsd.cn
|
||||||
#应用Id
|
#应用Id
|
||||||
app_id: OP00000003
|
app_id: OP00000003
|
||||||
#商户证书序列号
|
#商户证书序列号
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user