SandPayService.java 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. package com.izouma.nineth.service;
  2. import cn.com.sandpay.cashier.sdk.*;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.izouma.nineth.config.Constants;
  5. import com.izouma.nineth.config.GeneralProperties;
  6. import com.izouma.nineth.config.RedisKeys;
  7. import com.izouma.nineth.config.SandPayProperties;
  8. import com.izouma.nineth.dto.PayQuery;
  9. import com.izouma.nineth.enums.PayStatus;
  10. import com.izouma.nineth.exception.BusinessException;
  11. import com.izouma.nineth.repo.AuctionOrderRepo;
  12. import com.izouma.nineth.repo.GiftOrderRepo;
  13. import com.izouma.nineth.repo.MintOrderRepo;
  14. import com.izouma.nineth.repo.OrderRepo;
  15. import com.izouma.nineth.utils.DateTimeUtils;
  16. import com.izouma.nineth.utils.MD5Util;
  17. import com.izouma.nineth.utils.SnowflakeIdWorker;
  18. import lombok.AllArgsConstructor;
  19. import lombok.extern.slf4j.Slf4j;
  20. import org.apache.commons.codec.binary.Base64;
  21. import org.apache.commons.lang3.StringUtils;
  22. import org.springframework.data.redis.core.RedisTemplate;
  23. import org.springframework.stereotype.Service;
  24. import java.io.IOException;
  25. import java.math.BigDecimal;
  26. import java.math.RoundingMode;
  27. import java.net.URLDecoder;
  28. import java.net.URLEncoder;
  29. import java.nio.charset.StandardCharsets;
  30. import java.text.DecimalFormat;
  31. import java.text.DecimalFormatSymbols;
  32. import java.text.SimpleDateFormat;
  33. import java.time.LocalDateTime;
  34. import java.time.format.DateTimeFormatter;
  35. import java.util.*;
  36. import java.util.concurrent.TimeUnit;
  37. @Service
  38. @AllArgsConstructor
  39. @Slf4j
  40. public class SandPayService {
  41. private final OrderRepo orderRepo;
  42. private final GiftOrderRepo giftOrderRepo;
  43. private final SandPayProperties sandPayProperties;
  44. private final MintOrderRepo mintOrderRepo;
  45. private final SnowflakeIdWorker snowflakeIdWorker;
  46. private final GeneralProperties generalProperties;
  47. private final AuctionOrderRepo auctionOrderRepo;
  48. private final RedisTemplate<String, Object> redisTemplate;
  49. public static String paddingOrderId(String orderId) {
  50. if (orderId != null && orderId.length() < 12) {
  51. StringBuilder orderIdBuilder = new StringBuilder(orderId);
  52. for (int i = orderIdBuilder.length(); i < 12; i++) {
  53. orderIdBuilder.insert(0, "0");
  54. }
  55. orderId = orderIdBuilder.toString();
  56. }
  57. return orderId;
  58. }
  59. public static String getReqTime() {
  60. return DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmmss");
  61. }
  62. public static String getTimeout(int seconds) {
  63. return DateTimeUtils.format(LocalDateTime.now().plusSeconds(seconds), "yyyyMMddHHmmss");
  64. }
  65. public static String getTimeout(LocalDateTime createTime, int seconds) {
  66. return DateTimeUtils.format(Optional.ofNullable(createTime).orElse(LocalDateTime.now())
  67. .plusSeconds(seconds), "yyyyMMddHHmmss");
  68. }
  69. public static String convertAmount(BigDecimal amount) {
  70. DecimalFormat df = new DecimalFormat("000000000000", DecimalFormatSymbols.getInstance(Locale.US));
  71. return df.format(amount.multiply(new BigDecimal("100")));
  72. }
  73. public static JSONObject requestServer(JSONObject header, JSONObject body, String reqAddr) {
  74. Map<String, String> reqMap = new HashMap<String, String>();
  75. JSONObject reqJson = new JSONObject();
  76. reqJson.put("head", header);
  77. reqJson.put("body", body);
  78. String reqStr = reqJson.toJSONString();
  79. String reqSign;
  80. // 签名
  81. try {
  82. reqSign = new String(Base64.encodeBase64(CryptoUtil.digitalSign(reqStr.getBytes(StandardCharsets.UTF_8),
  83. CertUtil.getPrivateKey(), "SHA1WithRSA")));
  84. } catch (Exception e) {
  85. log.error(e.getMessage());
  86. return null;
  87. }
  88. //整体报文格式
  89. reqMap.put("charset", "UTF-8");
  90. reqMap.put("data", reqStr);
  91. reqMap.put("signType", "01");
  92. reqMap.put("sign", reqSign);
  93. reqMap.put("extend", "");
  94. String result;
  95. try {
  96. log.info("请求报文:\n{}", JSONObject.toJSONString(reqJson, true));
  97. result = HttpClient.doPost(reqAddr, reqMap, 30000, 30000);
  98. result = URLDecoder.decode(result, StandardCharsets.UTF_8);
  99. } catch (IOException e) {
  100. log.error(e.getMessage());
  101. return null;
  102. }
  103. Map<String, String> respMap = SDKUtil.convertResultStringToMap(result);
  104. String respData = respMap.get("data");
  105. String respSign = respMap.get("sign");
  106. // 验证签名
  107. boolean valid;
  108. try {
  109. valid = CryptoUtil.verifyDigitalSign(respData.getBytes(StandardCharsets.UTF_8),
  110. Base64.decodeBase64(respSign), CertUtil.getPublicKey(), "SHA1WithRSA");
  111. if (!valid) {
  112. log.error("verify sign fail.");
  113. return null;
  114. }
  115. log.info("verify sign success");
  116. JSONObject respJson = JSONObject.parseObject(respData);
  117. if (respJson != null) {
  118. log.info("响应码:[{}]", respJson.getJSONObject("head").getString("respCode"));
  119. log.info("响应描述:[{}]", respJson.getJSONObject("head").getString("respMsg"));
  120. log.info("响应报文:\n{}", JSONObject.toJSONString(respJson, true));
  121. } else {
  122. log.error("服务器请求异常!!!");
  123. }
  124. return respJson;
  125. } catch (Exception e) {
  126. log.error(e.getMessage());
  127. return null;
  128. }
  129. }
  130. public String pay(String orderId, String subject, BigDecimal amount, LocalDateTime expireAt, String type) {
  131. String pOrderId = paddingOrderId(orderId);
  132. JSONObject extend = new JSONObject();
  133. extend.put("type", type);
  134. extend.put("orderId", pOrderId);
  135. extend.put("id", pOrderId);
  136. JSONObject header = new JSONObject();
  137. header.put("version", "1.0"); //版本号
  138. header.put("method", "sandpay.trade.precreate"); //接口名称:统一下单并支付
  139. header.put("productId", "00000006"); //产品编码
  140. header.put("mid", sandPayProperties.getMid()); //商户号
  141. header.put("accessType", "1"); //接入类型设置为普通商户接入
  142. header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端
  143. header.put("reqTime", getReqTime()); //请求时间
  144. JSONObject body = new JSONObject();
  145. body.put("payTool", "0401"); //支付工具: 固定填写0401
  146. body.put("orderCode", pOrderId); //商户订单号
  147. body.put("totalAmount", convertAmount(amount)); //订单金额 12位长度,精确到分
  148. //body.put("limitPay", "5"); //限定支付方式 送1-限定不能使用贷记卡 送4-限定不能使用花呗 送5-限定不能使用贷记卡+花呗
  149. body.put("subject", subject); //订单标题
  150. body.put("body", subject); //订单描述
  151. body.put("txnTimeOut", getTimeout(expireAt, 0)); //订单超时时间
  152. body.put("notifyUrl", sandPayProperties.getNotifyUrl()); //异步通知地址
  153. body.put("bizExtendParams", ""); //业务扩展参数
  154. body.put("merchExtendParams", ""); //商户扩展参数
  155. body.put("extend", extend.toJSONString()); //扩展域
  156. JSONObject res = requestServer(header, body, "https://cashier.sandpay.com.cn/qr/api/order/create");
  157. String respCode = res.getJSONObject("head").getString("respCode");
  158. if ("000000".equals(respCode)) {
  159. redisTemplate.opsForValue()
  160. .set(RedisKeys.PAY_TMP + orderId, Constants.PayChannel.SAND, 1, TimeUnit.DAYS);
  161. return "alipays://platformapi/startapp?saId=10000007&qrcode="
  162. + res.getJSONObject("body").getString("qrCode");
  163. }
  164. String msg = res.getJSONObject("head").getString("respMsg");
  165. throw new BusinessException(Constants.PAY_ERR_MSG, msg);
  166. }
  167. public String payQuick(String orderId, String subject, BigDecimal amount, LocalDateTime expireAt,
  168. String type, String returnUrl) {
  169. String pOrderId = paddingOrderId(orderId);
  170. JSONObject extend = new JSONObject();
  171. extend.put("type", type);
  172. extend.put("orderId", pOrderId);
  173. extend.put("id", pOrderId);
  174. JSONObject header = new JSONObject();
  175. header.put("version", "1.0"); //版本号
  176. header.put("method", "sandpay.trade.pay"); //接口名称:统一下单
  177. header.put("mid", sandPayProperties.getMid()); //商户号
  178. header.put("accessType", "1"); //接入类型设置为平台商户接入 //接入类型设置为普通商户接入
  179. header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端
  180. header.put("reqTime", getReqTime()); //请求时间
  181. header.put("productId", "00000008"); //产品编码
  182. JSONObject body = new JSONObject();
  183. body.put("orderCode", pOrderId); //商户订单号
  184. body.put("totalAmount", convertAmount(amount)); //订单金额
  185. body.put("subject", subject); //订单标题
  186. body.put("body", subject); //订单描述
  187. body.put("txnTimeOut", getTimeout(expireAt, 0)); //订单超时时间
  188. body.put("clientIp", "192.168.22.55"); //客户端IP
  189. body.put("limitPay", ""); //限定支付方式 送1-限定不能使用贷记卡送 4-限定不能使用花呗 送5-限定不能使用贷记卡+花呗
  190. body.put("notifyUrl", sandPayProperties.getNotifyUrl()); //异步通知地址
  191. body.put("frontUrl", returnUrl); //前台通知地址
  192. body.put("storeId", ""); //商户门店编号
  193. body.put("terminalId", ""); //商户终端编号
  194. body.put("operatorId", ""); //操作员编号
  195. body.put("clearCycle", ""); //清算模式
  196. body.put("royaltyInfo", ""); //分账信息
  197. body.put("riskRateInfo", ""); //风控信息域
  198. body.put("bizExtendParams", ""); //业务扩展参数
  199. body.put("merchExtendParams", ""); //商户扩展参数
  200. body.put("extend", extend.toJSONString()); //扩展域
  201. body.put("payMode", "sand_h5"); //支付模式
  202. JSONObject res = requestServer(header, body, "https://cashier.sandpay.com.cn/gateway/api/order/pay");
  203. String respCode = res.getJSONObject("head").getString("respCode");
  204. if ("000000".equals(respCode)) {
  205. redisTemplate.opsForValue()
  206. .set(RedisKeys.PAY_TMP + orderId, Constants.PayChannel.SAND, 1, TimeUnit.DAYS);
  207. return res.getJSONObject("body").getString("credential");
  208. }
  209. String msg = res.getJSONObject("head").getString("respMsg");
  210. throw new BusinessException(Constants.PAY_ERR_MSG, msg);
  211. }
  212. public String payQuickBind(String orderId, String subject, BigDecimal amount, LocalDateTime expireAt,
  213. String type, String returnUrl, Long userId, String name, String idNo) {
  214. String pOrderId = paddingOrderId(orderId);
  215. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
  216. Calendar calendar = Calendar.getInstance();
  217. String createTime = sdf.format(calendar.getTime());
  218. calendar.add(Calendar.HOUR, 1);
  219. String endTime = sdf.format(calendar.getTime());
  220. String version = "10";
  221. String mer_no = sandPayProperties.getMid();
  222. //key1
  223. String mer_key = "fvLh4EEoJPJEkqg+6s3B8sTRVREAyVAxS34+5JGiJOmIFnIrnfD1k/KiUYo5Nfb7tBx0jj9HOI0=";
  224. UUID uuid = UUID.randomUUID();
  225. String mer_order_no = uuid.toString().replaceAll("-", "");
  226. String notify_url = sandPayProperties.getNotifyUrl();
  227. String order_amt = amount.toPlainString();
  228. //"userId":"用户在商户下唯一标识 1-10位","userName":"证件姓名","idCard":"18位身份证号码"
  229. String pay_extra = "{\"userId\":\"" + userId + "\",\"userName\":\"" + name + "\",\"idCard\":\"" + idNo + "\"}";
  230. //md5key
  231. String key = "KvKQYXhwvkoQRAiQN2IzVSaecqYFP8emDHJrLettbShZsd8k/V2OU/myAV6My/qeotZ8K0WG8ifDzgelCexY7517gmF7SqFUcyuKgSC+HFNmn14C4iy9JRy0NvsSJRjgMzZWAhrwNL5ixuz+UKFTiw==";
  232. JSONObject extend = new JSONObject();
  233. extend.put("type", type);
  234. extend.put("orderId", pOrderId);
  235. extend.put("id", pOrderId);
  236. Map<String, String> map = new LinkedHashMap<>();
  237. map.put("accsplit_flag", "NO");
  238. map.put("create_ip", "127_0_0_1");
  239. map.put("create_time", createTime);
  240. map.put("extend", extend.toJSONString());
  241. // if(!(gh_static_url==null||"".equals(gh_static_url)))map.put("gh_static_url",gh_static_url);;
  242. map.put("mer_key", mer_key);
  243. map.put("mer_no", mer_no);
  244. map.put("mer_order_no", mer_order_no);
  245. map.put("notify_url", notify_url);
  246. map.put("order_amt", order_amt);
  247. map.put("pay_extra", pay_extra);
  248. map.put("return_url", returnUrl);
  249. map.put("sign_type", "MD5");
  250. map.put("store_id", "000000");
  251. map.put("version", version);
  252. map.put("key", key);
  253. // map.put("extend", "aaa");
  254. // map.put("expire_time",endTime);
  255. // map.put("goods_name",goods_name);
  256. // map.put("product_code","02010006");
  257. // map.put("clear_cycle","0");
  258. String signature = "";
  259. for (String s : map.keySet()) {
  260. if (!(map.get(s) == null || map.get(s).equals(""))) {
  261. signature += s + "=";
  262. signature += map.get(s) + "&";
  263. }
  264. }
  265. signature = signature.substring(0, signature.length() - 1);
  266. String sign = MD5Util.encode(signature).toUpperCase();
  267. //url
  268. return "https://sandcash.mixienet.com.cn/pay/h5/quicktopup?" +
  269. "version=" + version +
  270. "&mer_no=" + mer_no +
  271. "&mer_key=" + URLEncoder.encode(mer_key, StandardCharsets.UTF_8) +
  272. "&mer_order_no=" + mer_order_no +
  273. "&create_time=" + createTime +
  274. "&expire_time=" + getTimeout(expireAt, 0) +
  275. "&extend=" + URLEncoder.encode(extend.toJSONString(), StandardCharsets.UTF_8) +
  276. "&order_amt=" + order_amt +
  277. "&notify_url=" + URLEncoder.encode(notify_url, StandardCharsets.UTF_8) +
  278. "&return_url=" + URLEncoder.encode(returnUrl, StandardCharsets.UTF_8) +
  279. "&create_ip=127_0_0_1" +
  280. "&goods_name=" + URLEncoder.encode(subject, StandardCharsets.UTF_8) +
  281. "&store_id=000000" +
  282. "&product_code=06030003" +
  283. "&clear_cycle=3" +
  284. "&pay_extra=" + URLEncoder.encode(pay_extra, StandardCharsets.UTF_8) +
  285. "&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" +
  286. "&accsplit_flag=NO" +
  287. "&jump_scheme=" +
  288. // "&gh_static_url="+gh_static_url+""+
  289. "&sign_type=MD5" +
  290. "&key=" + URLEncoder.encode(key, StandardCharsets.UTF_8) +
  291. "&sign=" + sign;
  292. }
  293. public JSONObject query(String orderId) {
  294. orderId = paddingOrderId(orderId);
  295. JSONObject header = new JSONObject();
  296. header.put("version", "1.0"); //版本号
  297. header.put("method", "sandpay.trade.query"); //接口名称:订单查询
  298. header.put("productId", "00000006"); //产品编码
  299. header.put("mid", sandPayProperties.getMid()); //商户号
  300. header.put("accessType", "1"); //接入类型设置为普通商户接入
  301. header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端
  302. header.put("reqTime", getReqTime()); //请求时间
  303. JSONObject body = new JSONObject();
  304. body.put("orderCode", orderId);
  305. body.put("extend", ""); //扩展域
  306. return requestServer(header, body, "https://cashier.sandpay.com.cn/qr/api/order/query");
  307. }
  308. public JSONObject refund(String orderId, BigDecimal amount) {
  309. orderId = paddingOrderId(orderId);
  310. JSONObject header = new JSONObject();
  311. header.put("version", "1.0"); //版本号
  312. header.put("method", "sandpay.trade.refund"); //接口名称:退货
  313. header.put("productId", "00000006"); //产品编码
  314. header.put("mid", sandPayProperties.getMid()); //商户号
  315. header.put("accessType", "1"); //接入类型设置为普通商户接入
  316. header.put("channelType", "07"); //渠道类型:07-互联网 08-移动端
  317. header.put("reqTime", getReqTime()); //请求时间
  318. JSONObject body = new JSONObject();
  319. body.put("orderCode", snowflakeIdWorker.nextId()); //商户订单号
  320. body.put("oriOrderCode", paddingOrderId(orderId)); //原交易订单号
  321. body.put("refundAmount", convertAmount(amount)); //退货金额
  322. body.put("refundReason", "退款"); //退货原因
  323. body.put("notifyUrl", sandPayProperties.getNotifyUrl()); //异步通知地址
  324. body.put("extend", "");
  325. return requestServer(header, body, "https://cashier.sandpay.com.cn/qr/api/order/refund");
  326. }
  327. public JSONObject transfer(String id, String name, String bank, BigDecimal amount, String phone) {
  328. JSONObject request = new JSONObject();
  329. DecimalFormat df = new DecimalFormat("000000000000", DecimalFormatSymbols.getInstance(Locale.US));
  330. request.put("version", "01"); //版本号
  331. request.put("productId", "00000004"); //产品ID
  332. request.put("tranTime", getReqTime()); //交易时间
  333. request.put("orderCode", id); //订单号
  334. request.put("timeOut", getTimeout(180)); //订单超时时间
  335. request.put("tranAmt", df.format(amount.multiply(new BigDecimal("100")))); //金额
  336. request.put("currencyCode", "156"); //币种
  337. request.put("accAttr", "0"); //账户属性 0-对私 1-对公
  338. request.put("accType", "4"); //账号类型 3-公司账户 4-银行卡
  339. request.put("accNo", bank); //收款人账户号
  340. request.put("accName", name); //收款人账户名
  341. request.put("provNo", ""); //收款人开户省份编码
  342. request.put("cityNo", ""); //收款人开会城市编码
  343. request.put("bankName", ""); //收款账户开户行名称
  344. request.put("bankType", ""); //收款人账户联行号
  345. request.put("remark", "消费"); //摘要
  346. request.put("payMode", ""); //付款模式
  347. request.put("channelType", ""); //渠道类型
  348. request.put("extendParams", ""); //业务扩展参数
  349. request.put("reqReserved", ""); //请求方保留域
  350. request.put("extend", ""); //扩展域
  351. request.put("phone", phone); //手机号
  352. String reqData = request.toJSONString();
  353. log.info("请求数据:{}", reqData);
  354. try {
  355. String aesKey = RandomStringGenerator.getRandomStringByLength(16);
  356. byte[] aesKeyBytes = aesKey.getBytes(StandardCharsets.UTF_8);
  357. byte[] plainBytes = reqData.getBytes(StandardCharsets.UTF_8);
  358. String encryptData = new String(Base64.encodeBase64(
  359. CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES",
  360. "AES/ECB/PKCS5Padding", null)), StandardCharsets.UTF_8);
  361. String sign = new String(Base64.encodeBase64(
  362. CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(),
  363. "SHA1WithRSA")), StandardCharsets.UTF_8);
  364. String encryptKey = new String(Base64.encodeBase64(
  365. CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11,
  366. "RSA/ECB/PKCS1Padding")), StandardCharsets.UTF_8);
  367. Map<String, String> reqMap = new HashMap<String, String>();
  368. //整体报文格式
  369. reqMap.put("transCode", "RTPM"); // 交易码
  370. reqMap.put("accessType", "0"); // 接入类型
  371. reqMap.put("merId", sandPayProperties.getMid()); // 合作商户ID 杉德系统分配,唯一标识
  372. reqMap.put("encryptKey", encryptKey); // 加密后的AES秘钥
  373. reqMap.put("encryptData", encryptData); // 加密后的请求/应答报文
  374. reqMap.put("sign", sign); // 签名
  375. reqMap.put("extend", ""); // 扩展域
  376. String result;
  377. try {
  378. log.info("请求报文:{}", reqMap);
  379. result = HttpClient.doPost("https://caspay.sandpay.com.cn/agent-main/openapi/agentpay",
  380. reqMap, 300000, 300000);
  381. result = URLDecoder.decode(result, StandardCharsets.UTF_8);
  382. } catch (IOException e) {
  383. log.error(e.getMessage());
  384. return null;
  385. }
  386. log.info("响应报文:{}", result);
  387. Map<String, String> responseMap = SDKUtil.convertResultStringToMap(result);
  388. String retEncryptKey = responseMap.get("encryptKey");
  389. String retEncryptData = responseMap.get("encryptData");
  390. String retSign = responseMap.get("sign");
  391. log.debug("retEncryptKey:[{}]", retEncryptKey);
  392. log.debug("retEncryptData:[{}]", retEncryptData);
  393. log.debug("retSign:[{}]", retSign);
  394. byte[] decodeBase64KeyBytes = Base64.decodeBase64(retEncryptKey
  395. .getBytes(StandardCharsets.UTF_8));
  396. byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(
  397. decodeBase64KeyBytes, CertUtil.getPrivateKey(), 2048, 11,
  398. "RSA/ECB/PKCS1Padding");
  399. byte[] decodeBase64DataBytes = Base64.decodeBase64(retEncryptData.getBytes(StandardCharsets.UTF_8));
  400. byte[] respDataBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes,
  401. merchantAESKeyBytes, "AES", "AES/ECB/PKCS5Padding", null);
  402. String respData = new String(respDataBytes, StandardCharsets.UTF_8);
  403. log.info("retData:[" + respData + "]");
  404. System.out.println("响应data数据:" + respData);
  405. byte[] signBytes = Base64.decodeBase64(retSign.getBytes(StandardCharsets.UTF_8));
  406. boolean isValid = CryptoUtil.verifyDigitalSign(respDataBytes, signBytes,
  407. CertUtil.getPublicKey(), "SHA1WithRSA");
  408. if (!isValid) {
  409. log.error("verify sign fail.");
  410. return null;
  411. }
  412. log.info("verify sign success");
  413. System.out.println("verify sign success");
  414. JSONObject respJson = JSONObject.parseObject(respData);
  415. return respJson;
  416. } catch (Exception e) {
  417. log.error(e.getMessage());
  418. return null;
  419. }
  420. }
  421. public JSONObject queryTransfer(String tranTime, String orderId) {
  422. JSONObject request = new JSONObject();
  423. request.put("version", "01"); // 版本号
  424. request.put("productId", "00000004"); // 产品ID
  425. request.put("tranTime", tranTime); // 查询订单的交易时间
  426. request.put("orderCode", orderId); // 要查询的订单号
  427. String reqData = request.toJSONString();
  428. log.info("请求数据:{}", reqData);
  429. try {
  430. String aesKey = RandomStringGenerator.getRandomStringByLength(16);
  431. byte[] aesKeyBytes = aesKey.getBytes(StandardCharsets.UTF_8);
  432. byte[] plainBytes = reqData.getBytes(StandardCharsets.UTF_8);
  433. String encryptData = new String(Base64.encodeBase64(
  434. CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES",
  435. "AES/ECB/PKCS5Padding", null)), StandardCharsets.UTF_8);
  436. String sign = new String(Base64.encodeBase64(
  437. CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(),
  438. "SHA1WithRSA")), StandardCharsets.UTF_8);
  439. String encryptKey = new String(Base64.encodeBase64(
  440. CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11,
  441. "RSA/ECB/PKCS1Padding")), StandardCharsets.UTF_8);
  442. Map<String, String> reqMap = new HashMap<String, String>();
  443. //整体报文格式
  444. reqMap.put("transCode", "ODQU"); // 交易码
  445. reqMap.put("accessType", "0"); // 接入类型
  446. reqMap.put("merId", sandPayProperties.getMid()); // 合作商户ID 杉德系统分配,唯一标识
  447. reqMap.put("plId", null);
  448. reqMap.put("encryptKey", encryptKey); // 加密后的AES秘钥
  449. reqMap.put("encryptData", encryptData); // 加密后的请求/应答报文
  450. reqMap.put("sign", sign); // 签名
  451. reqMap.put("extend", ""); // 扩展域
  452. String result;
  453. try {
  454. log.info("请求报文:{}", reqMap);
  455. result = HttpClient.doPost("https://caspay.sandpay.com.cn/agent-main/openapi/queryOrder",
  456. reqMap, 300000, 300000);
  457. result = URLDecoder.decode(result, StandardCharsets.UTF_8);
  458. } catch (IOException e) {
  459. log.error(e.getMessage());
  460. return null;
  461. }
  462. log.info("响应报文:{}", result);
  463. Map<String, String> responseMap = SDKUtil.convertResultStringToMap(result);
  464. String retEncryptKey = responseMap.get("encryptKey");
  465. String retEncryptData = responseMap.get("encryptData");
  466. String retSign = responseMap.get("sign");
  467. log.debug("retEncryptKey:[{}]", retEncryptKey);
  468. log.debug("retEncryptData:[{}]", retEncryptData);
  469. log.debug("retSign:[{}]", retSign);
  470. byte[] decodeBase64KeyBytes = Base64.decodeBase64(retEncryptKey
  471. .getBytes(StandardCharsets.UTF_8));
  472. byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(
  473. decodeBase64KeyBytes, CertUtil.getPrivateKey(), 2048, 11,
  474. "RSA/ECB/PKCS1Padding");
  475. byte[] decodeBase64DataBytes = Base64.decodeBase64(retEncryptData.getBytes(StandardCharsets.UTF_8));
  476. byte[] respDataBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes,
  477. merchantAESKeyBytes, "AES", "AES/ECB/PKCS5Padding", null);
  478. String respData = new String(respDataBytes, StandardCharsets.UTF_8);
  479. log.info("retData:[" + respData + "]");
  480. System.out.println("响应data数据:" + respData);
  481. byte[] signBytes = Base64.decodeBase64(retSign.getBytes(StandardCharsets.UTF_8));
  482. boolean isValid = CryptoUtil.verifyDigitalSign(respDataBytes, signBytes,
  483. CertUtil.getPublicKey(), "SHA1WithRSA");
  484. if (!isValid) {
  485. log.error("verify sign fail.");
  486. return null;
  487. }
  488. log.info("verify sign success");
  489. System.out.println("verify sign success");
  490. JSONObject respJson = JSONObject.parseObject(respData);
  491. return respJson;
  492. } catch (Exception e) {
  493. log.error(e.getMessage());
  494. return null;
  495. }
  496. }
  497. public PayQuery payQuery(String orderId) {
  498. JSONObject res = query(orderId);
  499. PayQuery query = new PayQuery(Constants.PayChannel.SAND);
  500. String respCode = res.getJSONObject("head").getString("respCode");
  501. if ("000000".equals(respCode)) {
  502. query.setExist(true);
  503. JSONObject body = res.getJSONObject("body");
  504. query.setMsg(body.getString("orderMsg") + " " + body.getString("oriRespMsg"));
  505. query.setAmount(new BigDecimal(body.getString("totalAmount"))
  506. .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
  507. query.setTransactionId(body.getString("payOrderCode"));
  508. if (StringUtils.isNotEmpty(body.getString("payTime"))) {
  509. query.setPayTime(LocalDateTime.parse(body.getString("payTime"), DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
  510. }
  511. if (StringUtils.isNotEmpty(body.getString("oriOrderCode"))) {
  512. query.setOrderId(body.getString("oriOrderCode"));
  513. }
  514. switch (body.getString("orderStatus")) {
  515. case "00":
  516. query.setStatus(PayStatus.SUCCESS);
  517. break;
  518. case "01":
  519. query.setStatus(PayStatus.PENDING);
  520. break;
  521. case "02":
  522. query.setStatus(PayStatus.FAIL);
  523. break;
  524. case "03":
  525. query.setStatus(PayStatus.CANCEL);
  526. break;
  527. case "04":
  528. query.setStatus(PayStatus.REFUNDED);
  529. break;
  530. case "05":
  531. query.setStatus(PayStatus.REFUNDING);
  532. break;
  533. }
  534. } else {
  535. query.setExist(false);
  536. query.setMsg(res.getString("msg"));
  537. }
  538. return query;
  539. }
  540. public BigDecimal balance() {
  541. JSONObject request = new JSONObject();
  542. request.put("version", "01"); //版本号
  543. request.put("productId", "00000004"); //产品ID
  544. request.put("tranTime", getReqTime()); //交易时间
  545. request.put("orderCode", snowflakeIdWorker.nextId()); //订单号
  546. String reqData = request.toJSONString();
  547. log.info("请求数据:{}", reqData);
  548. try {
  549. String aesKey = RandomStringGenerator.getRandomStringByLength(16);
  550. byte[] aesKeyBytes = aesKey.getBytes(StandardCharsets.UTF_8);
  551. byte[] plainBytes = reqData.getBytes(StandardCharsets.UTF_8);
  552. String encryptData = new String(Base64.encodeBase64(
  553. CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES",
  554. "AES/ECB/PKCS5Padding", null)), StandardCharsets.UTF_8);
  555. String sign = new String(Base64.encodeBase64(
  556. CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(),
  557. "SHA1WithRSA")), StandardCharsets.UTF_8);
  558. String encryptKey = new String(Base64.encodeBase64(
  559. CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11,
  560. "RSA/ECB/PKCS1Padding")), StandardCharsets.UTF_8);
  561. Map<String, String> reqMap = new HashMap<String, String>();
  562. //整体报文格式
  563. reqMap.put("transCode", "MBQU"); // 交易码
  564. reqMap.put("accessType", "0"); // 接入类型
  565. reqMap.put("merId", sandPayProperties.getMid()); // 合作商户ID 杉德系统分配,唯一标识
  566. reqMap.put("encryptKey", encryptKey); // 加密后的AES秘钥
  567. reqMap.put("encryptData", encryptData); // 加密后的请求/应答报文
  568. reqMap.put("sign", sign); // 签名
  569. reqMap.put("extend", ""); // 扩展域
  570. String result;
  571. try {
  572. log.info("请求报文:{}", reqMap);
  573. result = HttpClient.doPost("https://caspay.sandpay.com.cn/agent-main/openapi/queryBalance",
  574. reqMap, 300000, 300000);
  575. result = URLDecoder.decode(result, StandardCharsets.UTF_8);
  576. } catch (IOException e) {
  577. log.error(e.getMessage());
  578. return null;
  579. }
  580. log.info("响应报文:{}", result);
  581. Map<String, String> responseMap = SDKUtil.convertResultStringToMap(result);
  582. String retEncryptKey = responseMap.get("encryptKey");
  583. String retEncryptData = responseMap.get("encryptData");
  584. String retSign = responseMap.get("sign");
  585. log.debug("retEncryptKey:[{}]", retEncryptKey);
  586. log.debug("retEncryptData:[{}]", retEncryptData);
  587. log.debug("retSign:[{}]", retSign);
  588. byte[] decodeBase64KeyBytes = Base64.decodeBase64(retEncryptKey
  589. .getBytes(StandardCharsets.UTF_8));
  590. byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(
  591. decodeBase64KeyBytes, CertUtil.getPrivateKey(), 2048, 11,
  592. "RSA/ECB/PKCS1Padding");
  593. byte[] decodeBase64DataBytes = Base64.decodeBase64(retEncryptData.getBytes(StandardCharsets.UTF_8));
  594. byte[] respDataBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes,
  595. merchantAESKeyBytes, "AES", "AES/ECB/PKCS5Padding", null);
  596. String respData = new String(respDataBytes, StandardCharsets.UTF_8);
  597. log.info("retData:[" + respData + "]");
  598. System.out.println("响应data数据:" + respData);
  599. byte[] signBytes = Base64.decodeBase64(retSign.getBytes(StandardCharsets.UTF_8));
  600. boolean isValid = CryptoUtil.verifyDigitalSign(respDataBytes, signBytes,
  601. CertUtil.getPublicKey(), "SHA1WithRSA");
  602. if (!isValid) {
  603. log.error("verify sign fail.");
  604. return null;
  605. }
  606. log.info("verify sign success");
  607. System.out.println("verify sign success");
  608. JSONObject respJson = JSONObject.parseObject(respData);
  609. return respJson.getBigDecimal("balance").divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  610. } catch (Exception e) {
  611. log.error(e.getMessage());
  612. return null;
  613. }
  614. }
  615. }