Эх сурвалжийг харах

印度支付razorpay业务

nelson 2 сар өмнө
parent
commit
0930c0b022

+ 12 - 0
sikey-pay-api/src/main/java/cn/sikey/pay/api/razorpay/RazorpayApi.java

@@ -0,0 +1,12 @@
+package cn.sikey.pay.api.razorpay;
+
+
+import cn.sikey.pay.enums.ApiConstants;
+import org.springframework.cloud.openfeign.FeignClient;
+
+@FeignClient(name = ApiConstants.NAME)
+public interface RazorpayApi {
+
+    String PREFIX_ALIPAY = ApiConstants.PREFIX_ALIPAY;
+
+}

+ 23 - 0
sikey-pay-api/src/main/java/cn/sikey/pay/api/razorpay/dto/RazorpayTradeDTO.java

@@ -0,0 +1,23 @@
+package cn.sikey.pay.api.razorpay.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @Author: qin
+ * @Date: 2025/3/31
+ * @Description: 交易基础
+ */
+// @EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class RazorpayTradeDTO {
+    // 有效期(1:1年2:终身)
+    private Integer validity;
+    // 设备唯一标识
+    private String deviceId;
+    // 商户项目应用
+    private String merchantProjectApplication;
+}

+ 0 - 0
sikey-pay-api/src/main/java/cn/sikey/pay/api/razorpay/test.json


+ 4 - 6
sikey-pay-api/src/main/java/cn/sikey/pay/enums/ApiConstants.java

@@ -1,12 +1,10 @@
 package cn.sikey.pay.enums;
 
 
-import cn.sikey.framework.common.enums.RpcConstants;
-
 /**
  * API 相关的枚举
  *
- * @author qin
+ * @author nelson
  */
 public class ApiConstants {
 
@@ -17,11 +15,11 @@ public class ApiConstants {
      */
     public static final String NAME = "sikey-pay";
 
-  //  public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/alipay";
+    //  public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/alipay";
 
-    public static final String PREFIX_ALIPAY =  "/alipay";
+    public static final String PREFIX_ALIPAY = "/alipay";
 
-    public static final String PREFIX_WECHAT =  "/wechat";
+    public static final String PREFIX_WECHAT = "/wechat";
 
     public static final String VERSION = "1.0.0";
 

+ 1 - 1
sikey-pay-biz/pom.xml

@@ -31,7 +31,7 @@
 
         <dependency>
             <groupId>cn.sikey.cloud</groupId>
-            <artifactId>sikey-order-api</artifactId>
+            <artifactId>sikey-order-business-api</artifactId>
             <version>${revision}</version>
         </dependency>
 

+ 1 - 1
sikey-pay-biz/src/main/java/cn/sikey/pay/PayApplication.java

@@ -10,7 +10,7 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  * 项目的启动类
  * <p>
  *
- * @author qin
+ * @author nelson
  */
 @SpringBootApplication
 @EnableDiscoveryClient

+ 1 - 1
sikey-pay-biz/src/main/java/cn/sikey/pay/api/AlipayApiImpl.java

@@ -14,7 +14,7 @@ import java.util.Map;
 /**
  * 阿里支付 API 接口实现类
  *
- * @author qin
+ * @author nelson
  */
 @RestController // 提供 RESTful API 接口,给 Feign 调用
 @Validated

+ 15 - 0
sikey-pay-biz/src/main/java/cn/sikey/pay/api/RazorpayApiImpl.java

@@ -0,0 +1,15 @@
+package cn.sikey.pay.api;
+
+import cn.sikey.pay.api.razorpay.RazorpayApi;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Razorpay 支付 API 接口实现类
+ *
+ * @author nelson
+ */
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class RazorpayApiImpl implements RazorpayApi {
+}

+ 1 - 1
sikey-pay-biz/src/main/java/cn/sikey/pay/api/WechatApiImpl.java

@@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController;
 /**
  * 微信支付 API 接口实现类
  *
- * @author qin
+ * @author nelson
  */
 @RestController // 提供 RESTful API 接口,给 Feign 调用
 @Validated

+ 22 - 0
sikey-pay-biz/src/main/java/cn/sikey/pay/config/RazorpayConfig.java

@@ -0,0 +1,22 @@
+package cn.sikey.pay.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author: nelson
+ * @Date: 2025/3/31
+ * @Description: razorpay配置
+ */
+@Component
+@ConfigurationProperties(prefix = "razorpay")
+@Data
+public class RazorpayConfig {
+
+    private String keyId;
+
+    private String keySecret;
+
+    private String secret;
+}

+ 38 - 0
sikey-pay-biz/src/main/java/cn/sikey/pay/controller/app/razorpay/RazorpayPaymentController.java

@@ -0,0 +1,38 @@
+package cn.sikey.pay.controller.app.razorpay;
+
+import cn.sikey.pay.service.razorpay.RazorpayPaymentService;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+/**
+ * @Author: nelson
+ * @Date: 2025/3/31
+ * @Description: 网关
+ */
+@RestController
+@RequestMapping("/pay/razorpay")
+@Slf4j
+public class RazorpayPaymentController {
+
+    @Resource
+    private RazorpayPaymentService razorpayPaymentService;
+
+    /**
+     * 手机网站支付,回调
+     *
+     * @param request  参数
+     * @param response 参数
+     * @return
+     */
+    @PostMapping("/tradeWapPayGateway")
+    public String tradeWapPayGateway(HttpServletRequest request, HttpServletResponse response) {
+        return razorpayPaymentService.tradeWapPayGateway(request, response);
+    }
+
+}

+ 0 - 2
sikey-pay-biz/src/main/java/cn/sikey/pay/service/alipay/AlipayPaymentServiceImpl.java

@@ -157,8 +157,6 @@ public class AlipayPaymentServiceImpl extends AbstractPaymentService implements
      */
     @Override
     public String tradeWapPayGateway(HttpServletRequest request, HttpServletResponse response) {
-        // 幂等处理  分布式锁 TODO
-
         // 根据 商户唯一标识-项目名-应用类型 调用对应服务
         Map<String, String> paramsMap = MapUtil.convertRequestToMap(request);
         log.info("[手机网站支付-支付宝,回调]处理支付成功,支付信息:{}", paramsMap);

+ 14 - 0
sikey-pay-biz/src/main/java/cn/sikey/pay/service/razorpay/RazorpayPaymentService.java

@@ -0,0 +1,14 @@
+package cn.sikey.pay.service.razorpay;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * @Author: nelson
+ * @Date: 2025/3/31
+ * @Description: 支付服务
+ */
+public interface RazorpayPaymentService {
+
+    String tradeWapPayGateway(HttpServletRequest request, HttpServletResponse response);
+}

+ 122 - 0
sikey-pay-biz/src/main/java/cn/sikey/pay/service/razorpay/RazorpayPaymentServiceImpl.java

@@ -0,0 +1,122 @@
+package cn.sikey.pay.service.razorpay;
+
+import cn.hutool.core.util.StrUtil;
+import cn.sikey.framework.common.exception.ServiceException;
+import cn.sikey.framework.common.pojo.CommonResult;
+import cn.sikey.hmd.api.hmd.vo.payments.PaymentsRespVO;
+import cn.sikey.hmd.api.hmd.vo.plans.PlansRespVO;
+import cn.sikey.order.api.order.OrderApi;
+import cn.sikey.order.api.order.vo.GenerateOrderReqVO;
+import cn.sikey.pay.config.RazorpayConfig;
+import cn.sikey.pay.enums.PayStatusEnum;
+import cn.sikey.pay.service.AbstractPaymentService;
+import cn.sikey.pay.util.MapUtil;
+import cn.sikey.pay.util.VerifySignatureUtil;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpStatus;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @Author: nelson
+ * @Date: 2025/3/31
+ * @Description: razorpay
+ */
+@Service("razorpayPaymentService")
+@Slf4j
+public class RazorpayPaymentServiceImpl extends AbstractPaymentService implements RazorpayPaymentService {
+
+    @Resource
+    private OrderApi orderApi;
+
+    @Qualifier("myTaskExecutor")
+    @Autowired
+    private ThreadPoolTaskExecutor myTaskExecutor;
+
+    private static final String PAY_TYPE = "razorpay";
+
+    @Resource
+    private RazorpayConfig razorpayConfig;
+
+    /**
+     * 手机网站支付razorpay,回调
+     *
+     * @param request  参数
+     * @param response 参数
+     * @return
+     */
+    @Override
+    public String tradeWapPayGateway(HttpServletRequest request, HttpServletResponse response) {
+        // 根据 商户唯一标识-项目名-应用类型 调用对应服务
+        Map<String, String> paramsMap = MapUtil.convertRequestToMap(request);
+        log.info("[手机网站支付-razorpay,回调]处理支付成功,支付信息:{}", paramsMap);
+        String razorpayPaymentId = paramsMap.get("razorpay_payment_id");
+        String razorpayOrderId = paramsMap.get("razorpay_order_id");
+        String razorpaySignature = paramsMap.get("razorpay_signature");
+        if (StrUtil.isBlank(razorpayPaymentId) || StrUtil.isBlank(razorpayOrderId) || StrUtil.isBlank(razorpaySignature)) {
+            log.warn("[手机网站支付-razorpay,回调] 回调参数是空");
+            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "回调参数是空");
+        }
+
+        CommonResult<GenerateOrderReqVO> queryOrder = orderApi.queryOrder(razorpayOrderId);
+        if (!queryOrder.getCode().equals(HttpStatus.SC_OK)) {
+            log.warn("[手机网站支付-razorpay,回调]调用订单服务失败");
+            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "调用订单服务失败");
+        }
+
+        // 校验订单是否存在
+        GenerateOrderReqVO generateOrderReqVO = queryOrder.getData();
+        if (Objects.isNull(generateOrderReqVO)) {
+            log.warn("[手机网站支付-razorpay,回调]不存在该订单,订单号:{}", razorpayOrderId);
+            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "不存在该订单");
+        }
+
+        Long orderId = generateOrderReqVO.getId();
+
+        CommonResult<PaymentsRespVO> respVOCommonResult = paymentApi.queryPayments(orderId);
+        if (!respVOCommonResult.getCode().equals(HttpStatus.SC_OK)) {
+            log.warn("[手机网站支付-razorpay,回调]调用hmd付款交易服务查询失败,订单id:{}", orderId);
+            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "调用hmd付款交易服务查询失败");
+        }
+        PaymentsRespVO paymentsRespVO = respVOCommonResult.getData();
+
+        // 幂等处理
+        if (Objects.nonNull(paymentsRespVO) && Objects.nonNull(paymentsRespVO.getStatus())) {
+            if (paymentsRespVO.getStatus().equals(PayStatusEnum.SUCCESS.getValue())) {
+                log.warn("[手机网站支付-razorpay,回调]重复回调,订单号:{}", razorpayOrderId);
+                return "success";
+            }
+        }
+
+        boolean verifySignature = VerifySignatureUtil.verifySignature(razorpayOrderId, razorpayPaymentId, razorpaySignature, razorpayConfig.getSecret());
+        if (verifySignature) {
+            handlePaymentSuccess(generateOrderReqVO);
+            return "success";
+        }
+
+        return "failure";
+    }
+
+    /**
+     * 处理支付成功
+     *
+     * @param generateOrderReqVO 订单
+     * @return
+     */
+    private void handlePaymentSuccess(GenerateOrderReqVO generateOrderReqVO) {
+        String deviceId = generateOrderReqVO.getDeviceId();
+        PlansRespVO plansRespVO = super.queryPlans(generateOrderReqVO.getPlansId(), PAY_TYPE);
+        super.updateRemainingTimesExpireTime(plansRespVO.getValidity(), deviceId, PAY_TYPE);
+
+        log.info("[手机网站支付-razorpay,回调]处理支付成功");
+    }
+
+}

+ 0 - 31
sikey-pay-biz/src/main/java/cn/sikey/pay/util/AESUtil.java

@@ -1,31 +0,0 @@
-/*
-package cn.sikey.pay.util;
-
-import lombok.experimental.UtilityClass;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import java.nio.charset.StandardCharsets;
-import java.util.Base64;
-
-*/
-/**
- * @Author: qin
- * @Date: 2025/4/21
- * @Description: AES加密工具类
- *//*
-
-@UtilityClass
-public class AESUtil {
-    public static String encrypt(String content, String secret) throws Exception {
-        byte[] raw = secret.getBytes(StandardCharsets.UTF_8);
-        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
-        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-        IvParameterSpec iv = new IvParameterSpec(raw);
-        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
-        byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
-        return Base64.getEncoder().encodeToString(encrypted);
-    }
-}
-*/

+ 46 - 0
sikey-pay-biz/src/main/java/cn/sikey/pay/util/VerifySignatureUtil.java

@@ -0,0 +1,46 @@
+package cn.sikey.pay.util;
+
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Hex;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * @Author: nelson
+ * @Date: 2025/7/9
+ * @Description: 验证签名工具
+ */
+@Slf4j
+@UtilityClass
+public class VerifySignatureUtil {
+
+    /**
+     * 验证签名
+     *
+     * @param orderId           订单id
+     * @param razorpayPaymentId razorpay付款id
+     * @param razorpaySignature razorpay签名
+     * @param secret            秘钥
+     * @return
+     */
+    public static boolean verifySignature(String orderId, String razorpayPaymentId, String razorpaySignature, String secret) {
+        String payload = orderId + "|" + razorpayPaymentId;
+        try {
+            Mac sha256HMAC = Mac.getInstance("HmacSHA256");
+            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
+            sha256HMAC.init(secretKey);
+
+            byte[] bytes = sha256HMAC.doFinal(payload.getBytes());
+            String generatedSignature = Hex.encodeHexString(bytes);
+            return generatedSignature.equals(razorpaySignature);
+        } catch (Exception e) {
+            log.error("[验证签名]异常:{}", e);
+            return false;
+        }
+    }
+
+
+}
+

+ 7 - 0
sikey-pay-biz/src/main/resources/application.yml

@@ -96,6 +96,13 @@ sikey:
 tcmPulseDiagnosis:
   url: http://api.tecanswer.com/trade/api/v1/alipay/gateway
 
+---
+# razorpay配置
+razorpay:
+  keyId: rzp_test_bwL1qbvhAdCZqw
+  keySecret: bSIrco42uq0j6YBb43HbZ5Yn
+  secret: 2c98e1e2bb1aa00b15bc50f81dfcffc75b2e2ba7762a0c9626ec516f4855edcb74bd659c56fc369e32ef6503c6895bd6b6dfc6a5202ec181e0e54fc1c43a25c3
+
 ---
 #熔断器配置
 feign: