|
|
@@ -13,6 +13,7 @@ import com.izouma.nineth.repo.GiftOrderRepo;
|
|
|
import com.izouma.nineth.repo.MintOrderRepo;
|
|
|
import com.izouma.nineth.repo.OrderRepo;
|
|
|
import com.izouma.nineth.utils.DateTimeUtils;
|
|
|
+import com.izouma.nineth.utils.SnowflakeIdWorker;
|
|
|
import lombok.AllArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.codec.binary.Base64;
|
|
|
@@ -233,6 +234,121 @@ public class SandPayService {
|
|
|
}
|
|
|
|
|
|
|
|
|
- public void transfer(String name, String bank, BigDecimal amount) {
|
|
|
+ public JSONObject transfer(String name, String bank, BigDecimal amount) {
|
|
|
+ JSONObject request = new JSONObject();
|
|
|
+ DecimalFormat df = new DecimalFormat("000000000000", DecimalFormatSymbols.getInstance(Locale.US));
|
|
|
+ request.put("version", "01"); //版本号
|
|
|
+ request.put("productId", "00000004"); //产品ID
|
|
|
+ request.put("tranTime", DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmmss")); //交易时间
|
|
|
+ request.put("orderCode", new SnowflakeIdWorker(1, 1).nextId()); //订单号
|
|
|
+ request.put("timeOut", DateTimeUtils.format(LocalDateTime.now().plusMinutes(3), "yyyyMMddHHmmss")); //订单超时时间
|
|
|
+ request.put("tranAmt", df.format(amount.multiply(new BigDecimal("100")))); //金额
|
|
|
+ request.put("currencyCode", "156"); //币种
|
|
|
+ request.put("accAttr", "0"); //账户属性 0-对私 1-对公
|
|
|
+ request.put("accType", "4"); //账号类型 3-公司账户 4-银行卡
|
|
|
+ request.put("accNo", bank); //收款人账户号
|
|
|
+ request.put("accName", name); //收款人账户名
|
|
|
+ request.put("provNo", ""); //收款人开户省份编码
|
|
|
+ request.put("cityNo", ""); //收款人开会城市编码
|
|
|
+ request.put("bankName", ""); //收款账户开户行名称
|
|
|
+ request.put("bankType", ""); //收款人账户联行号
|
|
|
+ request.put("remark", "消费"); //摘要
|
|
|
+ request.put("payMode", ""); //付款模式
|
|
|
+ request.put("channelType", ""); //渠道类型
|
|
|
+ request.put("extendParams", ""); //业务扩展参数
|
|
|
+ request.put("reqReserved", ""); //请求方保留域
|
|
|
+ request.put("extend", ""); //扩展域
|
|
|
+ request.put("phone", ""); //手机号
|
|
|
+
|
|
|
+ String reqData = request.toJSONString();
|
|
|
+ log.info("请求数据:{}", reqData);
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ String aesKey = RandomStringGenerator.getRandomStringByLength(16);
|
|
|
+ byte[] aesKeyBytes = aesKey.getBytes(StandardCharsets.UTF_8);
|
|
|
+
|
|
|
+ byte[] plainBytes = reqData.getBytes(StandardCharsets.UTF_8);
|
|
|
+ String encryptData = new String(Base64.encodeBase64(
|
|
|
+ CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES",
|
|
|
+ "AES/ECB/PKCS5Padding", null)), StandardCharsets.UTF_8);
|
|
|
+
|
|
|
+ String sign = new String(Base64.encodeBase64(
|
|
|
+ CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(),
|
|
|
+ "SHA1WithRSA")), StandardCharsets.UTF_8);
|
|
|
+
|
|
|
+ String encryptKey = new String(Base64.encodeBase64(
|
|
|
+ CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11,
|
|
|
+ "RSA/ECB/PKCS1Padding")), StandardCharsets.UTF_8);
|
|
|
+
|
|
|
+
|
|
|
+ Map<String, String> reqMap = new HashMap<String, String>();
|
|
|
+ //整体报文格式
|
|
|
+ reqMap.put("transCode", "RTPM"); // 交易码
|
|
|
+ reqMap.put("accessType", "0"); // 接入类型
|
|
|
+ reqMap.put("merId", sandPayProperties.getMid()); // 合作商户ID 杉德系统分配,唯一标识
|
|
|
+ reqMap.put("encryptKey", encryptKey); // 加密后的AES秘钥
|
|
|
+ reqMap.put("encryptData", encryptData); // 加密后的请求/应答报文
|
|
|
+ reqMap.put("sign", sign); // 签名
|
|
|
+ reqMap.put("extend", ""); // 扩展域
|
|
|
+
|
|
|
+ String result;
|
|
|
+ try {
|
|
|
+ log.info("请求报文:{}", reqMap);
|
|
|
+ result = HttpClient.doPost("https://caspay.sandpay.com.cn/agent-main/openapi/agentpay",
|
|
|
+ reqMap, 10000, 10000);
|
|
|
+ result = URLDecoder.decode(result, StandardCharsets.UTF_8);
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error(e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("响应报文:{}", result);
|
|
|
+ Map<String, String> responseMap = SDKUtil.convertResultStringToMap(result);
|
|
|
+
|
|
|
+ String retEncryptKey = responseMap.get("encryptKey");
|
|
|
+ String retEncryptData = responseMap.get("encryptData");
|
|
|
+ String retSign = responseMap.get("sign");
|
|
|
+
|
|
|
+ log.debug("retEncryptKey:[{}]", retEncryptKey);
|
|
|
+ log.debug("retEncryptData:[{}]", retEncryptData);
|
|
|
+ log.debug("retSign:[{}]", retSign);
|
|
|
+
|
|
|
+ byte[] decodeBase64KeyBytes = Base64.decodeBase64(retEncryptKey
|
|
|
+ .getBytes(StandardCharsets.UTF_8));
|
|
|
+
|
|
|
+ byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(
|
|
|
+ decodeBase64KeyBytes, CertUtil.getPrivateKey(), 2048, 11,
|
|
|
+ "RSA/ECB/PKCS1Padding");
|
|
|
+
|
|
|
+ byte[] decodeBase64DataBytes = Base64.decodeBase64(retEncryptData.getBytes(StandardCharsets.UTF_8));
|
|
|
+
|
|
|
+ byte[] respDataBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes,
|
|
|
+ merchantAESKeyBytes, "AES", "AES/ECB/PKCS5Padding", null);
|
|
|
+
|
|
|
+ String respData = new String(respDataBytes, StandardCharsets.UTF_8);
|
|
|
+ log.info("retData:[" + respData + "]");
|
|
|
+ System.out.println("响应data数据:" + respData);
|
|
|
+
|
|
|
+ byte[] signBytes = Base64.decodeBase64(retSign.getBytes(StandardCharsets.UTF_8));
|
|
|
+
|
|
|
+ boolean isValid = CryptoUtil.verifyDigitalSign(respDataBytes, signBytes,
|
|
|
+ CertUtil.getPublicKey(), "SHA1WithRSA");
|
|
|
+
|
|
|
+ if (!isValid) {
|
|
|
+ log.error("verify sign fail.");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ log.info("verify sign success");
|
|
|
+ System.out.println("verify sign success");
|
|
|
+
|
|
|
+ JSONObject respJson = JSONObject.parseObject(respData);
|
|
|
+ return respJson;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+
|
|
|
+ log.error(e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
}
|
|
|
}
|