package com.izouma.nineth.service; import cn.com.sandpay.cashier.sdk.*; import com.alibaba.fastjson.JSONObject; import com.izouma.nineth.config.Constants; import com.izouma.nineth.config.GeneralProperties; import com.izouma.nineth.config.RedisKeys; import com.izouma.nineth.config.SandPayProperties; import com.izouma.nineth.dto.PayQuery; import com.izouma.nineth.enums.PayStatus; import com.izouma.nineth.exception.BusinessException; import com.izouma.nineth.repo.AuctionOrderRepo; 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.MD5Util; import com.izouma.nineth.utils.SnowflakeIdWorker; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.TimeUnit; @Service @AllArgsConstructor @Slf4j public class SandPayService { private final OrderRepo orderRepo; private final GiftOrderRepo giftOrderRepo; private final SandPayProperties sandPayProperties; private final MintOrderRepo mintOrderRepo; private final SnowflakeIdWorker snowflakeIdWorker; private final GeneralProperties generalProperties; private final AuctionOrderRepo auctionOrderRepo; private final RedisTemplate redisTemplate; public static String paddingOrderId(String orderId) { if (orderId != null && orderId.length() < 12) { StringBuilder orderIdBuilder = new StringBuilder(orderId); for (int i = orderIdBuilder.length(); i < 12; i++) { orderIdBuilder.insert(0, "0"); } orderId = orderIdBuilder.toString(); } return orderId; } public static String getReqTime() { return DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmmss"); } public static String getTimeout(int seconds) { return DateTimeUtils.format(LocalDateTime.now().plusSeconds(seconds), "yyyyMMddHHmmss"); } public static String getTimeout(LocalDateTime createTime, int seconds) { return DateTimeUtils.format(Optional.ofNullable(createTime).orElse(LocalDateTime.now()) .plusSeconds(seconds), "yyyyMMddHHmmss"); } public static String convertAmount(BigDecimal amount) { DecimalFormat df = new DecimalFormat("000000000000", DecimalFormatSymbols.getInstance(Locale.US)); return df.format(amount.multiply(new BigDecimal("100"))); } public static JSONObject requestServer(JSONObject header, JSONObject body, String reqAddr) { Map reqMap = new HashMap(); JSONObject reqJson = new JSONObject(); reqJson.put("head", header); reqJson.put("body", body); String reqStr = reqJson.toJSONString(); String reqSign; // 签名 try { reqSign = new String(Base64.encodeBase64(CryptoUtil.digitalSign(reqStr.getBytes(StandardCharsets.UTF_8), CertUtil.getPrivateKey(), "SHA1WithRSA"))); } catch (Exception e) { log.error(e.getMessage()); return null; } //整体报文格式 reqMap.put("charset", "UTF-8"); reqMap.put("data", reqStr); reqMap.put("signType", "01"); reqMap.put("sign", reqSign); reqMap.put("extend", ""); String result; try { log.info("请求报文:\n{}", JSONObject.toJSONString(reqJson, true)); result = HttpClient.doPost(reqAddr, reqMap, 30000, 30000); result = URLDecoder.decode(result, StandardCharsets.UTF_8); } catch (IOException e) { log.error(e.getMessage()); return null; } Map respMap = SDKUtil.convertResultStringToMap(result); String respData = respMap.get("data"); String respSign = respMap.get("sign"); // 验证签名 boolean valid; try { valid = CryptoUtil.verifyDigitalSign(respData.getBytes(StandardCharsets.UTF_8), Base64.decodeBase64(respSign), CertUtil.getPublicKey(), "SHA1WithRSA"); if (!valid) { log.error("verify sign fail."); return null; } log.info("verify sign success"); JSONObject respJson = JSONObject.parseObject(respData); if (respJson != null) { log.info("响应码:[{}]", respJson.getJSONObject("head").getString("respCode")); log.info("响应描述:[{}]", respJson.getJSONObject("head").getString("respMsg")); log.info("响应报文:\n{}", JSONObject.toJSONString(respJson, true)); } else { log.error("服务器请求异常!!!"); } return respJson; } catch (Exception e) { log.error(e.getMessage()); return null; } } public String pay(String orderId, String subject, BigDecimal amount, LocalDateTime expireAt, String type) { String pOrderId = paddingOrderId(orderId); JSONObject extend = new JSONObject(); extend.put("type", type); extend.put("orderId", pOrderId); extend.put("id", pOrderId); JSONObject header = new JSONObject(); header.put("version", "1.0"); //版本号 header.put("method", "sandpay.trade.precreate"); //接口名称:统一下单并支付 header.put("productId", "00000006"); //产品编码 header.put("mid", sandPayProperties.getMid()); //商户号 header.put("accessType", "1"); //接入类型设置为普通商户接入 header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端 header.put("reqTime", getReqTime()); //请求时间 JSONObject body = new JSONObject(); body.put("payTool", "0401"); //支付工具: 固定填写0401 body.put("orderCode", pOrderId); //商户订单号 body.put("totalAmount", convertAmount(amount)); //订单金额 12位长度,精确到分 //body.put("limitPay", "5"); //限定支付方式 送1-限定不能使用贷记卡 送4-限定不能使用花呗 送5-限定不能使用贷记卡+花呗 body.put("subject", subject); //订单标题 body.put("body", subject); //订单描述 body.put("txnTimeOut", getTimeout(expireAt, 0)); //订单超时时间 body.put("notifyUrl", sandPayProperties.getNotifyUrl()); //异步通知地址 body.put("bizExtendParams", ""); //业务扩展参数 body.put("merchExtendParams", ""); //商户扩展参数 body.put("extend", extend.toJSONString()); //扩展域 JSONObject res = requestServer(header, body, "https://cashier.sandpay.com.cn/qr/api/order/create"); String respCode = res.getJSONObject("head").getString("respCode"); if ("000000".equals(respCode)) { redisTemplate.opsForValue() .set(RedisKeys.PAY_TMP + orderId, Constants.PayChannel.SAND, 1, TimeUnit.DAYS); return "alipays://platformapi/startapp?saId=10000007&qrcode=" + res.getJSONObject("body").getString("qrCode"); } String msg = res.getJSONObject("head").getString("respMsg"); throw new BusinessException(Constants.PAY_ERR_MSG, msg); } public String payQuick(String orderId, String subject, BigDecimal amount, LocalDateTime expireAt, String type, String returnUrl) { String pOrderId = paddingOrderId(orderId); JSONObject extend = new JSONObject(); extend.put("type", type); extend.put("orderId", pOrderId); extend.put("id", pOrderId); JSONObject header = new JSONObject(); header.put("version", "1.0"); //版本号 header.put("method", "sandpay.trade.pay"); //接口名称:统一下单 header.put("mid", sandPayProperties.getMid()); //商户号 header.put("accessType", "1"); //接入类型设置为平台商户接入 //接入类型设置为普通商户接入 header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端 header.put("reqTime", getReqTime()); //请求时间 header.put("productId", "00000008"); //产品编码 JSONObject body = new JSONObject(); body.put("orderCode", pOrderId); //商户订单号 body.put("totalAmount", convertAmount(amount)); //订单金额 body.put("subject", subject); //订单标题 body.put("body", subject); //订单描述 body.put("txnTimeOut", getTimeout(expireAt, 0)); //订单超时时间 body.put("clientIp", "192.168.22.55"); //客户端IP body.put("limitPay", ""); //限定支付方式 送1-限定不能使用贷记卡送 4-限定不能使用花呗 送5-限定不能使用贷记卡+花呗 body.put("notifyUrl", sandPayProperties.getNotifyUrl()); //异步通知地址 body.put("frontUrl", returnUrl); //前台通知地址 body.put("storeId", ""); //商户门店编号 body.put("terminalId", ""); //商户终端编号 body.put("operatorId", ""); //操作员编号 body.put("clearCycle", ""); //清算模式 body.put("royaltyInfo", ""); //分账信息 body.put("riskRateInfo", ""); //风控信息域 body.put("bizExtendParams", ""); //业务扩展参数 body.put("merchExtendParams", ""); //商户扩展参数 body.put("extend", extend.toJSONString()); //扩展域 body.put("payMode", "sand_h5"); //支付模式 JSONObject res = requestServer(header, body, "https://cashier.sandpay.com.cn/gateway/api/order/pay"); String respCode = res.getJSONObject("head").getString("respCode"); if ("000000".equals(respCode)) { redisTemplate.opsForValue() .set(RedisKeys.PAY_TMP + orderId, Constants.PayChannel.SAND, 1, TimeUnit.DAYS); return res.getJSONObject("body").getString("credential"); } String msg = res.getJSONObject("head").getString("respMsg"); throw new BusinessException(Constants.PAY_ERR_MSG, msg); } public String payQuickBind(String orderId, String subject, BigDecimal amount, LocalDateTime expireAt, String type, String returnUrl, Long userId, String name, String idNo) { String pOrderId = paddingOrderId(orderId); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); Calendar calendar = Calendar.getInstance(); String createTime = sdf.format(calendar.getTime()); calendar.add(Calendar.HOUR, 1); String endTime = sdf.format(calendar.getTime()); String version = "10"; String mer_no = sandPayProperties.getMid(); //key1 String mer_key = "fvLh4EEoJPJEkqg+6s3B8sTRVREAyVAxS34+5JGiJOmIFnIrnfD1k/KiUYo5Nfb7tBx0jj9HOI0="; UUID uuid = UUID.randomUUID(); String mer_order_no = uuid.toString().replaceAll("-", ""); String notify_url = sandPayProperties.getNotifyUrl(); String order_amt = amount.toPlainString(); //"userId":"用户在商户下唯一标识 1-10位","userName":"证件姓名","idCard":"18位身份证号码" String pay_extra = "{\"userId\":\"" + userId + "\",\"userName\":\"" + name + "\",\"idCard\":\"" + idNo + "\"}"; //md5key String key = "KvKQYXhwvkoQRAiQN2IzVSaecqYFP8emDHJrLettbShZsd8k/V2OU/myAV6My/qeotZ8K0WG8ifDzgelCexY7517gmF7SqFUcyuKgSC+HFNmn14C4iy9JRy0NvsSJRjgMzZWAhrwNL5ixuz+UKFTiw=="; JSONObject extend = new JSONObject(); extend.put("type", type); extend.put("orderId", pOrderId); extend.put("id", pOrderId); Map map = new LinkedHashMap<>(); map.put("accsplit_flag", "NO"); map.put("create_ip", "127_0_0_1"); map.put("create_time", createTime); map.put("extend", extend.toJSONString()); // if(!(gh_static_url==null||"".equals(gh_static_url)))map.put("gh_static_url",gh_static_url);; map.put("mer_key", mer_key); map.put("mer_no", mer_no); map.put("mer_order_no", mer_order_no); map.put("notify_url", notify_url); map.put("order_amt", order_amt); map.put("pay_extra", pay_extra); map.put("return_url", returnUrl); map.put("sign_type", "MD5"); map.put("store_id", "000000"); map.put("version", version); map.put("key", key); // map.put("extend", "aaa"); // map.put("expire_time",endTime); // map.put("goods_name",goods_name); // map.put("product_code","02010006"); // map.put("clear_cycle","0"); String signature = ""; for (String s : map.keySet()) { if (!(map.get(s) == null || map.get(s).equals(""))) { signature += s + "="; signature += map.get(s) + "&"; } } signature = signature.substring(0, signature.length() - 1); String sign = MD5Util.encode(signature).toUpperCase(); //url return "https://sandcash.mixienet.com.cn/pay/h5/quicktopup?" + "version=" + version + "&mer_no=" + mer_no + "&mer_key=" + URLEncoder.encode(mer_key, StandardCharsets.UTF_8) + "&mer_order_no=" + mer_order_no + "&create_time=" + createTime + "&expire_time=" + getTimeout(expireAt, 0) + "&extend=" + URLEncoder.encode(extend.toJSONString(), StandardCharsets.UTF_8) + "&order_amt=" + order_amt + "¬ify_url=" + URLEncoder.encode(notify_url, StandardCharsets.UTF_8) + "&return_url=" + URLEncoder.encode(returnUrl, StandardCharsets.UTF_8) + "&create_ip=127_0_0_1" + "&goods_name=" + URLEncoder.encode(subject, StandardCharsets.UTF_8) + "&store_id=000000" + "&product_code=06030003" + "&clear_cycle=3" + "&pay_extra=" + URLEncoder.encode(pay_extra, StandardCharsets.UTF_8) + "&meta_option=%5B%7B%22s%22%3A%22Android%22,%22n%22%3A%22wxDemo%22,%22id%22%3A%22com.pay.paytypetest%22,%22sc%22%3A%22com.pay.paytypetest%22%7D%5D" + "&accsplit_flag=NO" + "&jump_scheme=" + // "&gh_static_url="+gh_static_url+""+ "&sign_type=MD5" + "&key=" + URLEncoder.encode(key, StandardCharsets.UTF_8) + "&sign=" + sign; } public JSONObject query(String orderId) { orderId = paddingOrderId(orderId); JSONObject header = new JSONObject(); header.put("version", "1.0"); //版本号 header.put("method", "sandpay.trade.query"); //接口名称:订单查询 header.put("productId", "00000006"); //产品编码 header.put("mid", sandPayProperties.getMid()); //商户号 header.put("accessType", "1"); //接入类型设置为普通商户接入 header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端 header.put("reqTime", getReqTime()); //请求时间 JSONObject body = new JSONObject(); body.put("orderCode", orderId); body.put("extend", ""); //扩展域 return requestServer(header, body, "https://cashier.sandpay.com.cn/qr/api/order/query"); } public JSONObject refund(String orderId, BigDecimal amount) { orderId = paddingOrderId(orderId); JSONObject header = new JSONObject(); header.put("version", "1.0"); //版本号 header.put("method", "sandpay.trade.refund"); //接口名称:退货 header.put("productId", "00000006"); //产品编码 header.put("mid", sandPayProperties.getMid()); //商户号 header.put("accessType", "1"); //接入类型设置为普通商户接入 header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端 header.put("reqTime", getReqTime()); //请求时间 JSONObject body = new JSONObject(); body.put("orderCode", snowflakeIdWorker.nextId()); //商户订单号 body.put("oriOrderCode", paddingOrderId(orderId)); //原交易订单号 body.put("refundAmount", convertAmount(amount)); //退货金额 body.put("refundReason", "退款"); //退货原因 body.put("notifyUrl", sandPayProperties.getNotifyUrl()); //异步通知地址 body.put("extend", ""); return requestServer(header, body, "https://cashier.sandpay.com.cn/qr/api/order/refund"); } public JSONObject transfer(String id, String name, String bank, BigDecimal amount, String phone) { 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", getReqTime()); //交易时间 request.put("orderCode", id); //订单号 request.put("timeOut", getTimeout(180)); //订单超时时间 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", 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 reqMap = new HashMap(); //整体报文格式 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, 300000, 300000); result = URLDecoder.decode(result, StandardCharsets.UTF_8); } catch (IOException e) { log.error(e.getMessage()); return null; } log.info("响应报文:{}", result); Map 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; } } public JSONObject queryTransfer(String tranTime, String orderId) { JSONObject request = new JSONObject(); request.put("version", "01"); // 版本号 request.put("productId", "00000004"); // 产品ID request.put("tranTime", tranTime); // 查询订单的交易时间 request.put("orderCode", orderId); // 要查询的订单号 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 reqMap = new HashMap(); //整体报文格式 reqMap.put("transCode", "ODQU"); // 交易码 reqMap.put("accessType", "0"); // 接入类型 reqMap.put("merId", sandPayProperties.getMid()); // 合作商户ID 杉德系统分配,唯一标识 reqMap.put("plId", null); 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/queryOrder", reqMap, 300000, 300000); result = URLDecoder.decode(result, StandardCharsets.UTF_8); } catch (IOException e) { log.error(e.getMessage()); return null; } log.info("响应报文:{}", result); Map 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; } } public PayQuery payQuery(String orderId) { JSONObject res = query(orderId); PayQuery query = new PayQuery(Constants.PayChannel.SAND); String respCode = res.getJSONObject("head").getString("respCode"); if ("000000".equals(respCode)) { query.setExist(true); JSONObject body = res.getJSONObject("body"); query.setMsg(body.getString("orderMsg") + " " + body.getString("oriRespMsg")); query.setAmount(new BigDecimal(body.getString("totalAmount")) .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)); query.setTransactionId(body.getString("payOrderCode")); if (StringUtils.isNotEmpty(body.getString("payTime"))) { query.setPayTime(LocalDateTime.parse(body.getString("payTime"), DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); } if (StringUtils.isNotEmpty(body.getString("oriOrderCode"))) { query.setOrderId(body.getString("oriOrderCode")); } switch (body.getString("orderStatus")) { case "00": query.setStatus(PayStatus.SUCCESS); break; case "01": query.setStatus(PayStatus.PENDING); break; case "02": query.setStatus(PayStatus.FAIL); break; case "03": query.setStatus(PayStatus.CANCEL); break; case "04": query.setStatus(PayStatus.REFUNDED); break; case "05": query.setStatus(PayStatus.REFUNDING); break; } } else { query.setExist(false); query.setMsg(res.getString("msg")); } return query; } public BigDecimal balance() { JSONObject request = new JSONObject(); request.put("version", "01"); //版本号 request.put("productId", "00000004"); //产品ID request.put("tranTime", getReqTime()); //交易时间 request.put("orderCode", snowflakeIdWorker.nextId()); //订单号 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 reqMap = new HashMap(); //整体报文格式 reqMap.put("transCode", "MBQU"); // 交易码 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/queryBalance", reqMap, 300000, 300000); result = URLDecoder.decode(result, StandardCharsets.UTF_8); } catch (IOException e) { log.error(e.getMessage()); return null; } log.info("响应报文:{}", result); Map 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.getBigDecimal("balance").divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); } catch (Exception e) { log.error(e.getMessage()); return null; } } }