|
|
@@ -0,0 +1,174 @@
|
|
|
+package com.kxs.common.cash.service.hs;
|
|
|
+
|
|
|
+import cn.hutool.core.bean.BeanUtil;
|
|
|
+import cn.hutool.core.date.DatePattern;
|
|
|
+import cn.hutool.core.date.LocalDateTimeUtil;
|
|
|
+import cn.hutool.core.lang.TypeReference;
|
|
|
+import cn.hutool.core.map.MapBuilder;
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import cn.hutool.http.HttpRequest;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.kxs.common.cash.handlers.CashMessageHandler;
|
|
|
+import com.kxs.common.cash.model.HsCashBackMessage;
|
|
|
+import com.kxs.common.cash.model.HsCashPayRes;
|
|
|
+import com.kxs.common.cash.model.HsCashRequest;
|
|
|
+import com.kxs.common.cash.model.params.hs.HsIsvParams;
|
|
|
+import com.kxs.common.cash.model.common.CashRequest;
|
|
|
+import com.kxs.common.cash.model.common.HsCashCommRes;
|
|
|
+import com.kxs.common.cash.model.params.IsvParams;
|
|
|
+import com.kxs.common.cash.service.CashService;
|
|
|
+import com.kxs.common.cash.service.ConfigContextService;
|
|
|
+import com.kxs.common.core.exception.GlobalCustomerException;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import jakarta.validation.Valid;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.util.StreamUtils;
|
|
|
+
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Base64;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 慧算提现服务
|
|
|
+ *
|
|
|
+ * @author 没秃顶的码农
|
|
|
+ * @date 2024-07-23
|
|
|
+ */
|
|
|
+@RequiredArgsConstructor
|
|
|
+@Slf4j
|
|
|
+public class HsCashService implements CashService {
|
|
|
+
|
|
|
+ private final ConfigContextService configContextService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Boolean cash(CashRequest cashReq) {
|
|
|
+
|
|
|
+ IsvParams isvParams = configContextService.getParams(cashReq.getIfCode());
|
|
|
+ if (isvParams instanceof HsIsvParams hsIsvParams && cashReq instanceof HsCashRequest hsCashRequest) {
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ String response = send(hsIsvParams, hsCashRequest);
|
|
|
+ HsCashCommRes<HsCashPayRes> commRes = JSONUtil.toBean(response, new TypeReference<>() {}, true);
|
|
|
+ if(commRes.getCode() != 0){
|
|
|
+ throw new RuntimeException(commRes.getErrorDesc());
|
|
|
+ }
|
|
|
+ return Boolean.TRUE;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("发起提现任务失败{}", JSON.toJSONString(hsCashRequest), e);
|
|
|
+ throw new GlobalCustomerException("发起提现任务失败,请联系管理员");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String cashBack(HttpServletRequest request, String ifCode) {
|
|
|
+
|
|
|
+ IsvParams isvParams = configContextService.getParams(ifCode);
|
|
|
+ if (isvParams instanceof HsIsvParams hsIsvParams) {
|
|
|
+
|
|
|
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
|
|
+
|
|
|
+ StreamUtils.copy(request.getInputStream(), baos);
|
|
|
+ String bodyStr = baos.toString();
|
|
|
+ HsCashBackMessage cashBackMessage = JSON.parseObject(bodyStr, HsCashBackMessage.class);
|
|
|
+ String timestamp = request.getHeader("X-Timestamp");
|
|
|
+ //验证签名
|
|
|
+ if (!checkSign(cashBackMessage, hsIsvParams, timestamp)) {
|
|
|
+ throw new GlobalCustomerException("签名错误");
|
|
|
+ }
|
|
|
+ //获取处理的handler方法
|
|
|
+ CashMessageHandler cashMessageHandler = getCashMessageHandler(ifCode);
|
|
|
+
|
|
|
+ return cashMessageHandler.handle(cashBackMessage);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new GlobalCustomerException("慧算回调地址获取参数异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return "{" +
|
|
|
+ " \"code\": 0\n" +
|
|
|
+ "}";
|
|
|
+ }
|
|
|
+
|
|
|
+ private String send(HsIsvParams hsIsvParams, HsCashRequest req) throws Exception {
|
|
|
+
|
|
|
+ Map<String, Object> requestParam = MapBuilder
|
|
|
+ .create(new HashMap<String, Object>())
|
|
|
+ .put("amount", req.getAmount())
|
|
|
+ .put("acctName", req.getAcctName())
|
|
|
+ .put("acctNo", req.getAcctNo())
|
|
|
+ .put("acctIdCard", req.getAcctIdCard())
|
|
|
+ .put("outOrderNo", req.getOutOrderNo())
|
|
|
+ .put("bankName", req.getBankName())
|
|
|
+ .put("callbackUrl", hsIsvParams.getCallbackUrl())
|
|
|
+ .put("acctMobile", req.getAcctMobile()).map();
|
|
|
+
|
|
|
+ String timestamp = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN);
|
|
|
+ // 计算签名
|
|
|
+ String requestJsonStr = generateRequestBody(requestParam);
|
|
|
+ String waitSignStr = timestamp + hsIsvParams.getSecret() + requestJsonStr;
|
|
|
+ String sign = generateSignature(waitSignStr, hsIsvParams.getSignType());
|
|
|
+
|
|
|
+ return HttpRequest.post(hsIsvParams.getUrl())
|
|
|
+ .header("X-Up-AppId", hsIsvParams.getAppId())
|
|
|
+ .header("X-Timestamp", timestamp)
|
|
|
+ .header("X-Sign-Type", hsIsvParams.getSignType())
|
|
|
+ .header("X-Sign", sign)
|
|
|
+ .body(JSON.toJSONString(requestParam))
|
|
|
+ .timeout(50000)//超时,毫秒
|
|
|
+ .execute().body();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证签名
|
|
|
+ *
|
|
|
+ * @param cashBackMessage 回调参数
|
|
|
+ * @param hsIsvParams isv参数
|
|
|
+ * @param timestamp 时间
|
|
|
+ * @return {@link Boolean}
|
|
|
+ */
|
|
|
+ private Boolean checkSign(HsCashBackMessage cashBackMessage, HsIsvParams hsIsvParams, String timestamp) throws Exception {
|
|
|
+ String sign = cashBackMessage.getSign();
|
|
|
+ Map<String, Object> requestParam = BeanUtil.beanToMap(cashBackMessage);
|
|
|
+ requestParam.remove("sign");
|
|
|
+
|
|
|
+ // 计算签名
|
|
|
+ String requestJsonStr = generateRequestBody(requestParam);
|
|
|
+ String waitSignStr = timestamp + hsIsvParams.getSecret() + requestJsonStr;
|
|
|
+ String checkSign = generateSignature(waitSignStr, hsIsvParams.getSignType());
|
|
|
+
|
|
|
+ return ObjectUtil.equals(sign, checkSign);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateRequestBody(Map<String, Object> requestParam) throws Exception {
|
|
|
+ ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+
|
|
|
+ return objectMapper.writeValueAsString(requestParam);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成签名
|
|
|
+ *
|
|
|
+ */
|
|
|
+ private String generateSignature(String waitSignStr, String signType) throws Exception {
|
|
|
+ MessageDigest messageDigest = MessageDigest.getInstance(signType);
|
|
|
+ messageDigest.reset();
|
|
|
+ messageDigest.update(waitSignStr.getBytes(StandardCharsets.UTF_8));
|
|
|
+ byte[] signBytes = messageDigest.digest();
|
|
|
+ return Base64.getEncoder().encodeToString(signBytes);
|
|
|
+ }
|
|
|
+}
|