Răsfoiți Sursa

Merge branch 'develop-pay' into develop

* develop-pay:
  余额支付封装完成
  余额支付
  支付宝支付已调起支付
  支付模块封装
  支付接口封装到一半
  no message
  no message
mac 2 ani în urmă
părinte
comite
0b1fc0894f
56 a modificat fișierele cu 2033 adăugiri și 45 ștergeri
  1. 5 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/CommonConstants.java
  2. 1 1
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/exception/GlobalBizExceptionHandler.java
  3. 19 0
      kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/util/SysUtils.java
  4. 43 0
      kxs-common/kxs-common-pay/pom.xml
  5. 48 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/config/AliPayAccountConfig.java
  6. 96 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/config/PayConfiguration.java
  7. 60 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/config/WechatAccountConfig.java
  8. 25 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/enums/PayConstants.java
  9. 25 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/enums/PayPlatformEnum.java
  10. 33 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/enums/PayStatusCommEnum.java
  11. 39 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/factory/PaymentFactory.java
  12. 34 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/handlers/DefaultPayMessageHandler.java
  13. 36 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/handlers/PayEventHandlers.java
  14. 28 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/handlers/PayMessageHandler.java
  15. 136 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/AliPayMessage.java
  16. 40 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/PayMessage.java
  17. 44 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/PayRequest.java
  18. 26 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/PayResponse.java
  19. 26 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/QueryRequest.java
  20. 49 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/QueryResponse.java
  21. 50 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/YuePayMessage.java
  22. 58 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/PayService.java
  23. 145 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/ali/AliPayService.java
  24. 44 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/wechat/WxPayService.java
  25. 68 0
      kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/yue/YuePayService.java
  26. 1 0
      kxs-common/kxs-common-pay/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  27. 1 0
      kxs-common/pom.xml
  28. 52 0
      kxs-product/kxs-product-api/src/main/java/com/kxs/product/api/dto/kxsapp/shop/PayOrderDTO.java
  29. 4 0
      kxs-product/kxs-product-biz/pom.xml
  30. 1 0
      kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/constant/enums/ProductErrorTypeEnum.java
  31. 2 1
      kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/controller/kxsapp/ShopController.java
  32. 43 0
      kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/handlers/AliPayMessageHandler.java
  33. 2 1
      kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/service/KxsShopOrderService.java
  34. 104 26
      kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/service/impl/KxsShopOrderServiceImpl.java
  35. 1 2
      kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/service/impl/KxsTicketTransferServiceImpl.java
  36. 1 0
      kxs-user/kxs-user-api/pom.xml
  37. 39 0
      kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/dto/kxsapp/userAccount/PayRequestDTO.java
  38. 8 2
      kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/feign/RemoteKxsUserService.java
  39. 22 0
      kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/feign/RemoteYuePayService.java
  40. 12 0
      kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/feign/config/KxsUserFeignClientConfiguration.java
  41. 94 0
      kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/model/KxsChangeType.java
  42. 101 0
      kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/model/KxsChangeTypeKind.java
  43. 121 0
      kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/model/KxsUserYuepay.java
  44. 30 0
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/constant/enums/YuePayStatusEnum.java
  45. 13 1
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/controller/kxsapp/UserAccountController.java
  46. 17 0
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/mapper/KxsChangeTypeKindMapper.java
  47. 17 0
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/mapper/KxsChangeTypeMapper.java
  48. 17 0
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/mapper/KxsUserYuepayMapper.java
  49. 5 1
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/KxsUserAmountService.java
  50. 15 0
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/KxsUserYuepayService.java
  51. 52 10
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/impl/KxsUserAmountServiceImpl.java
  52. 19 0
      kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/impl/KxsUserYuepayServiceImpl.java
  53. 18 0
      kxs-user/kxs-user-biz/src/main/resources/mapper/KxsChangeTypeKindMapper.xml
  54. 17 0
      kxs-user/kxs-user-biz/src/main/resources/mapper/KxsChangeTypeMapper.xml
  55. 21 0
      kxs-user/kxs-user-biz/src/main/resources/mapper/KxsUserYuepayMapper.xml
  56. 5 0
      pom.xml

+ 5 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/constant/CommonConstants.java

@@ -91,6 +91,11 @@ public interface CommonConstants {
 	 */
 	String KXS_WITHDRAWAL_PREFIX = "BC";
 
+	/**
+	 * KXS提现订单号前缀
+	 */
+	String KXS_SHOP_PREFIX = "BM";
+
 	/**
 	 * KXS提现订单号前缀
 	 */

+ 1 - 1
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/exception/GlobalBizExceptionHandler.java

@@ -162,7 +162,7 @@ public class GlobalBizExceptionHandler {
 	}
 
 	/**
-	 * 参数绑定异常
+	 * 远程调用报错
 	 * @param exception 错误类型
 	 * @return {@link R}
 	 */

+ 19 - 0
kxs-common/kxs-common-core/src/main/java/com/kxs/common/core/util/SysUtils.java

@@ -3,7 +3,9 @@ package com.kxs.common.core.util;
 import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.date.LocalDateTimeUtil;
 
+import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -31,4 +33,21 @@ public class SysUtils {
         }
         return null;
     }
+
+    /**
+     * 交易流水号 ID
+     *
+     * @param id ID
+     * @return {@link String}
+     */
+    public static String generatePaymentId(Long id) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+        String timestamp = sdf.format(new Date());
+
+        // 生成一个6位随机数作为流水号的后缀
+        int randomNum = (int) (Math.random() * 1000000);
+        String paymentId = timestamp + String.format("%06d", randomNum);
+
+        return paymentId + id;
+    }
 }

+ 43 - 0
kxs-common/kxs-common-pay/pom.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.kxs</groupId>
+        <artifactId>kxs-common</artifactId>
+        <version>1.1.0</version>
+    </parent>
+
+    <artifactId>kxs-common-pay</artifactId>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>com.kxs</groupId>
+            <artifactId>kxs-common-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.kxs</groupId>
+            <artifactId>kxs-user-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-extra</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alipay.sdk</groupId>
+            <artifactId>alipay-sdk-java</artifactId>
+            <version>4.39.48.ALL</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.springboot</groupId>
+            <artifactId>best-pay-sdk</artifactId>
+            <version>1.3.7</version>
+        </dependency>
+    </dependencies>
+</project>

+ 48 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/config/AliPayAccountConfig.java

@@ -0,0 +1,48 @@
+package com.kxs.common.pay.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 支付宝账户配置
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+@ConfigurationProperties(prefix = "pay.alipay")
+@Component
+public class AliPayAccountConfig {
+    /**
+     * appId
+     */
+    private String appId;
+    /**
+     * 商户私钥
+     */
+    private String privateKey;
+    /**
+     * 支付宝公钥
+     */
+    private String aliPayPublicKey;
+    /**
+     * 异步通知url
+     */
+    private String notifyUrl;
+
+    /**
+     * 同步返回的url
+     */
+    private String returnUrl;
+
+    /**
+     * 是否使用沙箱
+     */
+    private Boolean sandbox;
+
+    /**
+     * 签名类型
+     */
+    private String signType = "RSA2";
+}

+ 96 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/config/PayConfiguration.java

@@ -0,0 +1,96 @@
+package com.kxs.common.pay.config;
+
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.AlipayConfig;
+import com.alipay.api.DefaultAlipayClient;
+import com.kxs.common.pay.factory.PaymentFactory;
+import com.kxs.common.pay.handlers.PayEventHandlers;
+import com.kxs.common.pay.service.ali.AliPayService;
+import com.kxs.common.pay.service.wechat.WxPayService;
+import com.kxs.common.pay.service.yue.YuePayService;
+import com.kxs.user.api.feign.RemoteKxsUserService;
+import com.kxs.user.api.feign.RemoteYuePayService;
+import com.lly835.bestpay.config.WxPayConfig;
+import lombok.AllArgsConstructor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 支付配置
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableConfigurationProperties({AliPayAccountConfig.class, WechatAccountConfig.class})
+@AllArgsConstructor
+public class PayConfiguration {
+
+    private final WechatAccountConfig wechatAccountConfig;
+
+    private final AliPayAccountConfig aliPayAccountConfig;
+
+    private final RemoteYuePayService remoteYuePayService;
+
+    @Bean
+    public WxPayConfig wxPayConfig() {
+        WxPayConfig wxPayConfig = new WxPayConfig();
+        wxPayConfig.setAppId(wechatAccountConfig.getMpAppId());
+        wxPayConfig.setMiniAppId(wechatAccountConfig.getMiniAppId());
+        wxPayConfig.setMchId(wechatAccountConfig.getMchId());
+        wxPayConfig.setMchKey(wechatAccountConfig.getMchKey());
+        wxPayConfig.setKeyPath(wechatAccountConfig.getKeyPath());
+        wxPayConfig.setNotifyUrl(wechatAccountConfig.getNotifyUrl());
+        wxPayConfig.setAppAppId(wechatAccountConfig.getAppAppId());
+        return wxPayConfig;
+    }
+
+    @Bean
+    public AlipayClient alipayClient() throws AlipayApiException {
+        AlipayConfig aliPayConfig = new AlipayConfig();
+        aliPayConfig.setAppId(aliPayAccountConfig.getAppId());
+        aliPayConfig.setPrivateKey(aliPayAccountConfig.getPrivateKey());
+        aliPayConfig.setSignType(aliPayAccountConfig.getSignType());
+        aliPayConfig.setAlipayPublicKey(aliPayAccountConfig.getAliPayPublicKey());
+        return new DefaultAlipayClient(aliPayConfig);
+    }
+
+
+    @Bean
+    @ConditionalOnMissingBean
+    public YuePayService yuePayService() {
+
+        return new YuePayService(remoteYuePayService);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public AliPayService aliPayService(AlipayClient alipayClient) {
+
+        return new AliPayService(alipayClient, aliPayAccountConfig);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public WxPayService wxPayService(WxPayConfig wxPayConfig) {
+
+        return new WxPayService(wxPayConfig, wechatAccountConfig);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public PaymentFactory paymentFactory(AliPayService aliPayService, WxPayService wxPayService, YuePayService yuePayService) {
+
+        return new PaymentFactory(aliPayService, wxPayService, yuePayService);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public PayEventHandlers payEventHandlers(PaymentFactory paymentFactory) {
+
+        return new PayEventHandlers(paymentFactory);
+    }
+}

+ 60 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/config/WechatAccountConfig.java

@@ -0,0 +1,60 @@
+package com.kxs.common.pay.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 微信支付配置
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+@ConfigurationProperties(prefix = "wechat")
+@Component
+public class WechatAccountConfig {
+    /**
+     * 公众账号appid
+     * 获取地址 https://mp.weixin.qq.com
+     */
+    private String mpAppId;
+
+    /**
+     * 小程序appId
+     * 获取地址 https://mp.weixin.qq.com
+     */
+    private String miniAppId;
+
+    /**
+     * 小程序appSecret
+     */
+    private String miniAppSecret;
+
+    /**
+     * 商户号
+     * 获取地址 https://pay.weixin.qq.com
+     */
+    private String mchId;
+
+    /**
+     * 商户密钥
+     */
+    private String mchKey;
+
+    /**
+     * 商户证书路径
+     */
+    private String keyPath;
+
+    /**
+     * 微信支付异步通知地址
+     */
+    private String notifyUrl;
+
+    /**
+     * app应用appid
+     * 获取地址 https://open.weixin.qq.com
+     */
+    private String appAppId;
+}

+ 25 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/enums/PayConstants.java

@@ -0,0 +1,25 @@
+package com.kxs.common.pay.enums;
+
+/**
+ * 支付常量
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+public interface PayConstants {
+
+    /**
+     * 余额支付成功
+     */
+    String YUE_PAY_SUCCESS = "success";
+
+    /**
+     * 阿里支付成功
+     */
+    String ALI_PAY_SUCCESS = "success";
+    /**
+     * 阿里支付失败
+     */
+    String ALI_PAY_FAIL = "fail";
+
+}

+ 25 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/enums/PayPlatformEnum.java

@@ -0,0 +1,25 @@
+package com.kxs.common.pay.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 支付平台枚举
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Getter
+@AllArgsConstructor
+public enum PayPlatformEnum {
+
+    ALIPAY("alipay", "支付宝"),
+    WX("wx", "微信"),
+    YUEPAY("yuepay", "余额支付");
+
+    //状态码
+    private final String type;
+    //状态码码描述
+    private final String description;
+
+}

+ 33 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/enums/PayStatusCommEnum.java

@@ -0,0 +1,33 @@
+package com.kxs.common.pay.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 支付状态
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Getter
+@AllArgsConstructor
+public enum PayStatusCommEnum {
+
+    /**
+     * 等待买家付款
+     */
+    WAIT_BUYER_PAY,
+    /**
+     * 未付款交易超时关闭,或支付完成后全额退款
+     */
+    TRADE_CLOSED,
+    /**
+     * 交易成功
+     */
+    TRADE_SUCCESS,
+    /**
+     * 交易完成
+     */
+    TRADE_FINISHED
+
+}

+ 39 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/factory/PaymentFactory.java

@@ -0,0 +1,39 @@
+package com.kxs.common.pay.factory;
+
+import com.kxs.common.core.exception.GlobalCustomerException;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.service.PayService;
+import com.kxs.common.pay.service.ali.AliPayService;
+import com.kxs.common.pay.service.wechat.WxPayService;
+import com.kxs.common.pay.service.yue.YuePayService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+/**
+ * 支付工厂类
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-26
+ */
+@Component
+@RequiredArgsConstructor
+public class PaymentFactory {
+
+    private final AliPayService aliPayService;
+
+    private final WxPayService wxPayService;
+
+    private final YuePayService yuePayService;
+
+    public PayService getPayService(String payMethod) {
+        if (payMethod.equalsIgnoreCase(PayPlatformEnum.ALIPAY.getType())) {
+            return aliPayService;
+        } else if (payMethod.equalsIgnoreCase(PayPlatformEnum.WX.getType())) {
+            return wxPayService;
+        } else if (payMethod.equalsIgnoreCase(PayPlatformEnum.YUEPAY.getType())) {
+            return yuePayService;
+        } else {
+            throw new GlobalCustomerException("Invalid payment method");
+        }
+    }
+}

+ 34 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/handlers/DefaultPayMessageHandler.java

@@ -0,0 +1,34 @@
+package com.kxs.common.pay.handlers;
+
+import com.alibaba.fastjson.JSON;
+import com.kxs.common.pay.enums.PayConstants;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.model.PayMessage;
+import com.kxs.common.pay.service.PayService;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Map;
+
+/**
+ * 默认支付回调处理程序
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@Slf4j
+public class DefaultPayMessageHandler implements PayMessageHandler<PayMessage, PayService> {
+
+    public DefaultPayMessageHandler() {
+    }
+
+    public String handle(PayMessage payMessage, PayService payService) {
+        log.info("回调支付消息处理器,回调消息:" + JSON.toJSONString(payMessage.getPayMessage()));
+
+        return PayConstants.ALI_PAY_SUCCESS;
+    }
+
+    @Override
+    public int getOrder() {
+        return Integer.MIN_VALUE;
+    }
+}

+ 36 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/handlers/PayEventHandlers.java

@@ -0,0 +1,36 @@
+package com.kxs.common.pay.handlers;
+
+import com.alipay.api.AlipayApiException;
+import com.kxs.common.pay.factory.PaymentFactory;
+import com.kxs.common.pay.service.PayService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 支付事件处理程序
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@RestController
+@RequestMapping("/pay")
+@RequiredArgsConstructor
+public class PayEventHandlers {
+
+    private final PaymentFactory paymentFactory;
+
+
+    @RequestMapping(value = "payBack/{platform}")
+    public String payBack(HttpServletRequest request, @PathVariable String platform) {
+        try {
+            PayService payService = paymentFactory.getPayService(platform);
+            return payService.payBack(request);
+        } catch (AlipayApiException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 28 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/handlers/PayMessageHandler.java

@@ -0,0 +1,28 @@
+package com.kxs.common.pay.handlers;
+
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.model.PayMessage;
+import com.kxs.common.pay.service.PayService;
+import org.springframework.core.Ordered;
+
+import java.util.Map;
+
+/**
+ * 支付消息处理程序
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+public interface PayMessageHandler<M extends PayMessage, S extends PayService>  extends Ordered {
+
+    String handle(M var1, S var2);
+
+    /**
+     * 是否支持此支付平台
+     *
+     * @return boolean
+     */
+    default boolean support(PayPlatformEnum payPlatformEnum) {
+        return true;
+    }
+}

+ 136 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/AliPayMessage.java

@@ -0,0 +1,136 @@
+package com.kxs.common.pay.model;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 支付宝回调消息
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Builder
+public class AliPayMessage extends PayMessage {
+    @JSONField(
+            name = "notify_time"
+    )
+    private Date notifyTime;
+    @JSONField(
+            name = "notify_type"
+    )
+    private String notifyType;
+    @JSONField(
+            name = "notify_id"
+    )
+    private String notifyId;
+    @JSONField(
+            name = "app_id"
+    )
+    private String appId;
+    private String charset;
+    private String version;
+    @JSONField(
+            name = "sign_type"
+    )
+    private String signType;
+    private String sign;
+    @JSONField(
+            name = "trade_no"
+    )
+    private String tradeNo;
+    @JSONField(
+            name = "out_trade_no"
+    )
+    private String outTradeNo;
+    @JSONField(
+            name = "out_biz_no"
+    )
+    private String outBizNo;
+    @JSONField(
+            name = "buyer_id"
+    )
+    private String buyerId;
+    @JSONField(
+            name = "buyer_logon_id"
+    )
+    private String buyerLogonId;
+    @JSONField(
+            name = "seller_id"
+    )
+    private String sellerId;
+    @JSONField(
+            name = "seller_email"
+    )
+    private String sellerEmail;
+    @JSONField(
+            name = "trade_status"
+    )
+    private String tradeStatus;
+    @JSONField(
+            name = "total_amount"
+    )
+    private BigDecimal totalAmount;
+    @JSONField(
+            name = "receipt_amount"
+    )
+    private BigDecimal receiptAmount;
+    @JSONField(
+            name = "invoice_amount"
+    )
+    private BigDecimal invoiceAmount;
+    @JSONField(
+            name = "buyer_pay_amount"
+    )
+    private BigDecimal buyerPayAmount;
+    @JSONField(
+            name = "point_amount"
+    )
+    private BigDecimal pointAmount;
+    @JSONField(
+            name = "refund_fee"
+    )
+    private BigDecimal refundFee;
+    @JSONField(
+            name = "subject"
+    )
+    private String subject;
+    @JSONField(
+            name = "body"
+    )
+    private String body;
+    @JSONField(
+            name = "gmt_create"
+    )
+    private Date gmtCreate;
+    @JSONField(
+            name = "gmt_payment"
+    )
+    private Date gmtPayment;
+    @JSONField(
+            name = "gmt_refund"
+    )
+    private Date gmtRefund;
+    @JSONField(
+            name = "gmt_close"
+    )
+    private Date gmtClose;
+    @JSONField(
+            name = "fund_bill_list"
+    )
+    private String fundBillList;
+    @JSONField(
+            name = "passback_params"
+    )
+    private String passbackParams;
+    @JSONField(
+            name = "voucher_detail_list"
+    )
+    private String voucherDetailList;
+}

+ 40 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/PayMessage.java

@@ -0,0 +1,40 @@
+package com.kxs.common.pay.model;
+
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 支付回调公共
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@Data
+public class PayMessage implements Serializable {
+
+    private Map<String, String> payMessage = null;
+
+    /**
+     * 交易渠道类型
+     */
+    private PayPlatformEnum payType;
+
+    /**
+     * 交易类型(自定义)
+     */
+    private String tradeType;
+
+    /**
+     * 来源订单
+     */
+    private String fromPay;
+
+    /**
+     * 备注
+     */
+    private String describe;
+}

+ 44 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/PayRequest.java

@@ -0,0 +1,44 @@
+package com.kxs.common.pay.model;
+
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import lombok.Data;
+
+/**
+ * 付款请求
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+public class PayRequest {
+
+    /**
+     * 支付类型枚举
+     */
+    private PayPlatformEnum payTypeEnum;
+
+    /**
+     * 商户网站唯一订单号
+     */
+    private String outTradeNo;
+
+    /**
+     * 订单总金额,单位为元,精确到小数点后两位
+     */
+    private Double totalAmount;
+
+    /**
+     * 订单标题
+     */
+    private String subject;
+
+    /**
+     * 订单包含的商品列表信息,json格式
+     */
+    private String goodsDetail;
+
+    /**
+     * 公用回传参数
+     */
+    private String passbackParams;
+}

+ 26 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/PayResponse.java

@@ -0,0 +1,26 @@
+package com.kxs.common.pay.model;
+
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 支付响应公共类
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+@Builder
+public class PayResponse {
+
+    /**
+     * 支付平台枚举
+     */
+    private PayPlatformEnum payPlatformEnum;
+
+    /**
+     * 支付返回结果
+     */
+    private String result;
+}

+ 26 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/QueryRequest.java

@@ -0,0 +1,26 @@
+package com.kxs.common.pay.model;
+
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import lombok.Data;
+
+/**
+ * 查询请求
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+public class QueryRequest {
+
+    /**
+     * 支付类型枚举
+     */
+    private PayPlatformEnum payTypeEnum;
+
+    /**
+     * 商户网站唯一订单号
+     */
+    private String outTradeNo;
+
+
+}

+ 49 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/QueryResponse.java

@@ -0,0 +1,49 @@
+package com.kxs.common.pay.model;
+
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.enums.PayStatusCommEnum;
+import lombok.Builder;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 查询响应公共类
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+@Builder
+public class QueryResponse {
+
+    /**
+     * 支付平台枚举
+     */
+    private PayPlatformEnum payPlatformEnum;
+
+    /**
+     * 商户网站唯一订单号
+     */
+    private String outTradeNo;
+
+    /**
+     * 支付宝交易号
+     */
+    private String tradeNo;
+
+    /**
+     * 买家支付宝账号
+     */
+    private String buyerLogonId;
+
+    /**
+     * 支付状态
+     */
+    private PayStatusCommEnum tradeStatus;
+
+    /**
+     * 交易的订单金额 元
+     */
+    private BigDecimal totalAmount;
+}

+ 50 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/model/YuePayMessage.java

@@ -0,0 +1,50 @@
+package com.kxs.common.pay.model;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 余额同步回调消息
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-28
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Builder
+public class YuePayMessage extends PayMessage {
+
+
+    /**
+     * 交易编号
+     */
+    private String tradeNo;
+
+    /**
+     * 交易订单号
+     */
+    private String outTradeNo;
+
+    /**
+     * 交易状态
+     */
+    private Integer tradeStatus;
+
+    /**
+     * 交易金额
+     */
+    private BigDecimal tradeAmount;
+
+    /**
+     * 订单标题
+     */
+    private String subject;
+
+    /**
+     * 回传参数
+     */
+    private String passbackParams;
+}

+ 58 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/PayService.java

@@ -0,0 +1,58 @@
+package com.kxs.common.pay.service;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.alipay.api.AlipayApiException;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.handlers.PayMessageHandler;
+import com.kxs.common.pay.model.PayRequest;
+import com.kxs.common.pay.model.PayResponse;
+import com.kxs.common.pay.model.QueryRequest;
+import com.kxs.common.pay.model.QueryResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.core.Ordered;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * 支付公共接口服务
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+public interface PayService {
+
+    /**
+     * 支付
+     */
+    PayResponse appPay(PayRequest payReq) throws AlipayApiException;
+
+    /**
+     * 查询
+     */
+    QueryResponse query(QueryRequest queryReq) throws AlipayApiException;
+
+    /**
+     * 回调
+     */
+    String payBack(HttpServletRequest request) throws AlipayApiException;
+
+    /**
+     * 获取支付回调handler
+     */
+    default PayMessageHandler getPayMessageHandler(PayPlatformEnum payPlatformEnum){
+        Map<String, PayMessageHandler> payMessageHandlerMap = SpringUtil
+                .getBeansOfType(PayMessageHandler.class);
+        Optional<PayMessageHandler> optional = payMessageHandlerMap.values()
+                .stream()
+                .filter(service -> service.support(payPlatformEnum))
+                .max(Comparator.comparingInt(Ordered::getOrder));
+
+        if (optional.isEmpty()) {
+            throw new GlobalCustomerException("DefaultPayMessageHandler error , not register");
+        }
+        return optional.get();
+    }
+}

+ 145 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/ali/AliPayService.java

@@ -0,0 +1,145 @@
+package com.kxs.common.pay.service.ali;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.domain.AlipayTradeQueryModel;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.alipay.api.request.AlipayTradeAppPayRequest;
+import com.alipay.api.request.AlipayTradeQueryRequest;
+import com.alipay.api.response.AlipayTradeAppPayResponse;
+import com.alipay.api.response.AlipayTradeQueryResponse;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import com.kxs.common.pay.config.AliPayAccountConfig;
+import com.kxs.common.pay.enums.PayConstants;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.enums.PayStatusCommEnum;
+import com.kxs.common.pay.handlers.PayMessageHandler;
+import com.kxs.common.pay.model.*;
+import com.kxs.common.pay.service.PayService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.Ordered;
+
+import java.time.LocalDateTime;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * 支付宝支付服务
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class AliPayService implements PayService {
+
+    private final AlipayClient alipayClient;
+
+    private final AliPayAccountConfig aliPayAccountConfig;
+
+    @Override
+    public PayResponse appPay(PayRequest payReq) throws AlipayApiException {
+
+        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
+        //回掉地址
+        request.setNotifyUrl(aliPayAccountConfig.getNotifyUrl());
+        JSONObject bizContent = new JSONObject();
+        //支付信息
+        bizContent.put("out_trade_no", payReq.getOutTradeNo());
+        bizContent.put("total_amount", payReq.getTotalAmount());
+        bizContent.put("subject", payReq.getSubject());
+        bizContent.put("product_code", "QUICK_MSECURITY_PAY");
+        bizContent.put("time_expire", LocalDateTimeUtil.format(LocalDateTime.now().plusMinutes(20), DatePattern.NORM_DATETIME_PATTERN));
+        //商品信息
+        if(StrUtil.isNotBlank(payReq.getGoodsDetail())){
+            bizContent.put("goods_detail", payReq.getPassbackParams());
+        }
+        //扩展参数
+        if(StrUtil.isNotBlank(payReq.getPassbackParams())){
+            bizContent.put("passback_params", payReq.getPassbackParams());
+        }
+        request.setBizContent(bizContent.toString());
+        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
+        String orderStr = response.getBody();
+        if(response.isSuccess()){
+            return PayResponse.builder()
+                    .result(orderStr)
+                    .payPlatformEnum(PayPlatformEnum.ALIPAY).build();
+        }
+        return PayResponse.builder().payPlatformEnum(PayPlatformEnum.ALIPAY).build();
+    }
+
+    @Override
+    public QueryResponse query(QueryRequest queryReq) throws AlipayApiException {
+        // 构造请求参数以调用接口
+        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
+
+        // 设置订单支付时传入的商户订单号
+        model.setOutTradeNo(queryReq.getOutTradeNo());
+
+        request.setBizModel(model);
+        AlipayTradeQueryResponse response = alipayClient.execute(request);
+        if (response.isSuccess()) {
+            return QueryResponse.builder()
+                    .outTradeNo(response.getOutTradeNo())
+                    .tradeNo(response.getTradeNo())
+                    .buyerLogonId(response.getBuyerLogonId())
+                    .tradeStatus(PayStatusCommEnum.valueOf(response.getTradeStatus()))
+                    .payPlatformEnum(PayPlatformEnum.ALIPAY).build();
+        }
+        return null;
+    }
+
+
+    @Override
+    public String payBack(HttpServletRequest request) throws AlipayApiException {
+
+        Map<String, String> noticeParams = getNoticeParams(request);
+
+        boolean signVerified = AlipaySignature.rsaCheckV2(noticeParams, aliPayAccountConfig.getAliPayPublicKey(), "UTF-8", aliPayAccountConfig.getSignType());
+        if (!signVerified) {
+            log.error("alipay notifies that signature verification failed");
+            return PayConstants.ALI_PAY_FAIL;
+        }
+        AliPayMessage aliPayMessage = BeanUtil.toBean(noticeParams, AliPayMessage.class);
+        aliPayMessage.setPayMessage(noticeParams);
+        aliPayMessage.setPayType(PayPlatformEnum.ALIPAY);
+        PayMessageHandler payMessageHandler = getPayMessageHandler(PayPlatformEnum.ALIPAY);
+        return payMessageHandler.handle(aliPayMessage, this);
+    }
+
+
+    /**
+     * 获取通知参数
+     *
+     * @param request 请求
+     * @return {@link Map}< {@link String} , {@link String}>
+     */
+    public Map< String , String> getNoticeParams(HttpServletRequest request) {
+        Map< String , String > params = new HashMap<>();
+        Map<String, String[]> requestParams = request.getParameterMap();
+        for (String name : requestParams.keySet()) {
+            String[] values = requestParams.get(name);
+            String valueStr = "";
+            for (int i = 0; i < values.length; i++) {
+                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
+            }
+            //乱码解决,这段代码在出现乱码时使用。
+            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
+            params.put(name, valueStr);
+        }
+        return params;
+    }
+
+}

+ 44 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/wechat/WxPayService.java

@@ -0,0 +1,44 @@
+package com.kxs.common.pay.service.wechat;
+
+import com.kxs.common.pay.config.WechatAccountConfig;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.handlers.PayMessageHandler;
+import com.kxs.common.pay.model.PayRequest;
+import com.kxs.common.pay.model.PayResponse;
+import com.kxs.common.pay.model.QueryRequest;
+import com.kxs.common.pay.model.QueryResponse;
+import com.kxs.common.pay.service.PayService;
+import com.lly835.bestpay.config.WxPayConfig;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 微信支付 服务
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@RequiredArgsConstructor
+public class WxPayService implements PayService {
+
+    private final WxPayConfig wxPayConfig;
+
+    private final WechatAccountConfig wechatAccountConfig;
+
+
+    @Override
+    public PayResponse appPay(PayRequest payReq) {
+        return null;
+    }
+
+    @Override
+    public QueryResponse query(QueryRequest queryReq) {
+        return null;
+    }
+
+    @Override
+    public String payBack(HttpServletRequest request) {
+        return null;
+    }
+
+}

+ 68 - 0
kxs-common/kxs-common-pay/src/main/java/com/kxs/common/pay/service/yue/YuePayService.java

@@ -0,0 +1,68 @@
+package com.kxs.common.pay.service.yue;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.alibaba.fastjson.JSON;
+import com.kxs.common.core.constant.enums.ErrorTypeEnum;
+import com.kxs.common.core.exception.GlobalCustomerException;
+import com.kxs.common.core.util.R;
+import com.kxs.common.core.util.RetOps;
+import com.kxs.common.pay.config.WechatAccountConfig;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.handlers.PayMessageHandler;
+import com.kxs.common.pay.model.*;
+import com.kxs.common.pay.service.PayService;
+import com.kxs.user.api.dto.kxsapp.userAccount.PayRequestDTO;
+import com.kxs.user.api.feign.RemoteKxsUserService;
+import com.kxs.user.api.feign.RemoteYuePayService;
+import com.kxs.user.api.model.KxsUser;
+import com.lly835.bestpay.config.WxPayConfig;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.core.Ordered;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * 余额支付 服务
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@RequiredArgsConstructor
+public class YuePayService implements PayService {
+
+    private final RemoteYuePayService remoteYuePayService;
+
+    @Override
+    public PayResponse appPay(PayRequest payReq) {
+
+        PayRequestDTO payRequestDTO = new PayRequestDTO();
+        BeanUtil.copyProperties(payReq, payRequestDTO);
+        R result = remoteYuePayService.yuePay(payRequestDTO);
+        if (result.getStatus() != 1) {
+            throw new GlobalCustomerException(result.getMsg());
+        }
+        YuePayMessage payMessage = BeanUtil.toBean(result.getData(), YuePayMessage.class);
+
+        Optional.ofNullable(payMessage).orElseThrow(() -> new GlobalCustomerException("yue pay is empty"));
+
+        return PayResponse.builder()
+                .payPlatformEnum(PayPlatformEnum.YUEPAY)
+                .result(JSON.toJSONString(payMessage))
+                .build();
+    }
+
+    @Override
+    public QueryResponse query(QueryRequest queryReq) {
+        return null;
+    }
+
+    @Override
+    public String payBack(HttpServletRequest request) {
+        return null;
+    }
+
+}

+ 1 - 0
kxs-common/kxs-common-pay/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -0,0 +1 @@
+com.kxs.common.pay.config.PayConfiguration

+ 1 - 0
kxs-common/pom.xml

@@ -24,6 +24,7 @@
         <module>kxs-common-swagger</module>
         <module>kxs-common-seata</module>
         <module>kxs-common-oss</module>
+        <module>kxs-common-pay</module>
         <module>kxs-common-mq</module>
         <module>kxs-common-idempotent</module>
     </modules>

+ 52 - 0
kxs-product/kxs-product-api/src/main/java/com/kxs/product/api/dto/kxsapp/shop/PayOrderDTO.java

@@ -0,0 +1,52 @@
+package com.kxs.product.api.dto.kxsapp.shop;
+
+import com.kxs.product.api.model.KxsShopOrder;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 订单提交
+ *
+ * @author Lxq
+ * @date 2023-12-20
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class PayOrderDTO extends KxsShopOrder {
+
+    /**
+     * 订单ID
+     */
+    @Schema(description = "订单ID")
+    @NotNull(message = "订单ID不能为空")
+    private Integer id;
+
+    /**
+     * 提货方式 0自提 1邮寄
+     */
+    @NotNull(message = "提货方式不能为空")
+    @Schema(description = "提货方式 0自提 1邮寄")
+    private Integer getMode;
+
+    /**
+     * 支付渠道 0余额 1支付宝 2微信 3盟主储蓄金
+     */
+    @NotNull(message = "支付渠道不能为空")
+    @Schema(description = "支付渠道 0余额 1支付宝 2微信 3盟主储蓄金")
+    private Integer payType;
+
+    /**
+     * 支付密码
+     */
+    @Schema(description = "支付密码")
+    private String payPwd;
+
+    /**
+     * 备注
+     */
+    @Schema(description = "备注")
+    private String remark;
+
+}

+ 4 - 0
kxs-product/kxs-product-biz/pom.xml

@@ -83,6 +83,10 @@
             <groupId>com.kxs</groupId>
             <artifactId>kxs-common-idempotent</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.kxs</groupId>
+            <artifactId>kxs-common-pay</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 1 - 0
kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/constant/enums/ProductErrorTypeEnum.java

@@ -15,6 +15,7 @@ public enum ProductErrorTypeEnum {
 
 	ORDER_EMPTY(-1, "订单不存在"),
 	ORDER_NO_CANCEL(-1, "订单已不能取消"),
+	ORDER_STATUS_ERROR(-1, "订单状态错误"),
 	ORDER_NO_CONFIRM(-1, "订单已不能收货"),
 	USER_IS_NOT_FIND(-1, "划拨对象不在您的权限范围"),
 	USER_IS_NOT_AUTH(-1, "该创客未实名认证"),

+ 2 - 1
kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/controller/kxsapp/ShopController.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.kxs.common.core.util.R;
 import com.kxs.common.security.annotation.Inner;
+import com.kxs.product.api.dto.kxsapp.shop.PayOrderDTO;
 import com.kxs.product.api.model.KxsShopOrder;
 import com.kxs.product.api.dto.kxsapp.shop.GoodsSubmitDTO;
 import com.kxs.product.api.vo.kxsapp.shop.ShopGoodsListVO;
@@ -138,7 +139,7 @@ public class ShopController {
      * @return 发起支付
      */
     @PostMapping("/orderPay")
-    public R orderPay(@RequestBody KxsShopOrder param) {
+    public R orderPay(@RequestBody PayOrderDTO param) {
 
         return kxsShopOrderService.payOrder(param);
     }

+ 43 - 0
kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/handlers/AliPayMessageHandler.java

@@ -0,0 +1,43 @@
+package com.kxs.product.biz.handlers;
+
+import com.kxs.common.pay.enums.PayConstants;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.handlers.PayMessageHandler;
+import com.kxs.common.pay.model.AliPayMessage;
+import com.kxs.common.pay.service.ali.AliPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 支付宝支付回调处理器
+ * @author mac
+ */
+@Component
+@Slf4j
+public class AliPayMessageHandler implements PayMessageHandler<AliPayMessage, AliPayService> {
+
+
+    /**
+     * 处理支付回调消息的处理器接口
+     *
+     * @param payMessage 支付消息
+     * @param payService 支付服务
+     * @return xml, text格式的消息,如果在异步规则里处理的话,可以返回null
+     */
+    @Override
+    public String handle(AliPayMessage payMessage, AliPayService payService) {
+
+        log.info("支付宝支付回调消息处理,订单号:{}", payMessage.getOutTradeNo());
+        return PayConstants.ALI_PAY_SUCCESS;
+    }
+
+    @Override
+    public boolean support(PayPlatformEnum payPlatformEnum) {
+        return PayPlatformEnum.ALIPAY.equals(payPlatformEnum);
+    }
+
+    @Override
+    public int getOrder() {
+        return Integer.MIN_VALUE + 1;
+    }
+}

+ 2 - 1
kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/service/KxsShopOrderService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.kxs.common.core.util.R;
+import com.kxs.product.api.dto.kxsapp.shop.PayOrderDTO;
 import com.kxs.product.api.model.KxsShopOrder;
 import com.kxs.product.api.dto.kxsapp.shop.GoodsSubmitDTO;
 import com.kxs.product.api.vo.kxsapp.shop.ShopOrderCountVO;
@@ -43,7 +44,7 @@ public interface KxsShopOrderService extends IService<KxsShopOrder> {
      * @param param 参数
      * @return {@link R}
      */
-    R payOrder(KxsShopOrder param);
+    R payOrder(PayOrderDTO param);
 
     /**
      * kxs app 取消订单

+ 104 - 26
kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/service/impl/KxsShopOrderServiceImpl.java

@@ -1,31 +1,50 @@
 package com.kxs.product.biz.service.impl;
 
-import cn.hutool.core.lang.Snowflake;
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.IdUtil;
+import com.alipay.api.AlipayApiException;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.kxs.common.core.constant.CommonConstants;
+import com.kxs.common.core.exception.GlobalCustomerException;
 import com.kxs.common.core.util.R;
+import com.kxs.common.pay.enums.PayConstants;
+import com.kxs.common.pay.enums.PayPlatformEnum;
+import com.kxs.common.pay.enums.PayStatusCommEnum;
+import com.kxs.common.pay.factory.PaymentFactory;
+import com.kxs.common.pay.model.PayRequest;
+import com.kxs.common.pay.model.PayResponse;
+import com.kxs.common.pay.model.YuePayMessage;
 import com.kxs.common.security.util.SecurityUtils;
+import com.kxs.product.api.dto.kxsapp.shop.GoodsSubmitDTO;
+import com.kxs.product.api.dto.kxsapp.shop.PayOrderDTO;
 import com.kxs.product.api.model.KxsShopGoods;
+import com.kxs.product.api.model.KxsShopOrder;
 import com.kxs.product.api.model.KxsShopOrderInfo;
-import com.kxs.product.api.dto.kxsapp.shop.GoodsSubmitDTO;
 import com.kxs.product.api.vo.kxsapp.shop.*;
 import com.kxs.product.biz.constant.enums.KxsShopEnum;
 import com.kxs.product.biz.constant.enums.ProductErrorTypeEnum;
 import com.kxs.product.biz.mapper.KxsShopGoodsMapper;
 import com.kxs.product.biz.mapper.KxsShopOrderInfoMapper;
 import com.kxs.product.biz.mapper.KxsShopOrderMapper;
-import com.kxs.product.api.model.KxsShopOrder;
 import com.kxs.product.biz.service.KxsShopOrderService;
 import com.kxs.user.api.feign.RemoteKxsUserService;
+import com.lly835.bestpay.enums.OrderStatusEnum;
+import io.seata.spring.annotation.GlobalTransactional;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * 商城订单表(KxsShopOrder)表服务实现类
@@ -35,6 +54,7 @@ import java.util.List;
  */
 @Service("kxsShopOrderService")
 @RequiredArgsConstructor
+@Slf4j
 public class KxsShopOrderServiceImpl extends ServiceImpl<KxsShopOrderMapper, KxsShopOrder> implements KxsShopOrderService {
 
     private final KxsShopGoodsMapper goodsMapper;
@@ -43,6 +63,8 @@ public class KxsShopOrderServiceImpl extends ServiceImpl<KxsShopOrderMapper, Kxs
 
     private final RemoteKxsUserService remoteKxsUserService;
 
+    private final PaymentFactory paymentFactory;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public R submitOrder(GoodsSubmitDTO param) {
@@ -50,20 +72,20 @@ public class KxsShopOrderServiceImpl extends ServiceImpl<KxsShopOrderMapper, Kxs
         Long userId = SecurityUtils.getUser().getId();
 
         //订单号
-        Snowflake snowflake = IdUtil.getSnowflake(1, userId);
-        long orderSn = snowflake.nextId();
+        String orderSn = CommonConstants.KXS_SHOP_PREFIX + IdUtil.getSnowflakeNextIdStr();
 
         KxsShopOrder order = new KxsShopOrder();
         //参数赋值
         BeanUtils.copyProperties(param, order);
         order.setUserId(userId);
-        order.setOrderSn(String.valueOf(orderSn));
+        order.setOrderSn(orderSn);
         //支付金额
         order.setPayPrice(order.getTotalPrice());
         baseMapper.insert(order);
 
         //查询商品
         KxsShopGoods goods = goodsMapper.selectById(param.getGoodsId());
+        Optional.ofNullable(goods).orElseThrow(() -> new GlobalCustomerException("商品不存在"));
         //订单详情
         KxsShopOrderInfo orderInfo = new KxsShopOrderInfo();
         orderInfo.setNumber(param.getNumber());
@@ -76,7 +98,7 @@ public class KxsShopOrderServiceImpl extends ServiceImpl<KxsShopOrderMapper, Kxs
         orderInfo.setOrderId(order.getId());
         orderInfoMapper.insert(orderInfo);
 
-        return R.ok(Boolean.TRUE);
+        return R.ok(order.getId());
 
     }
 
@@ -103,26 +125,74 @@ public class KxsShopOrderServiceImpl extends ServiceImpl<KxsShopOrderMapper, Kxs
     }
 
     @Override
-    public R payOrder(KxsShopOrder param) {
+    @GlobalTransactional(rollbackFor = Exception.class)
+    public R payOrder(PayOrderDTO param) {
 
+        if(param.getId() == null){
+            return R.failed(ProductErrorTypeEnum.ORDER_EMPTY.getDescription());
+        }
+        if(param.getPayType() == null){
+            return R.failed("请选择支付方式");
+        }
         KxsShopOrder order = baseMapper.selectById(param.getId());
         if(order == null){
             return R.failed(ProductErrorTypeEnum.ORDER_EMPTY.getDescription());
         }
-
-        //余额支付
-        if(param.getPayType().equals(KxsShopEnum.YUE_PAY.getType())){
-            return this.yuePay(param);
+        if(!Objects.equals(order.getStatus(), KxsShopEnum.ORDER_NO_PAY.getType())){
+            return R.failed(ProductErrorTypeEnum.ORDER_STATUS_ERROR.getDescription());
         }
+        List<KxsShopOrderInfo> kxsShopOrderInfos = orderInfoMapper.selectList(Wrappers.<KxsShopOrderInfo>lambdaQuery().eq(KxsShopOrderInfo::getOrderId, order.getId()));
+        String subject = kxsShopOrderInfos.stream().map(KxsShopOrderInfo::getGoodsName).collect(Collectors.joining(","));
 
         //支付宝支付
         if(param.getPayType().equals(KxsShopEnum.ALI_PAY.getType())){
-            return this.aliPay(param);
+            return this.aliPay(order, subject);
         }
 
-        return R.failed();
+        //余额支付
+        if(param.getPayType().equals(KxsShopEnum.YUE_PAY.getType())){
+            Optional.ofNullable(param.getPayPwd()).orElseThrow(() -> new GlobalCustomerException("请输入支付密码"));
+            //校验支付密码
+            R<Boolean> booleanR = remoteKxsUserService.checkPayPwd(param.getPayPwd());
+            if(booleanR.getStatus() != 1 || !booleanR.getData()){
+                return R.failed("支付密码错误");
+            }
+
+            LambdaUpdateWrapper<KxsShopOrder> lambdaUpdateWrapper = Wrappers.<KxsShopOrder>lambdaUpdate().eq(KxsShopOrder::getId, param.getId());
+            lambdaUpdateWrapper.set(KxsShopOrder::getPayType, param.getPayType())
+                    .set(KxsShopOrder::getGetMode, param.getGetMode())
+                    .set(KxsShopOrder::getRemark, param.getRemark());
+
+            PayRequest payReq = new PayRequest();
+            payReq.setPayTypeEnum(PayPlatformEnum.ALIPAY);
+            payReq.setOutTradeNo(order.getOrderSn());
+            payReq.setSubject(subject);
+            payReq.setTotalAmount(order.getPayPrice().doubleValue());
+            try {
+                PayResponse payResponse = paymentFactory.getPayService(PayPlatformEnum.YUEPAY.getType()).appPay(payReq);
+
+                YuePayMessage payMessage = BeanUtil.toBean(payResponse, YuePayMessage.class);
+                if (payMessage != null) {
+                    //支付成功
+                    lambdaUpdateWrapper.set(KxsShopOrder::getStatus, KxsShopEnum.ORDER_NO_SEND.getType());
+                    lambdaUpdateWrapper.set(KxsShopOrder::getPayTime, LocalDateTime.now());
+                }else{
+                    return R.failed(payResponse.getResult());
+                }
+            } catch (AlipayApiException e) {
+                throw new GlobalCustomerException("余额支付异常");
+            }
+            //修改订单信息
+            this.update(lambdaUpdateWrapper);
+            //TODO 余额走支付接下来的逻辑
+        }
+
+
+        return R.ok();
     }
 
+
+
     @Override
     public R cancelOrder(KxsShopOrder param) {
 
@@ -194,24 +264,32 @@ public class KxsShopOrderServiceImpl extends ServiceImpl<KxsShopOrderMapper, Kxs
 
     }
 
-    /**
-     * 余额支付
-     *
-     * @param param 参数
-     * @return {@link R}
-     */
-    private R yuePay(KxsShopOrder param){
-        return R.ok();
-    }
 
     /**
      * 支付宝支付
      *
-     * @param param 参数
+     * @param param   参数
+     * @param subject 订单标题。
      * @return {@link R}
      */
-    private R aliPay(KxsShopOrder param){
-        return R.ok();
+    private R aliPay(KxsShopOrder param, String subject){
+
+        try {
+            PayRequest payReq = new PayRequest();
+            payReq.setPayTypeEnum(PayPlatformEnum.ALIPAY);
+            payReq.setOutTradeNo(param.getOrderSn());
+            payReq.setSubject(subject);
+            payReq.setTotalAmount(param.getPayPrice().doubleValue());
+            //扩展参数
+            payReq.setPassbackParams("shop_order");
+
+            PayResponse payResponse = paymentFactory.getPayService(PayPlatformEnum.ALIPAY.getType()).appPay(payReq);
+
+            return R.ok(payResponse);
+        } catch (AlipayApiException e) {
+            log.error("支付宝支付异常:{}", param.getOrderSn(), e);
+            throw new GlobalCustomerException("支付宝参数异常");
+        }
     }
 
 

+ 1 - 2
kxs-product/kxs-product-biz/src/main/java/com/kxs/product/biz/service/impl/KxsTicketTransferServiceImpl.java

@@ -62,8 +62,7 @@ public class KxsTicketTransferServiceImpl extends ServiceImpl<KxsTicketTransferM
         Integer userId = SecurityUtils.getUser().getId().intValue();
 
         //订单号
-        Snowflake snowflake = IdUtil.getSnowflake(1, userId);
-        String orderSn = "SC" + snowflake.nextId();
+        String orderSn = "SC" + IdUtil.getSnowflakeNextIdStr();
 
         //查询用户信息
         R<KxsUser> sendResult = remoteKxsUserService.loadUserById(userId, SecurityConstants.FROM_IN);

+ 1 - 0
kxs-user/kxs-user-api/pom.xml

@@ -50,5 +50,6 @@
             <groupId>com.kxs</groupId>
             <artifactId>kxs-common-seata</artifactId>
         </dependency>
+
     </dependencies>
 </project>

+ 39 - 0
kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/dto/kxsapp/userAccount/PayRequestDTO.java

@@ -0,0 +1,39 @@
+package com.kxs.user.api.dto.kxsapp.userAccount;
+
+import lombok.Data;
+
+/**
+ * 付款请求
+ *
+ * @author 没秃顶的码农
+ * @date 2024-04-24
+ */
+@Data
+public class PayRequestDTO {
+
+
+    /**
+     * 商户网站唯一订单号
+     */
+    private String outTradeNo;
+
+    /**
+     * 订单总金额,单位为元,精确到小数点后两位
+     */
+    private Double totalAmount;
+
+    /**
+     * 订单标题
+     */
+    private String subject;
+
+    /**
+     * 订单包含的商品列表信息,json格式
+     */
+    private String goodsDetail;
+
+    /**
+     * 公用回传参数
+     */
+    private String passbackParams;
+}

+ 8 - 2
kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/feign/RemoteKxsUserService.java

@@ -3,7 +3,6 @@ package com.kxs.user.api.feign;
 
 import com.kxs.common.core.constant.SecurityConstants;
 import com.kxs.common.core.util.R;
-import com.kxs.user.api.dto.kxsapp.user.RealUserDTO;
 import com.kxs.user.api.model.KxsLeader;
 import com.kxs.user.api.model.KxsUser;
 import com.kxs.user.api.model.KxsUserAdvance;
@@ -148,5 +147,12 @@ public interface RemoteKxsUserService {
 	@GetExchange("/leader/getDateBetweenLeaders")
 	R<List<KxsLeader>> getDateBetweenLeaders(@RequestParam("startTime") String startTime, @RequestParam("endTime") String endTime, @RequestHeader(SecurityConstants.FROM) String from);
 
-
+	/**
+	 * 校验支付密码
+	 *
+	 * @param payPwd 支付 PWD
+	 * @return {@link R}
+	 */
+	@GetExchange("/app/checkPayPwd")
+	R<Boolean> checkPayPwd(@RequestParam("payPwd") String payPwd);
 }

+ 22 - 0
kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/feign/RemoteYuePayService.java

@@ -0,0 +1,22 @@
+
+package com.kxs.user.api.feign;
+
+import com.kxs.common.core.util.R;
+import com.kxs.user.api.dto.kxsapp.userAccount.PayRequestDTO;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.service.annotation.PostExchange;
+
+import java.util.Map;
+
+/**
+ * @author Lxq
+ * @date 2023/6/22
+ */
+public interface RemoteYuePayService {
+
+	/**
+	 * 余额支付
+	 */
+	@PostExchange("/userAccount/yuePay")
+	R yuePay(@RequestBody PayRequestDTO payReq);
+}

+ 12 - 0
kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/feign/config/KxsUserFeignClientConfiguration.java

@@ -3,6 +3,7 @@ package com.kxs.user.api.feign.config;
 import cn.hutool.core.text.CharSequenceUtil;
 import com.kxs.common.core.constant.ServiceNameConstants;
 import com.kxs.user.api.feign.RemoteKxsUserService;
+import com.kxs.user.api.feign.RemoteYuePayService;
 import io.seata.core.context.RootContext;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -81,4 +82,15 @@ public class KxsUserFeignClientConfiguration {
 		return factory.createClient(RemoteKxsUserService.class);
 	}
 
+	/**
+	 * 余额支付
+	 * @param client 客户端
+	 * @return 余额支付服务
+	 */
+	@Bean
+	RemoteYuePayService remoteYuePayService(@Qualifier("kxsUserFeignClient") WebClient client) {
+		HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
+		return factory.createClient(RemoteYuePayService.class);
+	}
+
 }

+ 94 - 0
kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/model/KxsChangeType.java

@@ -0,0 +1,94 @@
+package com.kxs.user.api.model;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 收支明细分类表(KxsChangeType)表实体类
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class KxsChangeType extends Model<KxsChangeType> implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    
+    
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+
+    /**
+     * 新增时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    @Schema(description = "新增时间")
+    private LocalDateTime createTime;
+
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.UPDATE)
+    @Schema(description = "更新时间")
+    private LocalDateTime updateTime;
+
+
+    /**
+     * 删除标记,0未删除,1已删除
+     */
+    @Schema(description = "删除标记,0未删除,1已删除")
+    private Integer delFlag;
+
+
+    /**
+     * 版本
+     */
+    @Schema(description = "版本")
+    private Integer version;
+
+
+    /**
+     * 分类名称
+     */
+    @Schema(description = "分类名称")
+    private String name;
+
+
+    /**
+     * 收支类型 1新增 2减少
+     */
+    @Schema(description = "收支类型 1新增 2减少")
+    private String kind;
+
+
+    /**
+     * 排序
+     */
+    @Schema(description = "排序")
+    private Integer sort;
+
+
+
+
+}
+

+ 101 - 0
kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/model/KxsChangeTypeKind.java

@@ -0,0 +1,101 @@
+package com.kxs.user.api.model;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 收支明细分类明细表(KxsChangeTypeKind)表实体类
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class KxsChangeTypeKind extends Model<KxsChangeTypeKind> implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    
+    
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+
+    /**
+     * 新增时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    @Schema(description = "新增时间")
+    private LocalDateTime createTime;
+
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.UPDATE)
+    @Schema(description = "更新时间")
+    private LocalDateTime updateTime;
+
+
+    /**
+     * 删除标记,0未删除,1已删除
+     */
+    @Schema(description = "删除标记,0未删除,1已删除")
+    private Integer delFlag;
+
+
+    /**
+     * 版本
+     */
+    @Schema(description = "版本")
+    private Integer version;
+
+
+    /**
+     * 分类ID
+     */
+    @Schema(description = "分类ID")
+    private Integer changeTypeId;
+
+
+    /**
+     * 类型名称
+     */
+    @Schema(description = "类型名称")
+    private String name;
+
+
+    /**
+     * 排序
+     */
+    @Schema(description = "排序")
+    private Integer sort;
+
+
+    /**
+     * 收支类型 add新增 sub减少
+     */
+    @Schema(description = "收支类型 add新增 sub减少")
+    private String kind;
+
+
+
+
+}
+

+ 121 - 0
kxs-user/kxs-user-api/src/main/java/com/kxs/user/api/model/KxsUserYuepay.java

@@ -0,0 +1,121 @@
+package com.kxs.user.api.model;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 余额支付交易账单表(KxsUserYuepay)表实体类
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class KxsUserYuepay extends Model<KxsUserYuepay> implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    
+    
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+
+    /**
+     * 新增时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    @Schema(description = "新增时间")
+    private LocalDateTime createTime;
+
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.UPDATE)
+    @Schema(description = "更新时间")
+    private LocalDateTime updateTime;
+
+
+    /**
+     * 删除标记,0未删除,1已删除
+     */
+    @Schema(description = "删除标记,0未删除,1已删除")
+    @TableLogic
+    @TableField(fill = FieldFill.INSERT)
+    private String delFlag;
+
+
+    /**
+     * 版本
+     */
+    @Schema(description = "版本")
+    private Integer version;
+
+
+    /**
+     * 交易编号
+     */
+    @Schema(description = "交易编号")
+    private String tradeNo;
+
+
+    /**
+     * 订单交易编号
+     */
+    @Schema(description = "订单交易编号")
+    private String outTradeNo;
+
+
+    /**
+     * 用户ID
+     */
+    @Schema(description = "用户ID")
+    private Long userId;
+
+
+    /**
+     * 订单标题
+     */
+    @Schema(description = "订单标题")
+    private String subject;
+
+
+    /**
+     * 公共参数 原路返回
+     */
+    @Schema(description = "公共参数 原路返回")
+    private String passbackParams;
+
+
+    /**
+     * 交易状态
+     */
+    @Schema(description = "交易状态")
+    private Integer tradeStatus;
+
+
+    /**
+     * 交易金额
+     */
+    @Schema(description = "交易金额")
+    private BigDecimal tradeAmount;
+
+
+
+
+}
+

+ 30 - 0
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/constant/enums/YuePayStatusEnum.java

@@ -0,0 +1,30 @@
+package com.kxs.user.biz.constant.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+
+/**
+ * 余额支付状态类型枚举
+ *
+ * @author 没秃顶的码农
+ * @date 2024-01-02
+ */
+@Getter
+@RequiredArgsConstructor
+public enum YuePayStatusEnum {
+
+
+    SUCCESS(1, "支付成功"),
+    RETURN(-1, "退款");
+
+    /**
+     * 类型
+     */
+    private final Integer type;
+
+    /**
+     * 描述
+     */
+    private final String description;
+}

+ 13 - 1
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/controller/kxsapp/UserAccountController.java

@@ -3,6 +3,7 @@ package com.kxs.user.biz.controller.kxsapp;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.kxs.common.core.util.R;
+import com.kxs.user.api.dto.kxsapp.userAccount.PayRequestDTO;
 import com.kxs.user.api.dto.kxsapp.userAccount.UserAmountVariationListDTO;
 import com.kxs.user.api.dto.kxsapp.userAccount.WithdrawDepositDTO;
 import com.kxs.user.api.model.KxsUserAdvance;
@@ -165,6 +166,7 @@ public class UserAccountController {
 
         return R.ok(kxsUserAmountLogService.variationList(changeTypeId));
     }
+
     /**
      * 余额变动
      *
@@ -178,8 +180,18 @@ public class UserAccountController {
         return R.ok(kxsUserAmountService.userAmountChange(userAmountLog));
     }
 
+    /**
+     * 内部调用
+     * 余额支付
+     *
+     * @param payReq 支付参数
+     * @return {@link R}
+     */
+    @PostMapping("/yuePay")
+    public R yuePay(@RequestBody PayRequestDTO payReq){
 
-
+        return kxsUserAmountService.yuePay(payReq);
+    }
 
 
 }

+ 17 - 0
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/mapper/KxsChangeTypeKindMapper.java

@@ -0,0 +1,17 @@
+package com.kxs.user.biz.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.kxs.user.api.model.KxsChangeTypeKind;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 收支明细分类明细表(KxsChangeTypeKind)表数据库访问层
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+@Mapper
+public interface KxsChangeTypeKindMapper extends BaseMapper<KxsChangeTypeKind> {
+
+}
+

+ 17 - 0
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/mapper/KxsChangeTypeMapper.java

@@ -0,0 +1,17 @@
+package com.kxs.user.biz.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.kxs.user.api.model.KxsChangeType;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 收支明细分类表(KxsChangeType)表数据库访问层
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+@Mapper
+public interface KxsChangeTypeMapper extends BaseMapper<KxsChangeType> {
+
+}
+

+ 17 - 0
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/mapper/KxsUserYuepayMapper.java

@@ -0,0 +1,17 @@
+package com.kxs.user.biz.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.kxs.user.api.model.KxsUserYuepay;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 余额支付交易账单表(KxsUserYuepay)表数据库访问层
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+@Mapper
+public interface KxsUserYuepayMapper extends BaseMapper<KxsUserYuepay> {
+
+}
+

+ 5 - 1
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/KxsUserAmountService.java

@@ -2,7 +2,7 @@ package com.kxs.user.biz.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.kxs.common.core.util.R;
-import com.kxs.user.api.dto.kxsapp.userAccount.WithdrawDepositDTO;
+import com.kxs.user.api.dto.kxsapp.userAccount.PayRequestDTO;
 import com.kxs.user.api.model.KxsUserAmount;
 import com.kxs.user.api.model.KxsUserAmountLog;
 import com.kxs.user.api.vo.kxsapp.userAccount.MyAccountVO;
@@ -30,5 +30,9 @@ public interface KxsUserAmountService extends IService<KxsUserAmount> {
      */
     R userAmountChange(KxsUserAmountLog userAmountLog);
 
+    /**
+     * 余额支付
+     */
+    R yuePay(PayRequestDTO payReq);
 }
 

+ 15 - 0
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/KxsUserYuepayService.java

@@ -0,0 +1,15 @@
+package com.kxs.user.biz.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.kxs.user.api.model.KxsUserYuepay;
+
+/**
+ * 余额支付交易账单表(KxsUserYuepay)表服务接口
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+public interface KxsUserYuepayService extends IService<KxsUserYuepay> {
+
+}
+

+ 52 - 10
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/impl/KxsUserAmountServiceImpl.java

@@ -5,20 +5,27 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.kxs.common.core.constant.enums.KindTypeEnum;
 import com.kxs.common.core.util.R;
+import com.kxs.common.core.util.SysUtils;
+import com.kxs.common.mybatis.handler.MonthTableNameHandler;
 import com.kxs.common.security.util.SecurityUtils;
-import com.kxs.user.api.dto.kxsapp.userAccount.WithdrawDepositDTO;
+import com.kxs.user.api.dto.kxsapp.userAccount.PayRequestDTO;
+import com.kxs.user.api.model.KxsUserAmount;
 import com.kxs.user.api.model.KxsUserAmountLog;
+import com.kxs.user.api.model.KxsUserYuepay;
 import com.kxs.user.api.vo.kxsapp.userAccount.MyAccountVO;
+import com.kxs.user.biz.constant.enums.YuePayStatusEnum;
 import com.kxs.user.biz.mapper.KxsUserAmountMapper;
-import com.kxs.user.api.model.KxsUserAmount;
 import com.kxs.user.biz.service.KxsUserAmountLogService;
 import com.kxs.user.biz.service.KxsUserAmountService;
+import com.kxs.user.biz.service.KxsUserYuepayService;
 import lombok.RequiredArgsConstructor;
+import lombok.Synchronized;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
 import java.util.Objects;
+import java.util.UUID;
 
 /**
  * 用户账户表(KxsUserAmount)表服务实现类
@@ -31,6 +38,9 @@ import java.util.Objects;
 public class KxsUserAmountServiceImpl extends ServiceImpl<KxsUserAmountMapper, KxsUserAmount> implements KxsUserAmountService {
 
     private final KxsUserAmountLogService kxsUserAmountLogService;
+
+    private final KxsUserYuepayService kxsUserYuepayService;
+
     @Override
     public MyAccountVO myAccount() {
 
@@ -61,8 +71,12 @@ public class KxsUserAmountServiceImpl extends ServiceImpl<KxsUserAmountMapper, K
             afterAmount = NumberUtil.sub(userAmount.getAmount(), userAmountLog.getAmount());
         }
         userAmountLog.setAfterAmount(afterAmount);
-        userAmount.setAmount(NumberUtil.add(afterAmount));
+        userAmount.setAmount(afterAmount);
+
+        MonthTableNameHandler.setData(MonthTableNameHandler.getThisMoth());
         kxsUserAmountLogService.save(userAmountLog);
+        //删除线程,避免线程污染
+        MonthTableNameHandler.removeData();
 
         this.updateById(userAmount);
 
@@ -70,15 +84,43 @@ public class KxsUserAmountServiceImpl extends ServiceImpl<KxsUserAmountMapper, K
         return R.ok(Boolean.FALSE);
     }
 
+    @Override
+    @Synchronized
+    public R yuePay(PayRequestDTO payReq) {
 
+        Long userId = SecurityUtils.getUser().getId();
+        KxsUserAmount userAmount = this.getOne(Wrappers.<KxsUserAmount>lambdaQuery()
+                .eq(KxsUserAmount::getUserId, userId));
+        if(BigDecimal.valueOf(payReq.getTotalAmount()).compareTo(userAmount.getAmount()) > 0){
+            return R.failed("余额不足");
+        }
+        long count = kxsUserYuepayService.count(Wrappers.<KxsUserYuepay>lambdaQuery().eq(KxsUserYuepay::getOutTradeNo, payReq.getOutTradeNo()));
+        if(count > 1){
+            return R.failed("该订单号已支付");
+        }
 
-
-
-
-
-
-
-
+        KxsUserAmountLog kxsUserAmountLog = new KxsUserAmountLog();
+        kxsUserAmountLog.setAmount(BigDecimal.valueOf(payReq.getTotalAmount()));
+        kxsUserAmountLog.setUserId(userId.intValue());
+        //余额支付
+        kxsUserAmountLog.setVariationType(20);
+        kxsUserAmountLog.setRemark("购买" + payReq.getSubject());
+        kxsUserAmountLog.setKind(KindTypeEnum.SUB.getType());
+        this.userAmountChange(kxsUserAmountLog);
+        //余额支付账单
+        String tradeNo = SysUtils.generatePaymentId(userId);
+        KxsUserYuepay kxsUserYuepay = new KxsUserYuepay();
+        kxsUserYuepay.setSubject(payReq.getSubject());
+        kxsUserYuepay.setUserId(userId);
+        kxsUserYuepay.setTradeNo(tradeNo);
+        kxsUserYuepay.setPassbackParams(payReq.getPassbackParams());
+        kxsUserYuepay.setTradeStatus(YuePayStatusEnum.SUCCESS.getType());
+        kxsUserYuepay.setOutTradeNo(payReq.getOutTradeNo());
+        kxsUserYuepay.setTradeAmount(BigDecimal.valueOf(payReq.getTotalAmount()));
+        kxsUserYuepayService.save(kxsUserYuepay);
+
+        return R.ok(kxsUserYuepay);
+    }
 
 
 }

+ 19 - 0
kxs-user/kxs-user-biz/src/main/java/com/kxs/user/biz/service/impl/KxsUserYuepayServiceImpl.java

@@ -0,0 +1,19 @@
+package com.kxs.user.biz.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.kxs.user.biz.mapper.KxsUserYuepayMapper;
+import com.kxs.user.api.model.KxsUserYuepay;
+import com.kxs.user.biz.service.KxsUserYuepayService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 余额支付交易账单表(KxsUserYuepay)表服务实现类
+ *
+ * @author 系统
+ * @since 2024-04-29 14:20:11
+ */
+@Service("kxsUserYuepayService")
+public class KxsUserYuepayServiceImpl extends ServiceImpl<KxsUserYuepayMapper, KxsUserYuepay> implements KxsUserYuepayService {
+
+}
+

+ 18 - 0
kxs-user/kxs-user-biz/src/main/resources/mapper/KxsChangeTypeKindMapper.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.kxs.user.biz.mapper.KxsChangeTypeKindMapper">
+
+    <resultMap type="com.kxs.user.api.model.KxsChangeTypeKind" id="KxsChangeTypeKindMap">
+        <result column="id" property="id" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+        <result column="del_flag" property="delFlag" />
+        <result column="version" property="version" />
+        <result column="change_type_id" property="changeTypeId" />
+        <result column="name" property="name" />
+        <result column="sort" property="sort" />
+        <result column="kind" property="kind" />
+
+    </resultMap>
+
+</mapper>

+ 17 - 0
kxs-user/kxs-user-biz/src/main/resources/mapper/KxsChangeTypeMapper.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.kxs.user.biz.mapper.KxsChangeTypeMapper">
+
+    <resultMap type="com.kxs.user.api.model.KxsChangeType" id="KxsChangeTypeMap">
+        <result column="id" property="id" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+        <result column="del_flag" property="delFlag" />
+        <result column="version" property="version" />
+        <result column="name" property="name" />
+        <result column="kind" property="kind" />
+        <result column="sort" property="sort" />
+
+    </resultMap>
+
+</mapper>

+ 21 - 0
kxs-user/kxs-user-biz/src/main/resources/mapper/KxsUserYuepayMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.kxs.user.biz.mapper.KxsUserYuepayMapper">
+
+    <resultMap type="com.kxs.user.api.model.KxsUserYuepay" id="KxsUserYuepayMap">
+        <result column="id" property="id" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+        <result column="del_flag" property="delFlag" />
+        <result column="version" property="version" />
+        <result column="trade_no" property="tradeNo" />
+        <result column="out_trade_no" property="outTradeNo" />
+        <result column="user_id" property="userId" />
+        <result column="subject" property="subject" />
+        <result column="passback_params" property="passbackParams" />
+        <result column="trade_status" property="tradeStatus" />
+        <result column="trade_amount" property="tradeAmount" />
+
+    </resultMap>
+
+</mapper>

+ 5 - 0
pom.xml

@@ -255,6 +255,11 @@
                 <artifactId>kxs-common-mq</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.kxs</groupId>
+                <artifactId>kxs-common-pay</artifactId>
+                <version>${project.version}</version>
+            </dependency>