Prechádzať zdrojové kódy

印度支付razorpay业务

nelson 2 mesiacov pred
rodič
commit
6f1be63bcf
16 zmenil súbory, kde vykonal 1684 pridanie a 17 odobranie
  1. 8 0
      sikey-hmd-business/pom.xml
  2. 1 1
      sikey-hmd-business/sikey-hmd-business-biz/pom.xml
  3. 1 3
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/HmdApplication.java
  4. 1 1
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/controller/app/alipay/AlipayPaymentController.java
  5. 34 6
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/controller/app/index/PaymentIndexController.java
  6. 42 0
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/controller/app/razorpay/RazorpayPaymentController.java
  7. 3 1
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/enums/PayChannelEnum.java
  8. 30 0
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/AbstractPaymentService.java
  9. 2 0
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/plans/PlansServiceImpl.java
  10. 15 0
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/razorpay/RazorpayPaymentService.java
  11. 96 0
      sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/razorpay/RazorpayPaymentServiceImpl.java
  12. 1177 0
      sikey-hmd-business/sikey-hmd-business-biz/src/main/resources/templates/razorpayIndex.html
  13. 8 3
      sikey-selenium-business/sikey-selenium-business-biz/src/main/java/cn/sikey/selenium/scheduler/DeleteSessionScheduler.java
  14. 6 2
      sikey-selenium-business/sikey-selenium-business-biz/src/main/java/cn/sikey/selenium/util/WebDriverContextManagerUtil.java
  15. 58 0
      sikey-websocket-business/sikey-websocket-business-api/.flattened-pom.xml
  16. 202 0
      sikey-websocket-business/sikey-websocket-business-biz/.flattened-pom.xml

+ 8 - 0
sikey-hmd-business/pom.xml

@@ -60,6 +60,14 @@
                 <scope>import</scope>
             </dependency>
 
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
             <!-- Web 开发基础依赖 -->
             <dependency>
                 <groupId>cn.sikey.cloud</groupId>

+ 1 - 1
sikey-hmd-business/sikey-hmd-business-biz/pom.xml

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

+ 1 - 3
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/HmdApplication.java

@@ -13,9 +13,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
  *
  * @author qin
  */
-@SpringBootApplication(exclude = {
-        org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration.class
-})
+@SpringBootApplication
 @EnableDiscoveryClient
 @EnableTransactionManagement
 @Slf4j

+ 1 - 1
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/controller/app/alipay/AlipayPaymentController.java

@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 
 /**
- * @Author: qin
+ * @Author: nelson
  * @Date: 2025/3/31
  * @Description: 网关
  */

+ 34 - 6
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/controller/app/index/PaymentIndexController.java

@@ -61,8 +61,8 @@ public class PaymentIndexController {
         model.addAttribute("yearPrice", price);
         // 年套餐
         model.addAttribute("validity", 1);
-        model.addAttribute("featureList", getFeatureComparison());
-        model.addAttribute("paymentMethods", Arrays.asList(new PaymentMethod("alipay", "支付宝", true), new PaymentMethod("wechat", "微信支付", false)));
+        // model.addAttribute("featureList", getFeatureComparison());
+        // model.addAttribute("paymentMethods", Arrays.asList(new PaymentMethod("alipay", "支付宝", true), new PaymentMethod("wechat", "微信支付", false)));
 
         /*DeviceActivationDTO deviceActivationDTO = deviceActivationService.queryDeviceActivation(imei);
 
@@ -132,11 +132,11 @@ public class PaymentIndexController {
         return "userAgreement";
     }
 
-    public List<PricingPlan> getAvailablePlans() {
+    /*public List<PricingPlan> getAvailablePlans() {
         return Arrays.asList(new PricingPlan("包年服务", new BigDecimal("0.02"), 1), new PricingPlan("终身服务", new BigDecimal("0.01"), 2));
-    }
+    }*/
 
-    public LinkedList<Feature> getFeatureComparison() {
+    /*public LinkedList<Feature> getFeatureComparison() {
         LinkedList<Feature> featureList = new LinkedList<>();
 
         // 使用 LinkedHashMap 保证顺序
@@ -149,7 +149,7 @@ public class PaymentIndexController {
         featureList.add(new Feature("VIP", createOrderedMap("support", "支持", "advanced", "高级", "noLimit", "不限次"), true));
 
         return featureList;
-    }
+    }*/
 
     /**
      * 创建有序 Map 的辅助方法
@@ -165,4 +165,32 @@ public class PaymentIndexController {
         return map;
     }
 
+    @GetMapping("/razorpayIndex")
+    public String razorpayIndex(String imei, Model model) {
+        PlansRespVO plansRespVO = plansService.queryPlans(ValidityEnum.YEAR.getValue());
+        String price = "0.00";
+        if (Objects.nonNull(plansRespVO)) {
+            price = PriceUtil.convertFenToYuan(plansRespVO.getPrice());
+        }
+        model.addAttribute("imei", imei);
+        model.addAttribute("serviceInfo", new ServiceInfo("思奇智能AI助手", "现已接入Deep Seek大模型"));
+        //  model.addAttribute("pricingPlans", getAvailablePlans());
+
+        model.addAttribute("yearPrice", price);
+        // 年套餐
+        model.addAttribute("validity", 1);
+        // model.addAttribute("featureList", getFeatureComparison());
+        // model.addAttribute("paymentMethods", Arrays.asList(new PaymentMethod("alipay", "支付宝", true), new PaymentMethod("wechat", "微信支付", false)));
+
+        /*DeviceActivationDTO deviceActivationDTO = deviceActivationService.queryDeviceActivation(imei);
+
+        boolean isExpire = Objects.nonNull(deviceActivationDTO) && Objects.nonNull(deviceActivationDTO.getPlansValidity()) && deviceActivationDTO.getPlansValidity().intValue() == ValidityEnum.YEAR.getValue();
+        if (isExpire) {
+            log.warn("[手机网站支付-支付宝]您已是会员,未过有效期,勿需再支付");
+            return "lifelong";
+        }*/
+
+        return "razorpayIndex";
+    }
+
 }

+ 42 - 0
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/controller/app/razorpay/RazorpayPaymentController.java

@@ -0,0 +1,42 @@
+package cn.sikey.hmd.controller.app.razorpay;
+
+import cn.sikey.framework.common.pojo.CommonResult;
+import cn.sikey.hmd.controller.app.alipay.vo.CheckPayReqVO;
+import cn.sikey.hmd.enums.RateLimit;
+import cn.sikey.hmd.service.razorpay.RazorpayPaymentService;
+import cn.sikey.pay.api.razorpay.dto.RazorPayTradeDTO;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+/**
+ * @Author: nelson
+ * @Date: 2025/3/31
+ * @Description: 网关
+ */
+@RestController
+@RequestMapping("/hmd/razorpay")
+@Slf4j
+public class RazorpayPaymentController {
+
+    @Resource
+    private RazorpayPaymentService razorpayPaymentService;
+
+    /**
+     * 创建订单
+     * 手机网站支付
+     * yaml,sw_merchant_config,h5
+     *
+     * @param razorPayTradeDTO razorpay
+     * @return
+     */
+    @RateLimit(key = "razorpayTradeWapPay", type = RateLimit.LimitType.GLOBAL)
+    @GetMapping("/razorpayTradeWapPay")
+    public CommonResult<CheckPayReqVO> razorpayTradeWapPay(RazorPayTradeDTO razorPayTradeDTO) {
+        return razorpayPaymentService.razorpayTradeWapPay(razorPayTradeDTO);
+    }
+
+}

+ 3 - 1
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/enums/PayChannelEnum.java

@@ -15,7 +15,9 @@ import java.util.Arrays;
 public enum PayChannelEnum implements IntArrayValuable {
 
     ALI_PAY(1, "支付宝"),
-    WE_CHAT(2, "微信");
+    WE_CHAT(2, "微信"),
+    RAZOR_PAY(3,"razorPay")
+    ;
 
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PayChannelEnum::getValue).toArray();
 

+ 30 - 0
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/AbstractPaymentService.java

@@ -95,6 +95,36 @@ public abstract class AbstractPaymentService {
         return generateOrderReqVO;
     }
 
+    /**
+     * 获取razorpay订单
+     *
+     * @param plansId                    套餐编号
+     * @param deviceId                   设备编号
+     * @param merchantProjectApplication 商户
+     * @param price                      价格
+     * @param payType                    支付类型
+     * @return
+     */
+    public GenerateOrderReqVO getRazorpayGenerateOrderReqVO(Long plansId, String deviceId, String merchantProjectApplication, Integer price, String payType) {
+        GenerateOrderReqDTO generateOrderReqDTO = new GenerateOrderReqDTO();
+        generateOrderReqDTO.setPlansId(plansId);
+        generateOrderReqDTO.setDeviceId(deviceId);
+        generateOrderReqDTO.setMerchantProjectApplication(merchantProjectApplication);
+        generateOrderReqDTO.setTotalAmount(price);
+        CommonResult<GenerateOrderReqVO> generateOrder = orderApi.razorpayGenerateOrder(generateOrderReqDTO);
+        if (!generateOrder.getCode().equals(HttpStatus.SC_OK)) {
+            log.warn("[手机网站支付-{}]调用订单服务失败", payType);
+            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "调用订单服务失败");
+        }
+
+        GenerateOrderReqVO generateOrderReqVO = generateOrder.getData();
+        if (Objects.isNull(generateOrderReqVO) || Objects.isNull(generateOrderReqVO.getOrderSn())) {
+            log.warn("[手机网站支付-{}]调用订单服务数据是空", payType);
+            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "调用订单服务数据是空");
+        }
+        return generateOrderReqVO;
+    }
+
     /**
      * 保存付款
      *

+ 2 - 0
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/plans/PlansServiceImpl.java

@@ -21,6 +21,8 @@ public class PlansServiceImpl implements PlansService {
     private PlansMapper plansMapper;
 
     /**
+     * 查询套餐
+     *
      * @param validity 有效期
      * @return
      */

+ 15 - 0
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/razorpay/RazorpayPaymentService.java

@@ -0,0 +1,15 @@
+package cn.sikey.hmd.service.razorpay;
+
+import cn.sikey.framework.common.pojo.CommonResult;
+import cn.sikey.hmd.controller.app.alipay.vo.CheckPayReqVO;
+import cn.sikey.pay.api.razorpay.dto.RazorPayTradeDTO;
+
+/**
+ * @Author: nelson
+ * @Date: 2025/7/7
+ * @Description: 支付服务
+ */
+public interface RazorpayPaymentService {
+
+    CommonResult<CheckPayReqVO> razorpayTradeWapPay(RazorPayTradeDTO razorPayTradeDTO);
+}

+ 96 - 0
sikey-hmd-business/sikey-hmd-business-biz/src/main/java/cn/sikey/hmd/service/razorpay/RazorpayPaymentServiceImpl.java

@@ -0,0 +1,96 @@
+package cn.sikey.hmd.service.razorpay;
+
+import cn.sikey.framework.common.exception.ServiceException;
+import cn.sikey.framework.common.pojo.CommonResult;
+import cn.sikey.hmd.api.hmd.vo.plans.PlansRespVO;
+import cn.sikey.hmd.controller.app.alipay.vo.CheckPayReqVO;
+import cn.sikey.hmd.enums.PayChannelEnum;
+import cn.sikey.hmd.service.AbstractPaymentService;
+import cn.sikey.hmd.service.plans.PlansService;
+import cn.sikey.order.api.order.vo.GenerateOrderReqVO;
+import cn.sikey.pay.api.alipay.AlipayApi;
+import cn.sikey.pay.api.razorpay.dto.RazorPayTradeDTO;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.util.Objects;
+
+/**
+ * @Author: nelson
+ * @Date: 2025/3/31
+ * @Description: razorpay
+ */
+@Service("razorpayPaymentService")
+@Slf4j
+public class RazorpayPaymentServiceImpl extends AbstractPaymentService implements RazorpayPaymentService {
+
+    @Resource
+    private PlansService plansService;
+
+    @Resource
+    private AlipayApi alipayApi;
+
+    @Resource
+    private TransactionTemplate transactionTemplate;
+
+    private static final String PAY_TYPE = "razorPay";
+
+    /**
+     * 手机网站支付
+     *
+     * @param razorPayTradeDTO razorpay
+     * @return
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public CommonResult<CheckPayReqVO> razorpayTradeWapPay(RazorPayTradeDTO razorPayTradeDTO) {
+        String merchantProjectApplication = razorPayTradeDTO.getMerchantProjectApplication();
+        String deviceId = razorPayTradeDTO.getDeviceId();
+
+        super.checkPayMent(merchantProjectApplication, deviceId, PAY_TYPE);
+
+        // 校验支付,生成订单
+        CheckPayReqVO checkPayReqVO = checkPayAndGenerateOrder(razorPayTradeDTO);
+        // 保存支付记录
+        savePayments(checkPayReqVO.getOrderId(), checkPayReqVO.getPlansId(), deviceId, checkPayReqVO.getAmount(), PayChannelEnum.RAZOR_PAY.getValue(), merchantProjectApplication);
+
+        return CommonResult.success(checkPayReqVO);
+    }
+
+    /**
+     * 校验支付,生成订单
+     *
+     * @param razorPayTradeDTO razorPay交易
+     * @return
+     */
+    private CheckPayReqVO checkPayAndGenerateOrder(RazorPayTradeDTO razorPayTradeDTO) {
+        int validity = razorPayTradeDTO.getValidity();
+        PlansRespVO plansRespVO = plansService.queryPlans(validity);
+        if (Objects.isNull(plansRespVO)) {
+            log.warn("[手机网站支付-razorPay]请配置套餐");
+            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "请配置套餐");
+        }
+
+        int price = plansRespVO.getPrice();
+
+        String deviceId = razorPayTradeDTO.getDeviceId();
+
+        GenerateOrderReqVO generateOrderReqVO = super.getRazorpayGenerateOrderReqVO(plansRespVO.getId(), deviceId, razorPayTradeDTO.getMerchantProjectApplication(), price, PAY_TYPE);
+
+        String outTradeNo = generateOrderReqVO.getOrderSn();
+
+        CheckPayReqVO checkPayReqVO = new CheckPayReqVO();
+        checkPayReqVO.setOutTradeNo(outTradeNo);
+        checkPayReqVO.setOrderId(generateOrderReqVO.getId());
+        checkPayReqVO.setPlansId(plansRespVO.getId());
+        checkPayReqVO.setAmount(price);
+        return checkPayReqVO;
+    }
+
+}
+
+

+ 1177 - 0
sikey-hmd-business/sikey-hmd-business-biz/src/main/resources/templates/razorpayIndex.html

@@ -0,0 +1,1177 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title th:text="思奇智能AI助手">思奇智能AI助手</title>
+    <style>
+        :root {
+            --primary-color: #FF6B28;
+            --vip-color: #F5A623;
+            --border-color: #e0e0e0;
+        }
+
+        body {
+            font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+            background: url('/images/ai-background.png') no-repeat center;
+            background-size: cover;
+            background-position: center;
+            margin: 0;
+            line-height: 1.6;
+            position: relative; /* 为绝对定位子元素提供参照 */
+            /*z-index: 1000;*/
+            height: 610px;
+        }
+
+        /* 容器样式 */
+        .container {
+            /*max-width: 600px;*/
+            /*margin: -20% auto;*/
+            background: #F5F5F5;
+            border-radius: 15px;
+            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+            /* overflow: hidden;*/
+            position: absolute;
+            top: 15%;
+            width: 100%;
+            height: 50%;
+            margin-top: 80%;
+        }
+
+        /* 页头区域 */
+        .header {
+            position: absolute;
+            /* width: 350px; */
+            /* height: 95px; */
+            /* top: 80px; */
+            /* left: 16px; */
+            border-radius: 13px 13px 4px 4px;
+            background: linear-gradient(135deg, #5689AF, #025E8E);
+            /* padding: 1.2rem; */
+            /* z-index: -10; */
+            /* box-sizing: border-box; */
+            /* margin-top: -160px; */
+        }
+
+
+        /* 渐变边框实现 */
+        .header::before {
+            content: '';
+            position: absolute;
+            top: -1px;
+            left: -1px;
+            right: -1px;
+            bottom: -1px;
+            background: linear-gradient(135deg, #5689AF, #025E8E);
+            border-radius: inherit;
+            z-index: -1;
+        }
+
+        .branding {
+            display: flex;
+            align-items: center;
+            height: 100%;
+            gap: 15px;
+        }
+
+        .robot-logo {
+            width: 100px;
+            height: 125px;
+            /*animation: float 3s ease-in-out infinite;*/
+            flex-shrink: 0;
+        }
+
+        .header-text {
+            margin-left: 1rem;
+            color: #FEDCC8;
+            text-align: left;
+        }
+
+        .header-text h1 {
+            font-size: 1.4rem;
+            margin: 0 0 0.3rem;
+            line-height: 1.2;
+        }
+
+        .header-text p {
+            font-size: 0.9rem;
+            margin: 0;
+            opacity: 0.9;
+        }
+
+        /* 价格对比模块 */
+        .price-cards {
+            display: grid;
+            grid-template-columns: repeat(2, 1fr);
+            gap: 0.6rem;
+            padding: 1.0rem;
+            margin-top: -2%;
+            position: absolute;
+        }
+
+        .price-card {
+            position: relative;
+            padding: 0.8rem;
+            border-radius: 10px;
+            background: white;
+            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+            width: 140px;
+            height: 50px;
+            cursor: pointer;
+        }
+
+        .price-card.selected {
+            border: 2px solid #d96b32;
+            background-color: #ffe8e1;
+        }
+
+        .price-card .name {
+            font-weight: bold;
+            color: #333;
+        }
+
+        .price-card .price {
+            font-size: 1.2em;
+            color: #333;
+        }
+
+        /* 选中状态:名称和价格均橙色 */
+        .price-card.selected .name,
+        .price-card.selected .price {
+            color: #d96b32; /* 覆盖为橙色 */
+        }
+
+        .vip-badge {
+            position: absolute;
+            top: -15px;
+            right: -15px;
+            width: 40px;
+            height: 40px;
+        }
+
+        .redpacket-icon {
+            width: 30px;
+            vertical-align: middle;
+            margin-right: 8px;
+        }
+
+        /* 功能对比表 */
+        .feature-grid {
+            display: grid;
+            /*grid-template-columns: 2fr repeat(3, 1fr);*/
+            /* gap: 1px; */
+            background: #FFFFFF;
+            margin: 0.6rem;
+            position: relative;
+            /* margin-top: -1%;
+            border-radius: 10px;
+             border: 1px solid #ccc;*/
+        }
+
+        .feature-grid_out {
+            display: grid;
+            grid-template-columns: 2fr repeat(2, 1fr);
+            gap: 1px;
+            background: #FFFFFF;
+            margin: 0.8rem;
+            position: relative;
+            margin-top: 28%;
+            border-radius: inherit;
+            height: 50%;
+        }
+
+        .grid-item {
+            /*padding: 1rem;*/
+            background: white;
+            /*display: flex;*/
+            align-items: center;
+            min-height: 50px;
+        }
+
+        .col-header {
+            font-weight: 600;
+            /*background: #f8f9fa;*/
+            border: none; /* 隐藏边框 */
+            box-shadow: none; /* 隐藏可能的盒子阴影 */
+            outline: none; /* 隐藏可能的轮廓线 */
+        }
+
+
+        .isHighlight {
+            color: #D1530B !important;
+            font-weight: 600;
+            position: relative;
+        }
+
+
+        /*.isHighlight::after {
+            content: none; !* 删除左侧指示条 *!
+        }*/
+
+        /* 支付方式 */
+        .payment-section {
+            padding: 0.6rem;
+            position: absolute;
+            height: -webkit-fill-available;
+            margin-top: 3%;
+        }
+
+        .payment-section-pay {
+            display: flex;
+            align-items: center;
+        }
+
+        /*.payment-logo {
+            width: 24px;
+            height: 24px;
+            transition: transform 0.3s ease;
+        }*/
+
+        .payment-logo:hover {
+            transform: scale(1.1);
+        }
+
+        /* 购买按钮 */
+        .buy-button {
+            /*display: block;
+            margin: 2rem auto;
+            width: 80%;
+            max-width: 300px;
+            transition: opacity 0.3s ease;*/
+            margin-left: 5%;
+        }
+
+        .buy-button img {
+            width: 100%;
+        }
+
+        .buy-button:hover {
+            opacity: 0.9;
+        }
+
+        .middle {
+            margin-top: -140px;
+            position: relative;
+        }
+
+        .middle_picture {
+            width: -webkit-fill-available;
+        }
+
+        .payment-section_div {
+            position: absolute;
+            display: grid;
+        }
+
+        .payment-section_picture {
+            margin-left: 2%;
+            margin-top: -2%;
+        }
+
+        .middle_red_envelope {
+            position: absolute;
+            bottom: 16px;
+            left: 10px; /* 可根据需要调整水平位置 */
+            z-index: 1; /* 确保红包图片位于背景图片上方 */
+        }
+
+        .text {
+            color: #c16c3f;
+            font-size: 16px;
+            position: absolute;
+            top: 8px;
+            left: 35px;
+            z-index: 1;
+        }
+
+        .price-card .name {
+            width: 72px;
+            height: 25px;
+            font-size: 18px;
+        }
+
+        .price-card .price {
+            font-weight: 500; /* 字重(中等) */
+            font-size: 18px; /* 字号 */
+            line-height: 1; /* 行高 = 100%(1倍字号) */
+            letter-spacing: 0; /* 字符间距 */
+        }
+
+        /* 动画效果 */
+        @keyframes float {
+            0%, 100% {
+                transform: translateY(0);
+            }
+            50% {
+                transform: translateY(-10px);
+            }
+        }
+
+        /* 支付方式选项样式 */
+        .pay-radio {
+            margin-bottom: 2px; /* 选项间距 */
+        }
+
+        .pay-radio span {
+            margin-left: 2%;
+        }
+
+        /* 单个支付选项 */
+        .pay-radio label {
+            display: flex;
+            align-items: center;
+            gap: 2px;
+            padding: 16px 24px;
+            /*border: 2px solid #e9ecef;*/
+            border-radius: 6px;
+            cursor: pointer;
+            transition: all 0.3s ease;
+            background: white;
+            width: -webkit-fill-available;
+        }
+
+        /* 图片尺寸控制 */
+        .payment-logo {
+            width: 7%;
+            height: 7%;
+            object-fit: contain;
+        }
+
+        /* 文字样式 */
+        /*.pay-radio input[type='radio'] {
+            display: none;
+        }*/
+
+        /* 交互状态 */
+        .pay-radio label:hover {
+            border-color: #1890ff;
+            box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
+        }
+
+        .pay-radio input:checked + label {
+            border-color: #1890ff;
+            background-color: #e6f4ff;
+        }
+
+        .payment-methods {
+            /*display: grid;*/
+            grid-template-columns: 2fr repeat(3, 1fr);
+            gap: 1px;
+            /*background: #FFFFFF;*/
+            /*margin: 0.8rem;*/
+            /*position: relative;
+            */
+
+            margin: 0.2rem;
+            width: 151%;
+            position: relative;
+            border-radius: inherit;
+        }
+
+        /*.payment-buy {
+            margin-top: 5%;
+            width: 90%;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            padding: 20px;
+            border-radius: 10px;
+            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+        }*/
+
+        .container_agreement {
+            display: flex;
+            align-items: center;
+        }
+
+        .checkbox-container {
+            display: inline-flex;
+            vertical-align: middle; /* 固定垂直对齐方式 */
+            position: relative;
+            margin-right: 8px;
+        }
+
+        /* 自定义复选框视觉样式 */
+        .custom-checkbox {
+            position: absolute;
+            left: 0;
+            top: 50%;
+            transform: translateY(-50%); /* 垂直居中 */
+            width: 16px;
+            height: 16px;
+            border: 1px solid #d9d9d9;
+            border-radius: 4px;
+            background: white;
+            transition: all 0.3s;
+        }
+
+        .checkbox {
+            margin-right: 10px;
+            position: relative;
+        }
+
+        .checkbox-container input[type="checkbox"] {
+            display: none;
+        }
+
+        .checkbox-container input[type="checkbox"] + .checked-label:before {
+            content: "";
+            display: inline-block;
+            width: 16px;
+            height: 16px;
+            border: 1px solid #ccc;
+            border-radius: 3px;
+            margin-right: 5px;
+            position: relative;
+        }
+
+        .checkbox-container input[type="checkbox"]:checked + .checked-label:before {
+            content: "✔";
+            font-size: 14px;
+            color: #1E90FF;
+            text-align: center;
+            line-height: 16px;
+        }
+
+        .text_agreement {
+            color: #1E90FF; /* 蓝色字体 */
+        }
+
+        .container_agreement_font {
+            font-size: 10px;
+            font-family: Arial, sans-serif;
+            /*margin-left: auto;*/
+        }
+
+        .price-display {
+            flex: 1;
+            text-align: left;
+            font-size: 18px;
+            font-weight: bold;
+            color: white;
+            margin-left: -200px;
+            position: absolute;
+        }
+
+        .background-container {
+            /* 你可以根据需要设置背景图片或其他背景样式 */
+            /* 例如:background-image: url('your-background-image.jpg'); */
+            background-color: #005580;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            padding: 20px;
+            border-radius: 50px;
+            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+            width: 130%;
+            height: 20px;
+        }
+
+        .purchase-button {
+            background-color: #87CDFF;
+            border: none;
+            border-radius: 30px;
+            padding: 15px 25px;
+            font-size: 16px;
+            color: white;
+            cursor: pointer;
+            transition: background-color 0.3s ease;
+            margin-right: -70%;
+        }
+
+        .pay-div {
+            display: flex;
+            align-items: center;
+            justify-content: flex-end;
+            /*background-color: #f5f5f5;*/
+            padding: 10px;
+            border-radius: 8px;
+            margin-left: auto;
+        }
+
+        /*协议复选框*/
+        .container_agreement {
+            display: flex;
+            align-items: center;
+            position: relative;
+            margin: 8px 0 8px 20%;
+        }
+
+        .checkbox-container {
+            margin-right: 10px;
+        }
+
+        .text_agreement {
+            color: #1890ff;
+            cursor: pointer;
+        }
+
+        /* 提示信息样式 */
+        .error-tip {
+            position: absolute;
+            bottom: -20px;
+            left: 30px;
+            color: #ff4d4f;
+            font-size: 12px;
+            display: none;
+        }
+
+        @keyframes fadeIn {
+            from {
+                opacity: 0;
+            }
+            to {
+                opacity: 1;
+            }
+        }
+
+        /* 新增错误提示样式 */
+        .error-tip {
+            position: absolute;
+            bottom: 150px; /* 调整提示位置到协议上方 */
+            left: 0;
+            color: #ff4d4f;
+            font-size: 12px;
+            width: 210px;
+            white-space: pre-line; /* 允许换行显示多行错误 */
+            display: none;
+        }
+
+        .show-tip {
+            display: block;
+            animation: fadeIn 0.3s;
+        }
+
+        /* 新增价格样式 */
+        .price-display .price-number {
+            font-size: 22px; /* 价格字体大小 */
+            font-weight: bold;
+            color: white;
+        }
+
+        /* 新增价格样式 */
+        .price-card .price-number {
+            font-size: 22px; /* 价格字体大小 */
+            font-weight: bold;
+        }
+
+        /*.price-display .price-unit {
+            font-size: 14px;  !* 单位字体大小 *!
+            color: #666;
+        }*/
+
+
+        /* 新增动画效果 */
+        @keyframes modalSlideIn {
+            from {
+                transform: translateY(60px) scale(0.96);
+                opacity: 0;
+            }
+            to {
+                transform: translateY(0) scale(1);
+                opacity: 1;
+            }
+        }
+
+        .modal-content {
+            animation: modalSlideIn 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+            box-shadow: 0 12px 24px rgba(0, 0, 0, 0.16);
+        }
+
+        /* 悬停效果 */
+        #copy-email:hover {
+            transform: translateY(-1px);
+            box-shadow: 0 6px 16px rgba(135, 205, 255, 0.4);
+        }
+
+        #copy-email {
+            transition: all 0.3s ease;
+            background: linear-gradient(135deg, #87CDFF, #5A8FEB);
+        }
+
+        #copy-email.copied {
+            background: #4CAF50 !important;
+        }
+
+        #copy-email.error {
+            background: #ff4444 !important;
+        }
+    </style>
+</head>
+<script src="/js/axios/axios.min.js"></script>
+
+<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
+<script>
+    /*var options = {
+        "key": "YOUR_KEY_ID", // Enter the Key ID generated from the Dashboard
+        "amount": "50000", // Amount is in currency subunits. Default currency is INR. Hence, 50000 refers to 50000 paise
+        "currency": "INR",
+        "name": "Acme Corp", //your business name
+        "description": "Test Transaction",
+        "image": "https://example.com/your_logo",
+        "order_id": "order_9A33XWu170gUtm", //This is a sample Order ID. Pass the `id` obtained in the response of Step 1
+        "handler": function (response){
+            alert(response.razorpay_payment_id);
+            alert(response.razorpay_order_id);
+            alert(response.razorpay_signature)
+        },
+        "prefill": { //We recommend using the prefill parameter to auto-fill customer's contact information, especially their phone number
+            "name": "Gaurav Kumar", //your customer's name
+            "email": "gaurav.kumar@example.com",
+            "contact": "9000090000"  //Provide the customer's phone number for better conversion rates
+        },
+        "notes": {
+            "address": "Razorpay Corporate Office"
+        },
+        "theme": {
+            "color": "#3399cc"
+        }
+    };
+    var rzp1 = new Razorpay(options);
+    rzp1.on('payment.failed', function (response){
+        alert(response.error.code);
+        alert(response.error.description);
+        alert(response.error.source);
+        alert(response.error.step);
+        alert(response.error.reason);
+        alert(response.error.metadata.order_id);
+        alert(response.error.metadata.payment_id);
+    });
+    document.getElementById('purchase-button').onclick = function(e){
+        rzp1.open();
+        e.preventDefault();
+    }*/
+</script>
+
+<body>
+<div class="container" th:fragment="main">
+    <!-- 页头动态数据绑定 -->
+    <header class="header">
+        <!--<div class="branding">
+            <div class="header-text">
+                <h1 th:text="${serviceInfo.title}"></h1>
+                <p th:text="${serviceInfo.subtitle}"></p>
+            </div>
+            <img th:src="@{/images/助手机器人.png}" alt="AI助手" class="robot-logo">
+        </div>-->
+    </header>
+
+    <!--<div class="middle">
+        <img th:src="@{/images/开通终身服务超级划算背景.png}" alt="AI助手" class="middle_picture">
+        <div>
+            <img th:src="@{/images/红包.png}" alt="AI助手" class="middle_red_envelope">
+        </div>
+        <div class="text">开通终身服务超级划算</div>
+    </div>-->
+
+    <!-- 年套餐价格 -->
+    <input type="hidden" class="yearPrice" th:value="${yearPrice}"/>
+
+    <!--<section class="price-cards">
+        <div th:each="plan : ${pricingPlans}" class="price-card" onclick="selectPlan(this)">
+            <p>
+                <input type="hidden" class="priceCard" th:value="${plan.type}"/>
+                <span th:text="${plan.name}" class="name"></span>
+                <span th:text="${plan.price}" class="price price-number"></span>元
+            </p>
+        </div>
+    </section>-->
+
+    <!-- 功能对比表动态生成 -->
+    <!--<section class="feature-grid_out">
+        <th:block th:each="col : ${featureList}">
+            <div class="feature-grid">
+                <div class="grid-item col-header" th:classappend="${col.getIsHighlight()} ? 'isHighlight' : ''"
+                     th:text="${col.name}"></div>
+                <div th:each="detail : ${col.details}"
+                     class="grid-item"
+                     th:classappend="${col.getIsHighlight()} ? 'isHighlight' : ''"
+                     th:text="${detail.value}"></div>
+            </div>
+        </th:block>
+    </section>-->
+
+    <!--支付方式-->
+    <section class="payment-section">
+        <div class="payment-section-pay">
+            <div>
+                <img th:src="@{/images/付费方式前面的标志.png}" alt="AI助手">
+            </div>
+            <div class="payment-section_picture">付费方式</div>
+        </div>
+
+        <div class="payment-methods">
+            <div class="alipay-methods">
+                <div class="pay-radio">
+                    <label>
+                        <img th:src="@{/images/alipay-logo.png}"
+                             alt="pay type"
+                             class="payment-logo">
+                        <span>razorpay</span>
+                        <div class="pay-div">
+                            <input type="radio" name="payment"
+                                   th:value="razorpay"
+                                   th:checked="true">
+                        </div>
+                    </label>
+                </div>
+            </div>
+
+        <!--<div class="payment-methods">
+            <div class="alipay-methods">
+                <div class="pay-radio">
+                    <label>
+                        <img th:src="@{/images/alipay-logo.png}"
+                             alt="支付方式"
+                             class="payment-logo">
+                        <span>支付宝支付</span>
+                        <div class="pay-div">
+                            <input type="radio" name="payment"
+                                   th:value="alipay"
+                                   th:checked="true">
+                        </div>
+                    </label>
+                </div>
+            </div>-->
+
+            <!--<div>
+                <div class="pay-radio">
+                    <label>
+                        <img th:src="@{/images/wechat-logo.png}"
+                             alt="支付方式"
+                             class="payment-logo">
+                        <span>微信支付</span>
+                        <div class="pay-div">
+                            <input type="radio" name="payment"
+                                   th:value="wechat">
+                        </div>
+                    </label>
+                </div>
+            </div>-->
+
+            <div class="container_agreement">
+                <div class="checkbox-container">
+                    <input type="checkbox" id="agreement" class="checkbox">
+                    <label for="agreement" class="checked-label"></label>
+                </div>
+                <div class="container_agreement_font">我已阅读并同意<span
+                        class="text_agreement">《付费升级服务免责声明》</span></div>
+                <!-- 统一错误提示容器 -->
+                <div class="error-tip" id="errorTip"></div>
+            </div>
+        </div>
+
+        <!--<div class="payment-buy">
+            <div class="price-display">9.9元/年</div>
+            <div>
+                <a th:href="@{/order/create}" class="buy-button">
+                    <img th:src="@{/images/立即购买背景.png}"
+                         alt="立即购买">
+                </a>
+            </div>
+        </div>-->
+        <div class="background-container">
+            <div class="price-display"><span class="price-number"></span>元/年</div>
+            <button class="purchase-button" id="purchase-button">立即购买</button>
+        </div>
+        <input class="validity" type="hidden" th:value="${validity}"/>
+
+        <!-- 在background-container下方添加联系客服链接 -->
+        <div class="contact-service"
+             style="text-align: center; font-size: 10px;font-family: Arial, sans-serif;margin-left: 50%;margin-top: 1%;">
+            点击这里<a href="javascript:void(0)" id="contact-customer-service"
+                       style="color: #1E90FF; font-size: 10px;font-family: Arial, sans-serif;margin-top: 15px; text-decoration: underline;">联系客服</a>
+        </div>
+    </section>
+</div>
+
+
+<!-- 客服弹窗模态框 -->
+<div id="customer-service-modal" class="modal"
+     style="display: none; position: fixed; z-index: 9999; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.4);">
+    <div class="modal-content"
+         style="background-color: #fff; margin: 15% auto; padding: 0; width: 340px; border-radius: 16px; overflow: hidden; position: relative;">
+        <!-- 顶部背景 -->
+        <div style="position: relative; height: 180px; margin-top: -40px; margin-bottom: -20px;z-index: 1;overflow: visible; ">
+            <img th:src="@{/images/child.png}" alt="背景" style="width: 50%;
+    height: 100px;
+    object-fit: contain;
+    position: relative;
+    z-index: 2;
+    margin-top: 15%;
+    margin-left: 26%;">
+            <!--<div class="stars" style="position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        z-index: 1;
+        pointer-events: none;">
+                <img th:src="@{/images/child.png}" alt="星星" style="width: 100%;
+            height: 100%;
+            object-fit: cover;
+            opacity: 0.8;">
+            </div>-->
+        </div>
+
+        <!-- 内容区 -->
+        <div style="padding: 24px; margin-top: -20px; position: relative; z-index: 2;">
+            <div style="text-align: center; margin-bottom: 24px;">
+                <div style="color: #1876FD; font-size: 18px; font-weight: 500; margin-bottom: 8px;">
+                    service@sikey.com.cn
+                </div>
+                <div style="color: #474747; font-size: 12px;">请联系我们的客服邮箱</div>
+            </div>
+
+            <div style="display: flex; justify-content: center; position: relative; padding-bottom: 50px;">
+                <button id="copy-email" style="
+                    background: linear-gradient(135deg, #87CDFF, #5A8FEB);
+                    border: none;
+                    padding: 10px 32px;
+                    border-radius: 24px;
+                    color: #FFFFFF;
+                    font-size: 14px;
+                    cursor: pointer;
+                    display: flex;
+                    align-items: center;
+                    gap: 8px;
+                    box-shadow: 0 4px 12px rgba(135,205,255,0.3);
+                ">
+                    <!--<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M8 4V16H18V4H8Z" stroke="white" stroke-width="2"/>
+                        <path d="M6 20H16V8H6V20Z" stroke="white" stroke-width="2"/>
+                    </svg>-->
+                    复制邮箱
+                </button>
+
+                <!-- 关闭按钮移动到复制按钮底部 -->
+                <div class="close-container" style="
+                    position: absolute;
+                    bottom: 0;
+                    left: 50%;
+                    transform: translateX(-50%);
+                    margin-top: 20px;
+                ">
+                    <div style="
+                        background: #FFFFFF;
+                        width: 40px;
+                        height: 40px;
+                        border-radius: 50%;
+                        display: flex;
+                        align-items: center;
+                        justify-content: center;
+                        box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+                        cursor: pointer;
+                    ">
+                        <span class="close" style="
+                            color: #666;
+                            font-size: 24px;
+                            line-height: 1;
+                            margin-bottom: 4px;
+                        ">×</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 公共模板片段 -->
+<!--<div th:fragment="css-resource" th:remove="tag">
+    <link rel="stylesheet" th:href="@{/css/theme-${theme}.css}">
+</div>-->
+
+<!--<div th:fragment="scripts" th:remove="tag">
+    <script th:src="@{/js/payment-validation.js}"></script>
+</div>-->
+</body>
+
+<script>
+    // 获取选择的套餐
+    /*function selectPlan(clickedElement) {
+        // 1. 移除所有卡片的选中样式(通过删除 CSS 类名)
+        const allCards = document.querySelectorAll('.price-card');
+        allCards.forEach(card => card.classList.remove('selected'));
+
+        // 2. 为当前点击的卡片添加选中样式(通过添加 CSS 类名)
+        clickedElement.classList.add('selected');
+
+        // 3. 动态获取价格和服务类型,更新到价格显示区域
+        const price = clickedElement.querySelector('.price').textContent;
+        const serviceType = clickedElement.querySelector('.priceCard').value;
+
+        const priceInput = document.querySelector('.price-display-price');
+        priceInput.value = serviceType
+
+        const unit = serviceType == 1 ? '元/年' : '元/终身';
+        const priceDisplay = document.querySelector('.price-display'); // 使用 querySelector 直接获取元素
+        // priceDisplay.textContent = `${price}${unit}`;  // 使用 textContent 修改内容
+
+        // 使用模板字符串构建带样式的HTML
+        priceDisplay.innerHTML = `
+    <span class="price-number">${price}</span>
+    <span class="price-unit">${unit}</span>`;
+    }*/
+
+    // 定义获取付费方式的函数
+    function getSelectedPayment() {
+        const radios = document.getElementsByName('payment');
+        for (const radio of radios) {
+            if (radio.checked) {
+                return radio.value;
+            }
+        }
+        return null;
+    }
+
+    // 确保DOM加载完成
+    document.addEventListener("DOMContentLoaded", function () {
+        const purchaseButton = document.getElementById('purchase-button');
+        const errorTip = document.getElementById('errorTip');
+
+        const yearPrice = document.querySelector('.yearPrice').value;
+        const priceDisplay = document.querySelector('.price-display'); // 使用 querySelector 直接获取元素
+        // priceDisplay.textContent = `${yearPrice}`;  // 使用 textContent 修改内容
+        // 使用模板字符串构建带样式的HTML
+        priceDisplay.innerHTML = `
+    <span class="price-number">${yearPrice}</span>
+    <span class="price-unit">元/年</span>`;
+
+        // 表单验证函数
+        function validateForm() {
+            let errors = [];
+
+            // 1. 校验套餐选择
+            /*const selectedPlan = document.querySelector('.price-card.selected');
+            if (!selectedPlan) {
+                errors.push("请选择套餐类型(包年或终身服务)");
+            }*/
+
+            // 2. 校验支付方式
+            const paymentSelected = document.querySelector('input[name="payment"]:checked');
+            if (!paymentSelected) {
+                errors.push("请选择支付方式(支付宝或微信)");
+            }
+
+            // 3. 校验协议勾选
+            const agreementChecked = document.getElementById('agreement').checked;
+            if (!agreementChecked) {
+                errors.push("请阅读并同意《付费升级服务免责声明》");
+            }
+
+            // 显示/隐藏错误提示
+            if (errors.length > 0) {
+                errorTip.innerHTML = errors.join("\n"); // 用换行符分隔错误
+                errorTip.classList.add('show-tip');
+                return false;
+            } else {
+                errorTip.classList.remove('show-tip');
+                return true;
+            }
+        }
+
+
+        function handlePurchaseButtonClick(e) {
+            e.preventDefault(); // 阻止默认提交
+            if (!validateForm()) return;
+
+            const paymentMethod = getSelectedPayment();
+            if (paymentMethod) {
+                let path;
+
+                // 获取当前页面的完整 URL
+                const currentUrl = window.location.href;
+                // 创建 URL 对象
+                const urlObj = new URL(currentUrl);
+
+                // 获取查询参数对象
+                const params = new URLSearchParams(urlObj.search);
+                // 获取单个参数
+                const imei = params.get('imei');
+
+                const validity = document.querySelector('.validity').value;
+
+                    if (paymentMethod === 'razorpay') {
+                    path = "/app-api/hmd/razorpay/selectRazorpayTradeWapPay";
+                    const pathWithDomain = urlObj.origin + path;
+                    const responseHtml = axios.get(pathWithDomain, {
+                        params: {
+                            validity: validity,
+                            deviceId: imei,
+                            subject: '思奇智能AI助手',
+                            merchantProjectApplication: '1:hmd:3'
+                        }
+                    }).then(response => {
+                        if (response.data.code !== 200) {
+                            const errorTip = document.getElementById('errorTip');
+                            errorTip.innerHTML = response.data.msg;
+                            errorTip.classList.add('show-tip');
+                            return false;
+                        }
+
+                        // razorpay
+                        var options = {
+                            "key": "rzp_test_bwL1qbvhAdCZqw", // Enter the Key ID generated from the Dashboard
+                            "amount": response.data.amount, // Amount is in currency subunits. Default currency is INR. Hence, 50000 refers to 50000 paise
+                            "currency": "INR",
+                            "name": "思奇智能AI助手", //your business name
+                            "description": "Test Transaction",
+                            "image": "https://example.com/your_logo",
+                            "order_id": response.data.outTradeNo, //This is a sample Order ID. Pass the `id` obtained in the response of Step 1
+                            "callback_url": "https://eneqd3r9zrjok.x.pipedream.net/",
+                            "prefill": { //We recommend using the prefill parameter to auto-fill customer's contact information especially their phone number
+                                "name": "Gaurav Kumar", //your customer's name
+                                "email": "gaurav.kumar@example.com",
+                                "contact": "9000090000" //Provide the customer's phone number for better conversion rates
+                            },
+                            "notes": {
+                                "address": "Razorpay Corporate Office"
+                            },
+                            "theme": {
+                                "color": "#3399cc"
+                            }
+                        };
+
+                        var rzp1 = new Razorpay(options);
+                        rzp1.open();
+                        e.preventDefault();
+
+                        /*const tempDiv = document.createElement('div');
+                        document.body.appendChild(tempDiv); // 先插入 DOM
+                        tempDiv.innerHTML = response.data.data;
+                        const form = tempDiv.querySelector('form');
+                        form.submit();*/
+                    }).catch(error => {
+                        // 处理请求错误
+                        console.error('请求出错:', error);
+                    });
+                }
+            } else {
+                alert('请先选择支付方式!');
+            }
+        }
+
+        if (purchaseButton) {
+            purchaseButton.addEventListener('click', handlePurchaseButtonClick);
+        }
+    });
+
+    /*协议复选框*/
+    /*const checkbox = document.getElementById('agreement');
+    const errorTip = document.getElementById('errorTip');*/
+
+    // 实时监听复选框状态
+    /*checkbox.addEventListener('change', function () {
+        toggleErrorTip(this.checked);
+    });*/
+
+    // 切换提示显示状态
+    /*function toggleErrorTip(isChecked) {
+        if (isChecked) {
+            errorTip.classList.remove('show-tip');
+        } else {
+            errorTip.classList.add('show-tip');
+        }
+    }*/
+
+    // 点击协议文字时自动勾选
+    document.querySelector('.text_agreement').addEventListener('click', function () {
+        const checkbox = document.getElementById('agreement');
+        checkbox.checked = !checkbox.checked;
+        checkbox.dispatchEvent(new Event('change')); // 触发校验
+
+        // 获取当前页面的完整 URL
+        const currentUrl = window.location.href;
+        // 创建 URL 对象
+        const urlObj = new URL(currentUrl);
+        window.location.href = urlObj.origin + "/h5/hmd/payment/agreement";
+    });
+
+    // 添加联系客服弹窗逻辑
+    const modal = document.getElementById('customer-service-modal');
+    const contactLink = document.getElementById('contact-customer-service');
+    const closeBtn = document.querySelector('.close');
+    const copyBtn = document.getElementById('copy-email');
+
+    // 打开弹窗
+    contactLink.onclick = function () {
+        modal.style.display = 'block';
+    }
+
+    // 关闭弹窗
+    closeBtn.onclick = function () {
+        modal.style.display = 'none';
+    }
+
+    // 点击外部区域关闭
+    window.onclick = function (event) {
+        if (event.target == modal) {
+            modal.style.display = 'none';
+        }
+    }
+
+    // 复制功能
+    document.getElementById('copy-email').addEventListener('click', function () {
+        const email = 'service@sikey.com.cn';
+
+        // 现代浏览器方案
+        if (navigator.clipboard) {
+            navigator.clipboard.writeText(email)
+                .then(() => showCopyFeedback('✓ 已复制'))
+                .catch(() => fallbackCopy(email));
+        }
+        // 兼容旧版浏览器方案
+        else {
+            fallbackCopy(email);
+        }
+    });
+
+    // 显示复制反馈
+    function showCopyFeedback(message) {
+        const btn = document.getElementById('copy-email');
+        const originalHTML = btn.innerHTML;
+
+        btn.innerHTML = message;
+        btn.style.backgroundColor = '#4CAF50'; // 成功颜色
+
+        setTimeout(() => {
+            btn.innerHTML = originalHTML;
+            btn.style.backgroundColor = ''; // 恢复原色
+        }, 2000);
+    }
+
+    // 兼容旧浏览器的复制方案
+    function fallbackCopy(text) {
+        const textArea = document.createElement('textarea');
+        textArea.value = text;
+        textArea.style.position = 'fixed';  // 避免滚动跳转
+        document.body.appendChild(textArea);
+        textArea.select();
+
+        try {
+            const successful = document.execCommand('copy');
+            if (successful) {
+                showCopyFeedback('✓ 已复制');
+            } else {
+                showErrorFeedback();
+            }
+        } catch (err) {
+            showErrorFeedback();
+        } finally {
+            document.body.removeChild(textArea);
+        }
+    }
+
+    // 显示错误提示
+    function showErrorFeedback() {
+        const btn = document.getElementById('copy-email');
+        btn.innerHTML = '⚠ 复制失败';
+        btn.style.backgroundColor = '#ff4444';
+
+        setTimeout(() => {
+            btn.innerHTML = `<svg...>复制邮箱</>`; // 恢复原始内容
+            btn.style.backgroundColor = '';
+        }, 2000);
+    }
+</script>
+</html>

+ 8 - 3
sikey-selenium-business/sikey-selenium-business-biz/src/main/java/cn/sikey/selenium/scheduler/DeleteSessionScheduler.java

@@ -5,18 +5,23 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+import java.time.LocalDateTime;
+
 @Slf4j
 @Component
 public class DeleteSessionScheduler {
 
+    public static final int THREE_MINUTES = 3;
 
     /**
-     * 每5分钟运行删除会话
+     * 每1分钟运行删除会话
      */
-    @Scheduled(cron = "0 */5 * * * *")  // 每5分钟执行一次
+    @Scheduled(cron = "0 */1 * * * *")  // 每1分钟执行一次
     public void run() {
         WebDriverContextManagerUtil.sessionMap.asMap().forEach((k, v) -> {
-            WebDriverContextManagerUtil.destroySession(k);
+            if (v.getCreateTime().plusMinutes(THREE_MINUTES).isBefore(LocalDateTime.now())) {
+                WebDriverContextManagerUtil.destroySession(k);
+            }
         });
     }
 }

+ 6 - 2
sikey-selenium-business/sikey-selenium-business-biz/src/main/java/cn/sikey/selenium/util/WebDriverContextManagerUtil.java

@@ -12,8 +12,7 @@ import org.openqa.selenium.support.ui.WebDriverWait;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.time.Duration;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.time.LocalDateTime;
 import java.util.concurrent.TimeUnit;
 
 
@@ -82,6 +81,7 @@ public class WebDriverContextManagerUtil {
     public static class DriverSession {
         private final WebDriver driver;
         private final WebDriverWait wait;
+        private final LocalDateTime createTime = LocalDateTime.now();
 
         public DriverSession(WebDriver driver, WebDriverWait wait) {
             this.driver = driver;
@@ -95,6 +95,10 @@ public class WebDriverContextManagerUtil {
         public WebDriverWait getWait() {
             return wait;
         }
+
+        public LocalDateTime getCreateTime() {
+            return createTime;
+        }
     }
 
     public static WebDriver getDriverConfig(String url) throws MalformedURLException {

+ 58 - 0
sikey-websocket-business/sikey-websocket-business-api/.flattened-pom.xml

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>cn.sikey.cloud</groupId>
+  <artifactId>sikey-websocket-business-api</artifactId>
+  <version>2.3.0-SNAPSHOT</version>
+  <name>sikey-websocket-business-api</name>
+  <description>websocket 模块 API,暴露给其它模块调用</description>
+  <dependencies>
+    <dependency>
+      <groupId>cn.sikey.cloud</groupId>
+      <artifactId>sikey-common</artifactId>
+      <version>2.3.0-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-validation</artifactId>
+      <version>3.4.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+      <version>4.2.0</version>
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+  <repositories>
+    <repository>
+      <id>huaweicloud</id>
+      <name>huawei</name>
+      <url>https://mirrors.huaweicloud.com/repository/maven/</url>
+    </repository>
+    <repository>
+      <id>aliyunmaven</id>
+      <name>aliyun</name>
+      <url>https://maven.aliyun.com/repository/public</url>
+    </repository>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>spring-milestones</id>
+      <name>Spring Milestones</name>
+      <url>https://repo.spring.io/milestone</url>
+    </repository>
+    <repository>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+      <id>spring-snapshots</id>
+      <name>Spring Snapshots</name>
+      <url>https://repo.spring.io/snapshot</url>
+    </repository>
+  </repositories>
+</project>

+ 202 - 0
sikey-websocket-business/sikey-websocket-business-biz/.flattened-pom.xml

@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>cn.sikey.cloud</groupId>
+  <artifactId>sikey-websocket-business-biz</artifactId>
+  <version>2.3.0-SNAPSHOT</version>
+  <name>sikey-websocket-business-biz</name>
+  <description>sikey-websocket-business-biz</description>
+  <dependencies>
+    <dependency>
+      <groupId>cn.sikey.cloud</groupId>
+      <artifactId>sikey-spring-boot-starter-web</artifactId>
+      <version>2.3.0-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-consul-config</artifactId>
+      <version>4.2.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-consul-discovery</artifactId>
+      <version>4.2.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>cn.sikey.cloud</groupId>
+      <artifactId>sikey-common</artifactId>
+      <version>2.3.0-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <version>1.18.36</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mapstruct</groupId>
+      <artifactId>mapstruct</artifactId>
+      <version>1.6.2</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mapstruct</groupId>
+      <artifactId>mapstruct-jdk8</artifactId>
+      <version>1.6.2</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mapstruct</groupId>
+      <artifactId>mapstruct-processor</artifactId>
+      <version>1.6.2</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.skywalking</groupId>
+      <artifactId>apm-toolkit-trace</artifactId>
+      <version>9.0.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.skywalking</groupId>
+      <artifactId>apm-toolkit-logback-1.x</artifactId>
+      <version>9.0.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.skywalking</groupId>
+      <artifactId>apm-toolkit-opentracing</artifactId>
+      <version>9.0.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.opentracing</groupId>
+      <artifactId>opentracing-api</artifactId>
+      <version>0.33.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.opentracing</groupId>
+      <artifactId>opentracing-util</artifactId>
+      <version>0.33.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.opentracing</groupId>
+      <artifactId>opentracing-noop</artifactId>
+      <version>0.33.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp3</groupId>
+      <artifactId>okhttp</artifactId>
+      <version>4.12.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>cn.hutool</groupId>
+      <artifactId>hutool-all</artifactId>
+      <version>5.8.28</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>cn.hutool</groupId>
+      <artifactId>hutool-core</artifactId>
+      <version>5.8.28</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+      <version>2.11.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <version>1.15</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+      <version>1.5.12</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>1.5.12</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>2.0.16</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>cn.sikey.cloud</groupId>
+      <artifactId>sikey-spring-boot-starter-mybatis</artifactId>
+      <version>2.3.0-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>cn.sikey.cloud</groupId>
+      <artifactId>sikey-spring-boot-starter-redis</artifactId>
+      <version>2.3.0-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-amqp</artifactId>
+      <version>3.4.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-websocket</artifactId>
+      <version>3.4.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>33.3.1-jre</version>
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+  <repositories>
+    <repository>
+      <id>huaweicloud</id>
+      <name>huawei</name>
+      <url>https://mirrors.huaweicloud.com/repository/maven/</url>
+    </repository>
+    <repository>
+      <id>aliyunmaven</id>
+      <name>aliyun</name>
+      <url>https://maven.aliyun.com/repository/public</url>
+    </repository>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>spring-milestones</id>
+      <name>Spring Milestones</name>
+      <url>https://repo.spring.io/milestone</url>
+    </repository>
+    <repository>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+      <id>spring-snapshots</id>
+      <name>Spring Snapshots</name>
+      <url>https://repo.spring.io/snapshot</url>
+    </repository>
+  </repositories>
+</project>