Parcourir la source

改造支付分为需确认模式,清理过时代码

- 开门前预创建支付分订单 + wx.openBusinessView 用户确认
- Redis 暂存支付分信息,回调创建订单时自动关联
- PayScoreSyncTask 增加 CREATED 状态兜底轮询
- Order 新增 payScoreValue 字段
- 删除 User.payscoreEnabled 及开通/检查相关接口
- 清理 enable/success/scoreLow/settlement/authorize/createOrder 6 个过时页面

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline il y a 2 semaines
Parent
commit
2ea13c7bb0

+ 25 - 2
haha-admin/src/main/java/com/haha/admin/task/PayScoreSyncTask.java

@@ -72,10 +72,33 @@ public class PayScoreSyncTask {
     }
 
     /**
-     * 同步 DOING 状态订单(兜底防止回调丢失)
-     * 查询 payScoreState = DOING 且 serviceStartTime 超过 SYNC_DELAY_MINUTES 分钟的订单
+     * 同步 CREATED 和 DOING 状态订单(兜底防止回调丢失)
      */
     private void syncDoingOrders() {
+        // 同步 CREATED 状态订单(等待用户确认的回调可能丢失)
+        syncCreatedOrders();
+
+        // 同步 DOING 状态订单
+        syncDoingOrdersInternal();
+    }
+
+    private void syncCreatedOrders() {
+        try {
+            List<Order> createdOrders = orderService.lambdaQuery()
+                    .eq(Order::getPayScoreState, PayScoreState.CREATED.getCode())
+                    .lt(Order::getServiceStartTime, LocalDateTime.now().minusMinutes(SYNC_DELAY_MINUTES))
+                    .list();
+
+            if (!createdOrders.isEmpty()) {
+                log.info("[支付分同步] 找到 {} 个 CREATED 状态订单,开始同步", createdOrders.size());
+                processSyncOrders(createdOrders);
+            }
+        } catch (Exception e) {
+            log.error("[支付分同步] 同步 CREATED 状态订单失败", e);
+        }
+    }
+
+    private void syncDoingOrdersInternal() {
         try {
             List<Order> doingOrders = orderService.lambdaQuery()
                     .eq(Order::getPayScoreState, PayScoreState.DOING.getCode())

+ 2 - 0
haha-admin/src/main/resources/db/migration/V4__add_pay_score_value.sql

@@ -0,0 +1,2 @@
+ALTER TABLE `t_order`
+ADD COLUMN `pay_score_value` INT DEFAULT NULL COMMENT '用户微信支付分分数值' AFTER `pay_score_state`;

+ 3 - 0
haha-entity/src/main/java/com/haha/entity/Order.java

@@ -88,6 +88,9 @@ public class Order implements Serializable {
     /** 支付分订单状态: CREATED/DOING/USER_PAYING/DONE */
     private String payScoreState;
 
+    /** 用户微信支付分分数值 */
+    private Integer payScoreValue;
+
     /** 支付分扣款失败原因(扣款失败时记录微信返回的错误信息) */
     private String payScoreFailReason;
 

+ 0 - 5
haha-entity/src/main/java/com/haha/entity/User.java

@@ -32,11 +32,6 @@ public class User implements Serializable {
     
     private Integer status;
     
-    /**
-     * 微信支付分开通状态:0-未开通,1-已开通
-     */
-    private Integer payscoreEnabled;
-    
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
     private LocalDateTime createTime;
     

+ 17 - 50
haha-miniapp/src/main/java/com/haha/miniapp/controller/PayScoreController.java

@@ -20,7 +20,7 @@ import java.util.Map;
 
 /**
  * 微信支付分控制器
- * 提供支付分服务订单的创建、完结、取消、查询等 API
+ * 需确认模式:每次服务开门前预创建支付分订单,用户确认后开门
  */
 @Slf4j
 @RestController
@@ -31,64 +31,31 @@ public class PayScoreController {
     private final PayScoreService payScoreService;
 
     /**
-     * 检查用户是否已开通微信支付分
+     * 预创建支付分服务订单(需确认模式,开门前调用)
+     * 返回 package 参数供前端调用 wx.openBusinessView 拉起确认页
      */
-    @GetMapping("/check-enable")
-    public Result<Map<String, Object>> checkPayscoreEnabled() {
-        Long userId = StpUtil.getLoginIdAsLong();
-        log.info("用户 {} 查询支付分开通状态", userId);
-
+    @PostMapping("/pre-create")
+    public Result<Map<String, Object>> preCreatePayScoreOrder(@RequestBody Map<String, Object> params) {
         try {
-            Map<String, Object> data = payScoreService.checkPayscoreEnabled(userId);
-            log.info("用户 {} 支付分开通状态:{}", userId, data.get("enabled"));
-            return Result.success("查询成功", data);
-        } catch (Exception e) {
-            log.error("查询支付分开通状态失败 - userId: {}", userId, e);
-            return Result.error("查询失败:" + e.getMessage());
-        }
-    }
-
-    /**
-     * 开通微信支付分(调起小程序授权页面)
-     */
-    @PostMapping("/enable")
-    public Result<Map<String, Object>> enablePayscore(@RequestBody(required = false) Map<String, Object> params) {
-        Long userId = StpUtil.getLoginIdAsLong();
-        log.info("用户 {} 申请开通支付分", userId);
+            Long userId = StpUtil.getLoginIdAsLong();
 
-        try {
-            Map<String, Object> data = payScoreService.enablePayscore(userId);
-            if (Boolean.TRUE.equals(data.get("enabled"))) {
-                return Result.success("您已开通微信支付分", data);
+            if (!params.containsKey("deviceId")) {
+                return com.haha.common.constant.ResponseEnum.PARAM_FORMAT_ERROR.toResult("缺少必填参数 deviceId");
             }
-            log.info("用户 {} 准备开通支付分,outOrderNo: {}", userId, data.get("outOrderNo"));
-            return Result.success("请完成支付分授权", data);
-        } catch (Exception e) {
-            log.error("开通支付分失败 - userId: {}", userId, e);
-            return Result.error("开通失败:" + e.getMessage());
-        }
-    }
 
-    /**
-     * 确认开通微信支付分(用户完成授权后调用)
-     */
-    @PostMapping("/confirm-enable")
-    public Result<Map<String, Object>> confirmEnablePayscore(@RequestBody Map<String, Object> params) {
-        Long userId = StpUtil.getLoginIdAsLong();
-        log.info("用户 {} 确认开通支付分", userId);
+            String deviceId = params.get("deviceId").toString();
 
-        try {
-            Map<String, Object> data = payScoreService.confirmUserPayScoreOrder(userId, params);
-            if (data != null) {
-                log.info("用户 {} 确认开通支付分成功", userId);
-                return Result.success("开通成功", data);
+            log.info("用户 {} 预创建支付分服务订单 - deviceId: {}", userId, deviceId);
+
+            Map<String, Object> data = payScoreService.preCreatePayScoreOrder(userId, deviceId, null);
+            if (Boolean.TRUE.equals(data.get("success"))) {
+                return Result.success("创建成功", data);
             } else {
-                log.warn("用户 {} 开通支付分失败", userId);
-                return com.haha.common.constant.ResponseEnum.OPERATION_FAILED.toResult("开通失败,请重试");
+                return Result.error(String.valueOf(data.get("errorMsg")));
             }
         } catch (Exception e) {
-            log.error("确认开通支付分失败 - userId: {}", userId, e);
-            return Result.error("开通失败:" + e.getMessage());
+            log.error("预创建支付分订单失败", e);
+            return Result.error("创建失败:" + e.getMessage());
         }
     }
 

+ 13 - 37
haha-mp/src/api/payscore.ts

@@ -5,24 +5,24 @@
 import { get, post } from '../utils/request';
 
 /**
- * 检查用户是否已开通微信支付分
+ * 预创建支付分服务订单(需确认模式,开门前调用)
  */
-export interface CheckPayscoreResponse {
-  enabled: boolean;
-  userId: number;
+export interface PreCreatePayscoreOrderRequest {
+  deviceId: string;
 }
 
-/**
- * 开通微信支付分
- */
-export interface EnablePayscoreResponse {
-  enabled: boolean;
-  userId: number;
-  outOrderNo?: string;
-  serviceId?: string;
-  appId?: string;
+export interface PreCreatePayscoreOrderResponse {
+  success: boolean;
+  orderId: number;
+  outOrderNo: string;
+  package: string;
+  errorMsg?: string;
 }
 
+export const preCreatePayscoreOrder = (params: PreCreatePayscoreOrderRequest): Promise<PreCreatePayscoreOrderResponse> => {
+  return post<PreCreatePayscoreOrderResponse>('/payscore/pre-create', params);
+};
+
 /**
  * 创建支付分服务订单
  */
@@ -39,34 +39,10 @@ export interface CreatePayscoreOrderResponse {
   package: string;
 }
 
-/**
- * 检查用户是否已开通微信支付分
- */
-export const checkPayscoreEnabled = (): Promise<CheckPayscoreResponse> => {
-  return get<CheckPayscoreResponse>('/payscore/check-enable');
-};
-
-/**
- * 开通微信支付分(标记已开通)
- */
-export const enablePayscore = (): Promise<EnablePayscoreResponse> => {
-  return post<EnablePayscoreResponse>('/payscore/enable', {});
-};
-
-/**
- * 创建支付分服务订单
- */
 export const createPayscoreOrder = (params: CreatePayscoreOrderRequest): Promise<CreatePayscoreOrderResponse> => {
   return post<CreatePayscoreOrderResponse>('/payscore/create', params);
 };
 
-/**
- * 轮询确认开通状态
- */
-export const confirmEnablePayscore = (outOrderNo: string): Promise<{ enabled: boolean }> => {
-  return post<{ enabled: boolean }>('/payscore/confirm-enable', { outOrderNo });
-};
-
 /**
  * 修改支付分订单金额
  */

+ 0 - 32
haha-mp/src/pages.json

@@ -49,38 +49,6 @@
 				"navigationBarTextStyle": "black"
 			}
 		},
-		{
-			"path": "pages/payscore/enable",
-			"style": {
-				"navigationBarTitleText": "开通微信支付分",
-				"navigationBarBackgroundColor": "#FFFFFF",
-				"navigationBarTextStyle": "black"
-			}
-		},
-		{
-			"path": "pages/payscore/success",
-			"style": {
-				"navigationBarTitleText": "开通成功",
-				"navigationBarBackgroundColor": "#07C160",
-				"navigationBarTextStyle": "white"
-			}
-		},
-		{
-			"path": "pages/payscore/scoreLow",
-			"style": {
-				"navigationBarTitleText": "支付分评估",
-				"navigationBarBackgroundColor": "#F57C00",
-				"navigationBarTextStyle": "white"
-			}
-		},
-		{
-			"path": "pages/payscore/settlement",
-			"style": {
-				"navigationBarTitleText": "结算完成",
-				"navigationBarBackgroundColor": "#07C160",
-				"navigationBarTextStyle": "white"
-			}
-		},
 		{
 			"path": "pages/orderDetail/orderDetail",
 			"style": {

+ 50 - 37
haha-mp/src/pages/index/index.vue

@@ -64,7 +64,7 @@
 import { ref, onMounted } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
 import { scanDoor } from '../../api/device'
-import { checkPayscoreEnabled } from '../../api/payscore'
+import { preCreatePayscoreOrder } from '../../api/payscore'
 import { getCouponCount } from '../../api/coupon'
 import { isLoggedIn, getToken } from '../../utils/auth'
 import { logger } from '../../utils/logger'
@@ -105,25 +105,6 @@ const scanCode = async () => {
     return;
   }
 
-  // 检查微信支付分开通状态
-  try {
-    const payscoreResult = await checkPayscoreEnabled()
-    if (!payscoreResult.enabled) {
-      // 未开通,跳转到开通页面
-      uni.navigateTo({
-        url: '/pages/payscore/enable'
-      })
-      return
-    }
-  } catch (error: any) {
-    logger.error('检查支付分状态失败:', error)
-    // 如果检查失败,也跳转到开通页面
-    uni.navigateTo({
-      url: '/pages/payscore/enable'
-    })
-    return
-  }
-
   // 调用摄像头扫码
   uni.scanCode({
     success: async function (res) {
@@ -168,23 +149,8 @@ const scanCode = async () => {
       }
 
       // 弹出选择弹窗:查看柜内商品 or 直接开门
-      uni.showActionSheet({
-        itemList: ['查看柜内商品', '直接开门'],
-        success: async (actionRes) => {
-          if (actionRes.tapIndex === 0) {
-            // 查看柜内商品 -> 跳转到商品陈列页
-            uni.navigateTo({
-              url: '/pages/products/products?deviceId=' + deviceId
-            });
-          } else if (actionRes.tapIndex === 1) {
-            // 直接开门 -> 执行现有开门逻辑
-            await doOpenDoor(deviceId);
-          }
-        },
-        fail: () => {
-          logger.log('用户取消选择');
-        }
-      });
+      // 需确认模式:创建支付分订单并拉起用户确认
+      await confirmPayScoreAndOpenDoor(deviceId);
     },
     fail: function (err) {
       logger.log('扫码取消:', err);
@@ -196,6 +162,53 @@ const scanCode = async () => {
   });
 };
 
+/**
+ * 需确认模式:预创建支付分订单 + 用户确认 + 开门
+ */
+const confirmPayScoreAndOpenDoor = async (deviceId: string) => {
+  uni.showLoading({ title: '创建订单...', mask: true });
+
+  try {
+    // 1. 预创建支付分服务订单
+    const result = await preCreatePayscoreOrder({ deviceId });
+
+    if (!result.success || !result.package) {
+      uni.hideLoading();
+      uni.showToast({
+        title: result.errorMsg || '创建支付分订单失败',
+        icon: 'none',
+      });
+      return;
+    }
+
+    uni.hideLoading();
+
+    // 2. 拉起微信支付分确认页面
+    wx.openBusinessView({
+      businessType: 'payscore',
+      extraData: {
+        mch_id: '',
+        package: result.package,
+      },
+      success: async () => {
+        logger.log('支付分确认成功,开始开门');
+        await doOpenDoor(deviceId);
+      },
+      fail: (err: any) => {
+        logger.error('支付分确认取消或失败', err);
+        uni.showToast({ title: '授权已取消', icon: 'none' });
+      },
+    });
+  } catch (error: any) {
+    uni.hideLoading();
+    logger.error('预创建支付分订单失败', error);
+    uni.showToast({
+      title: error?.message || '创建支付分订单失败',
+      icon: 'none',
+    });
+  }
+};
+
 /**
  * 执行开门操作
  */

+ 0 - 735
haha-mp/src/pages/payscore/enable.vue

@@ -1,735 +0,0 @@
-<template>
-  <view class="container">
-    <!-- 微信风格头部 -->
-    <!-- <view class="wx-header">
-      <view class="header-content">
-        <image class="header-logo" src="/static/icons/payscore-white.svg" mode="aspectFit"></image>
-        <text class="header-title">微信支付分</text>
-      </view>
-    </view> -->
-
-    <!-- 主要内容区 -->
-    <view class="content">
-      <!-- 分数要求卡片 - 突出显示 -->
-      <view class="score-card">
-        <view class="score-badge">必需条件</view>
-        <view class="score-main">
-          <text class="score-number">550</text>
-          <text class="score-unit">分及以上</text>
-        </view>
-        <view class="score-desc">
-          <text class="desc-icon">ℹ</text>
-          <text class="desc-text">分数越高,可享受的信用服务越多</text>
-        </view>
-      </view>
-
-      <!-- 核心权益 -->
-      <view class="benefits-section">
-        <text class="section-title">核心权益</text>
-        <view class="benefits-grid">
-          <view class="benefit-card">
-            <view class="benefit-icon benefit-icon-1">
-              <text class="icon-emoji">💳</text>
-            </view>
-            <text class="benefit-title">550分优享</text>
-            <text class="benefit-desc">开门后付款</text>
-          </view>
-          
-          <view class="benefit-card">
-            <view class="benefit-icon benefit-icon-2">
-              <text class="icon-emoji">🛡️</text>
-            </view>
-            <text class="benefit-title">免密支付</text>
-            <text class="benefit-desc">小额自动扣款</text>
-          </view>
-          
-          <view class="benefit-card">
-            <view class="benefit-icon benefit-icon-3">
-              <text class="icon-emoji">⚡</text>
-            </view>
-            <text class="benefit-title">快速开门</text>
-            <text class="benefit-desc">秒速开门</text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 使用须知 -->
-      <view class="info-card">
-        <text class="card-title">使用须知</text>
-
-        <view class="card-content">
-          <view class="notice-item">
-            <text class="notice-icon notice-icon--correct">✓</text>
-            <text class="notice-text">请依次排队购买,有序选购商品</text>
-          </view>
-          <view class="notice-item">
-            <text class="notice-icon notice-icon--correct">✓</text>
-            <text class="notice-text">选购完成后请随手关门,避免影响他人</text>
-          </view>
-          <view class="notice-item">
-            <text class="notice-icon notice-icon--wrong">✗</text>
-            <text class="notice-text">请勿遮挡柜内摄像头,确保商品识别准确</text>
-          </view>
-          <view class="notice-item">
-            <text class="notice-icon notice-icon--wrong">✗</text>
-            <text class="notice-text">请勿向柜中放入杂物或非购买物品</text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 开通流程 -->
-      <view class="steps-section">
-        <text class="section-title">如何开通</text>
-        
-        <view class="step-item">
-          <view class="step-number step-1">1</view>
-          <view class="step-content">
-            <text class="step-title">点击开通按钮</text>
-            <text class="step-desc">授权开启微信支付分服务</text>
-          </view>
-        </view>
-        
-        <view class="step-item">
-          <view class="step-number step-2">2</view>
-          <view class="step-content">
-            <text class="step-title">完成身份验证</text>
-            <text class="step-desc">根据提示完成身份信息验证</text>
-          </view>
-        </view>
-        
-        <view class="step-item">
-          <view class="step-number step-3">3</view>
-          <view class="step-content">
-            <text class="step-title">等待评分结果</text>
-            <text class="step-desc">系统将自动评估您的信用分数</text>
-          </view>
-        </view>
-        
-        <view class="step-item">
-          <view class="step-number step-4">4</view>
-          <view class="step-content">
-            <text class="step-title">开通成功</text>
-            <text class="step-desc">成功后即可使用微信支付分服务</text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 温馨提示 -->
-      <view class="tips-section">
-        <view class="tips-card">
-          <text class="tips-icon">💡</text>
-          <view class="tips-content">
-            <text class="tips-title">温馨提示</text>
-            <view class="tip-item">
-              <text class="tip-dot">•</text>
-              <text class="tip-content">微信支付分评估过程不收取任何费用</text>
-            </view>
-            <view class="tip-item">
-              <text class="tip-dot">•</text>
-              <text class="tip-content">请确保已实名认证并绑定银行卡</text>
-            </view>
-            <view class="tip-item">
-              <text class="tip-dot">•</text>
-              <text class="tip-content">良好的信用记录有助于提升分数</text>
-            </view>
-          </view>
-        </view>
-      </view>
-    </view>
-
-    <!-- 底部操作区 -->
-    <view class="footer">
-      <button class="enable-button" @click="handleEnablePayscore" :loading="isEnabling">
-        <view class="button-content">
-          <image class="button-logo" src="/static/icons/payscore-white.svg" mode="aspectFit"></image>
-          <text class="button-text">{{ isEnabling ? '开通中...' : '立即开通' }}</text>
-        </view>
-      </button>
-      
-      <view class="slogan-wrapper">
-        <BrandSlogan type="score" :dark="true" />
-      </view>
-    </view>
-  </view>
-</template>
-
-<script setup lang="ts">
-import { ref } from 'vue'
-// @ts-ignore
-import { enablePayscore, checkPayscoreEnabled, confirmEnablePayscore } from '@/api/payscore'
-
-import BrandSlogan from '@/components/BrandSlogan.vue'
-import { logger } from '@/utils/logger';
-import { CUSTOMER_SERVICE_PHONE } from '@/utils/config';
-
-declare const wx: any
-
-const isEnabling = ref(false)
-const customerServicePhone = CUSTOMER_SERVICE_PHONE
-
-// 返回上一页
-const goBack = () => {
-  uni.navigateBack()
-}
-
-// 处理开通支付分
-const handleEnablePayscore = async () => {
-  if (isEnabling.value) return
-  
-  try {
-    isEnabling.value = true
-    
-    // 调用开通接口,获取支付分订单信息
-    const result = await enablePayscore()
-    
-    // 如果已开通,直接提示
-    if (result.enabled) {
-      uni.showModal({
-        title: '已开通',
-        content: '您已开通微信支付分,无需重复开通',
-        showCancel: false,
-        success: () => {
-          goBack()
-        }
-      })
-      return
-    }
-    
-    // 调起微信支付分开通页面
-    const { outOrderNo, serviceId, appId } = result
-    
-    if (!outOrderNo || !serviceId) {
-      throw new Error('缺少必要参数')
-    }
-    
-    // 使用微信官方 API 调起支付分开通页面
-    wx.openBusinessView({
-      businessType: 'payscore',
-      extraData: {
-        mch_id: '1612345678', // 商户号,需要替换为真实的
-        service_id: serviceId,
-        out_order_no: outOrderNo
-      },
-      success: (res: any) => {
-        logger.log('调起支付分成功:', res)
-        // 用户已完成授权操作,轮询确认开通状态
-        pollEnableStatus(outOrderNo)
-      },
-      fail: (err: any) => {
-        logger.error('调起支付分失败:', err)
-        // 用户取消或失败,仍然可以手动确认
-        uni.showModal({
-          title: '提示',
-          content: '开通失败或已取消,请重试',
-          confirmText: '再试一次'
-        })
-      }
-    })
-    
-  } catch (error: any) {
-    logger.error('开通支付分失败:', error)
-    uni.showModal({
-      title: '开通失败',
-      content: error.message || '开通过程中出现错误,请稍后重试',
-      confirmText: '再试一次'
-    })
-  } finally {
-    isEnabling.value = false
-  }
-}
-
-// 轮询确认开通状态
-const pollEnableStatus = (outOrderNo: string) => {
-  let count = 0
-  const maxCount = 30 // 最多轮询 30 次
-  
-  const timer = setInterval(async () => {
-    count++
-    
-    if (count >= maxCount) {
-      clearInterval(timer)
-      uni.showModal({
-        title: '提示',
-        content: '开通超时,请重新尝试',
-        confirmText: '重试'
-      })
-      return
-    }
-    
-    try {
-      // 调用确认开通接口(通过统一请求封装,自动携带 token)
-      const data = await confirmEnablePayscore(outOrderNo)
-      
-      if (data?.enabled) {
-        clearInterval(timer)
-        
-        // 开通成功
-        uni.showModal({
-          title: '开通成功',
-          content: '您已成功开通微信支付分,现在可以扫码开门了!',
-          showCancel: false,
-          success: () => {
-            const pages = getCurrentPages()
-            if (pages.length > 1) {
-              const prevPage = pages[pages.length - 2]
-              if (prevPage && (prevPage as any).onPayscoreEnabled) {
-                ;(prevPage as any).onPayscoreEnabled()
-              }
-              uni.navigateBack()
-            } else {
-              uni.reLaunch({
-                url: '/pages/index/index'
-              })
-            }
-          }
-        })
-      }
-    } catch (error) {
-      logger.error('轮询开通状态失败:', error)
-    }
-  }, 2000) // 每 2 秒轮询一次
-}
-
-// 联系客服
-const contactService = () => {
-  uni.makePhoneCall({
-    phoneNumber: CUSTOMER_SERVICE_PHONE
-  })
-}
-
-// 检查支付分状态(页面加载时)
-const checkPayscoreStatus = async () => {
-  try {
-    const result = await checkPayscoreEnabled()
-    if (result.enabled) {
-      // 如果已开通,提示用户
-      uni.showModal({
-        title: '提示',
-        content: '您已开通微信支付分,无需重复开通',
-        showCancel: false,
-        success: () => {
-          goBack()
-        }
-      })
-    }
-  } catch (error) {
-    logger.error('检查支付分状态失败:', error)
-  }
-}
-
-// 页面加载时检查状态
-checkPayscoreStatus()
-</script>
-
-<style lang="scss" scoped>
-/* 微信支付分页面 — 使用设计令牌 */
-
-.container {
-  min-height: 100vh;
-  background: $color-bg-secondary;
-  padding-bottom: 200rpx;
-}
-
-/* 微信风格头部 - 纯色背景 */
-.wx-header {
-  width: 100%;
-  background: $color-wechat-green;
-  padding: 32rpx 40rpx 28rpx;
-  box-sizing: border-box;
-  
-  .header-content {
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    justify-content: center;
-    height: 100%;
-  }
-
-  .header-logo {
-    width: 44rpx;
-    height: 44rpx;
-    margin-right: 16rpx;
-  }
-  
-  .header-title {
-    font-size: 36rpx;
-    font-weight: 600;
-    color: #FFFFFF;
-    text-align: center;
-  }
-  
-  .header-subtitle {
-    display: none;
-  }
-}
-
-.content {
-  padding: 30rpx;
-}
-
-.slogan-wrapper {
-  display: flex;
-  justify-content: center;
-  margin-bottom: 30rpx;
-}
-
-/* 分数要求卡片 - 突出显示 */
-.score-card {
-  background: $color-bg-primary;
-  border-radius: 20rpx;
-  padding: 40rpx 32rpx;
-  margin-bottom: 30rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-  position: relative;
-  overflow: hidden;
-  
-  .score-badge {
-    position: absolute;
-    top: 20rpx;
-    right: 20rpx;
-    background: $color-error;
-    color: #FFFFFF;
-    font-size: 22rpx;
-    padding: 8rpx 16rpx;
-    border-radius: 20rpx;
-    font-weight: 500;
-  }
-  
-  .score-main {
-    display: flex;
-    align-items: baseline;
-    justify-content: center;
-    margin: 32rpx 0 24rpx;
-    
-    .score-number {
-      font-size: 96rpx;
-      font-weight: bold;
-      color: $color-wechat-green;
-      line-height: 1;
-    }
-    
-    .score-unit {
-      font-size: 28rpx;
-      color: $color-text-secondary;
-      margin-left: 16rpx;
-    }
-  }
-  
-  .score-desc {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    background: rgba(7, 193, 96, 0.05);
-    padding: 16rpx 24rpx;
-    border-radius: 12rpx;
-    
-    .desc-icon {
-      font-size: 28rpx;
-      color: $color-wechat-green;
-      margin-right: 12rpx;
-    }
-    
-    .desc-text {
-      font-size: 24rpx;
-      color: $color-text-secondary;
-    }
-  }
-}
-
-/* 核心权益 - 三列布局 */
-.benefits-section {
-  margin-bottom: 30rpx;
-  
-  .section-title {
-    font-size: 32rpx;
-    font-weight: 600;
-    color: $color-text-primary;
-    margin-bottom: 24rpx;
-    display: block;
-  }
-  
-  .benefits-grid {
-    display: flex;
-    justify-content: space-between;
-    gap: 20rpx;
-  }
-  
-  .benefit-card {
-    flex: 1;
-    background: $color-bg-primary;
-    border-radius: 16rpx;
-    padding: 32rpx 20rpx;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    text-align: center;
-    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-    
-    .benefit-icon {
-      width: 80rpx;
-      height: 80rpx;
-      border-radius: 50%;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      margin-bottom: 20rpx;
-      background: $color-wechat-green;
-        
-      .icon-emoji {
-        font-size: 40rpx;
-        color: #FFFFFF;
-      }
-    }
-    
-    .benefit-title {
-      font-size: 28rpx;
-      font-weight: 600;
-      color: $color-text-primary;
-      margin-bottom: 8rpx;
-    }
-    
-    .benefit-desc {
-      font-size: 22rpx;
-      color: $color-text-secondary;
-    }
-  }
-}
-
-/* 信息卡片 */
-.info-card {
-  background: $color-bg-primary;
-  border-radius: 20rpx;
-  padding: 24rpx 32rpx;
-  margin-bottom: 30rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-  
-  .card-title {
-    font-size: 32rpx;
-    font-weight: 600;
-    color: $color-text-primary;
-    margin-bottom: 16rpx;
-    display: block;
-  }
-  
-  .card-content {
-    .notice-item {
-      display: flex;
-      align-items: flex-start;
-      margin-bottom: 16rpx;
-
-      &:last-child {
-        margin-bottom: 0;
-      }
-    }
-
-    .notice-icon {
-      font-size: 28rpx;
-      font-weight: bold;
-      margin-right: 12rpx;
-      flex-shrink: 0;
-      line-height: 1.6;
-
-      &--correct {
-        color: $color-wechat-green;
-      }
-
-      &--wrong {
-        color: $color-error;
-      }
-    }
-
-    .notice-text {
-      font-size: 28rpx;
-      color: $color-text-secondary;
-      line-height: 1.6;
-    }
-  }
-}
-
-/* 开通流程 */
-.steps-section {
-  background: $color-bg-primary;
-  border-radius: 20rpx;
-  padding: 40rpx 32rpx;
-  margin-bottom: 30rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-  
-  .section-title {
-    font-size: 32rpx;
-    font-weight: 600;
-    color: $color-text-primary;
-    margin-bottom: 32rpx;
-    display: block;
-  }
-  
-  .step-item {
-    display: flex;
-    align-items: flex-start;
-    margin-bottom: 32rpx;
-    
-    &:last-child {
-      margin-bottom: 0;
-    }
-    
-    .step-number {
-      width: 48rpx;
-      height: 48rpx;
-      border-radius: 50%;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 28rpx;
-      font-weight: 600;
-      color: #FFFFFF;
-      margin-right: 24rpx;
-      flex-shrink: 0;
-      background: $color-wechat-green;
-      
-      &.step-1,
-      &.step-2,
-      &.step-3,
-      &.step-4 {
-        background: $color-wechat-green;
-      }
-    }
-    
-    .step-content {
-      flex: 1;
-      
-      .step-title {
-        font-size: 28rpx;
-        font-weight: 600;
-        color: $color-text-primary;
-        display: block;
-        margin-bottom: 8rpx;
-      }
-      
-      .step-desc {
-        font-size: 24rpx;
-        color: $color-text-secondary;
-        display: block;
-        line-height: 1.4;
-      }
-    }
-  }
-}
-
-/* 温馨提示 */
-.tips-section {
-  margin-bottom: 30rpx;
-  
-  .tips-card {
-    background: rgba(7, 193, 96, 0.05);
-    border: 2rpx solid rgba(7, 193, 96, 0.2);
-    border-radius: 16rpx;
-    padding: 32rpx 28rpx;
-    display: flex;
-    align-items: flex-start;
-    
-    .tips-icon {
-      font-size: 40rpx;
-      color: $color-wechat-green;
-      margin-right: 20rpx;
-      flex-shrink: 0;
-    }
-    
-    .tips-content {
-      flex: 1;
-      
-      .tips-title {
-        font-size: 28rpx;
-        font-weight: 600;
-        color: $color-text-primary;
-        display: block;
-        margin-bottom: 16rpx;
-      }
-      
-      .tip-item {
-        display: flex;
-        align-items: flex-start;
-        margin-bottom: 12rpx;
-        
-        &:last-child {
-          margin-bottom: 0;
-        }
-        
-        .tip-dot {
-          color: $color-wechat-green;
-          font-size: 28rpx;
-          margin-right: 12rpx;
-          flex-shrink: 0;
-        }
-        
-        .tip-content {
-          font-size: 24rpx;
-          color: $color-text-secondary;
-          line-height: 1.6;
-        }
-      }
-    }
-  }
-}
-
-/* 底部操作区 */
-.footer {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  background: $color-bg-primary;
-  padding: 32rpx 40rpx;
-  padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
-  box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.05);
-  
-  .enable-button {
-    width: 100%;
-    height: 88rpx;
-    background: $color-wechat-green;
-    border-radius: 44rpx;
-    border: none;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    margin-bottom: 20rpx;
-    
-    &[disabled] {
-      background: $color-border;
-      opacity: 0.6;
-    }
-
-    .button-content {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    .button-logo {
-      width: 36rpx;
-      height: 36rpx;
-      margin-right: 12rpx;
-    }
-    
-    .button-text {
-      font-size: 32rpx;
-      font-weight: 600;
-      color: #FFFFFF;
-    }
-  }
-  
-  .service-section {
-    text-align: center;
-    
-    .service-text {
-      font-size: 24rpx;
-      color: $color-text-tertiary;
-      margin-right: 8rpx;
-    }
-    
-    .service-link {
-      font-size: 24rpx;
-      color: $color-wechat-green;
-      font-weight: 500;
-    }
-  }
-}
-</style>

+ 0 - 376
haha-mp/src/pages/payscore/scoreLow.vue

@@ -1,376 +0,0 @@
-<template>
-  <view class="container">
-    <!-- 警告头部 -->
-    <view class="header">
-      <view class="header-bg"></view>
-      <!-- 警告图标 -->
-      <view class="warn-icon-wrap">
-        <svg class="warn-icon" viewBox="0 0 120 120">
-          <circle cx="60" cy="60" r="52" fill="none" stroke="#FFFFFF" stroke-width="4" opacity="0.3"/>
-          <circle cx="60" cy="60" r="44" fill="rgba(255,255,255,0.15)"/>
-          <line x1="60" y1="32" x2="60" y2="70" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round"/>
-          <circle cx="60" cy="86" r="5" fill="#FFFFFF"/>
-        </svg>
-      </view>
-      <text class="header-title">分数未达标</text>
-      <text class="header-subtitle">暂不符合先享后付开通条件</text>
-    </view>
-
-    <!-- 分数卡片 -->
-    <view class="score-card">
-      <text class="score-label">您的微信支付分</text>
-      <view class="score-value-row">
-        <text class="score-number">480</text>
-        <text class="score-unit">分</text>
-      </view>
-      <view class="score-threshold">
-        <text class="threshold-text">550分及以上可开通</text>
-      </view>
-      <!-- 进度条 -->
-      <view class="progress-bar">
-        <view class="progress-track">
-          <view class="progress-fill" :style="{ width: '58%' }"></view>
-        </view>
-        <view class="progress-labels">
-          <text class="progress-label">0</text>
-          <text class="progress-label progress-label--goal">550</text>
-          <text class="progress-label">850+</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 如何提升分数 -->
-    <view class="tips-section">
-      <text class="section-title">如何提升支付分</text>
-
-      <view class="tip-card">
-        <view class="tip-item">
-          <view class="tip-num">1</view>
-          <view class="tip-content">
-            <text class="tip-title">完善个人信息</text>
-            <text class="tip-desc">在微信支付中完善实名信息、绑定银行卡等资料</text>
-          </view>
-        </view>
-
-        <view class="tip-item">
-          <view class="tip-num">2</view>
-          <view class="tip-content">
-            <text class="tip-title">保持良好的支付习惯</text>
-            <text class="tip-desc">按时支付各类生活缴费、信用卡还款等</text>
-          </view>
-        </view>
-
-        <view class="tip-item">
-          <view class="tip-num">3</view>
-          <view class="tip-content">
-            <text class="tip-title">多使用微信支付</text>
-            <text class="tip-desc">日常消费多使用微信支付,积累信用记录</text>
-          </view>
-        </view>
-
-        <view class="tip-item">
-          <view class="tip-num">4</view>
-          <view class="tip-content">
-            <text class="tip-title">遵守信用约定</text>
-            <text class="tip-desc">按时履约,避免产生违约记录</text>
-          </view>
-        </view>
-      </view>
-    </view>
-
-    <!-- 温馨提示 -->
-    <view class="notice-card">
-      <text class="notice-icon">💡</text>
-      <text class="notice-text">微信支付分每月1日更新,保持良好的信用行为有助于提升分数</text>
-    </view>
-
-    <!-- 品牌标语 -->
-    <view class="brand-wrap">
-      <BrandSlogan type="score" :score="550" :dark="true" :mono="true" />
-    </view>
-
-    <!-- 底部按钮 -->
-    <view class="footer">
-      <button class="btn-back" @click="handleBack">暂不开通</button>
-    </view>
-  </view>
-</template>
-
-<script setup lang="ts">
-import BrandSlogan from '@/components/BrandSlogan.vue'
-
-const handleBack = () => {
-  uni.navigateBack({
-    fail: () => {
-      uni.reLaunch({ url: '/pages/index/index' })
-    }
-  })
-}
-</script>
-
-<style lang="scss" scoped>
-.container {
-  min-height: 100vh;
-  background: $color-bg-secondary;
-  padding-bottom: 160rpx;
-}
-
-/* 警告头部 */
-.header {
-  background: linear-gradient(135deg, #FF9800, #F57C00);
-  padding: 48rpx 40rpx 44rpx;
-  text-align: center;
-  position: relative;
-  overflow: hidden;
-}
-
-.header-bg {
-  position: absolute;
-  top: -60rpx;
-  right: -40rpx;
-  width: 240rpx;
-  height: 240rpx;
-  border-radius: 50%;
-  background: rgba(255, 255, 255, 0.06);
-}
-
-.warn-icon-wrap {
-  display: flex;
-  justify-content: center;
-  margin-bottom: 24rpx;
-}
-
-.warn-icon {
-  width: 120rpx;
-  height: 120rpx;
-}
-
-.header-title {
-  font-size: 44rpx;
-  font-weight: 700;
-  color: #FFFFFF;
-  display: block;
-  margin-bottom: 8rpx;
-  letter-spacing: 4rpx;
-}
-
-.header-subtitle {
-  font-size: 26rpx;
-  color: rgba(255, 255, 255, 0.85);
-  display: block;
-}
-
-/* 分数卡片 */
-.score-card {
-  margin: -30rpx 30rpx 30rpx;
-  background: $color-bg-primary;
-  border-radius: 20rpx;
-  padding: 40rpx 32rpx 36rpx;
-  text-align: center;
-  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
-  position: relative;
-  z-index: 1;
-}
-
-.score-label {
-  font-size: 26rpx;
-  color: $color-text-secondary;
-  display: block;
-  margin-bottom: 12rpx;
-}
-
-.score-value-row {
-  display: flex;
-  align-items: baseline;
-  justify-content: center;
-  margin-bottom: 16rpx;
-}
-
-.score-number {
-  font-size: 96rpx;
-  font-weight: 800;
-  color: $color-warning;
-  line-height: 1;
-}
-
-.score-unit {
-  font-size: 32rpx;
-  color: $color-text-secondary;
-  margin-left: 8rpx;
-}
-
-.score-threshold {
-  display: flex;
-  justify-content: center;
-  margin-bottom: 28rpx;
-}
-
-.threshold-text {
-  font-size: 26rpx;
-  color: $color-error;
-  font-weight: 500;
-  background: rgba(244, 67, 54, 0.06);
-  padding: 10rpx 24rpx;
-  border-radius: 20rpx;
-}
-
-/* 进度条 */
-.progress-bar {
-  padding: 0 8rpx;
-}
-
-.progress-track {
-  height: 12rpx;
-  background: $color-bg-tertiary;
-  border-radius: 6rpx;
-  overflow: hidden;
-  margin-bottom: 12rpx;
-}
-
-.progress-fill {
-  height: 100%;
-  background: linear-gradient(90deg, $color-warning, #FF7043);
-  border-radius: 6rpx;
-  transition: width 0.6s ease;
-}
-
-.progress-labels {
-  display: flex;
-  justify-content: space-between;
-}
-
-.progress-label {
-  font-size: 20rpx;
-  color: $color-text-tertiary;
-
-  &--goal {
-    color: $color-error;
-    font-weight: 600;
-  }
-}
-
-/* 提升建议 */
-.tips-section {
-  padding: 0 30rpx;
-  margin-bottom: 24rpx;
-
-  .section-title {
-    font-size: 30rpx;
-    font-weight: 600;
-    color: $color-text-primary;
-    margin-bottom: 20rpx;
-    display: block;
-  }
-}
-
-.tip-card {
-  background: $color-bg-primary;
-  border-radius: 16rpx;
-  padding: 28rpx 28rpx 8rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
-}
-
-.tip-item {
-  display: flex;
-  align-items: flex-start;
-  margin-bottom: 24rpx;
-}
-
-.tip-num {
-  width: 44rpx;
-  height: 44rpx;
-  border-radius: 50%;
-  background: $color-bg-tertiary;
-  color: $color-text-secondary;
-  font-size: 24rpx;
-  font-weight: 700;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-  margin-right: 20rpx;
-}
-
-.tip-content {
-  flex: 1;
-}
-
-.tip-title {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: $color-text-primary;
-  display: block;
-  margin-bottom: 6rpx;
-}
-
-.tip-desc {
-  font-size: 24rpx;
-  color: $color-text-secondary;
-  line-height: 1.5;
-  display: block;
-}
-
-/* 温馨提示 */
-.notice-card {
-  margin: 0 30rpx 24rpx;
-  background: rgba(255, 152, 0, 0.05);
-  border: 2rpx solid rgba(255, 152, 0, 0.15);
-  border-radius: 14rpx;
-  padding: 24rpx 28rpx;
-  display: flex;
-  align-items: flex-start;
-}
-
-.notice-icon {
-  font-size: 36rpx;
-  margin-right: 16rpx;
-  flex-shrink: 0;
-}
-
-.notice-text {
-  font-size: 24rpx;
-  color: $color-text-secondary;
-  line-height: 1.6;
-  flex: 1;
-}
-
-/* 品牌标语 */
-.brand-wrap {
-  display: flex;
-  justify-content: center;
-  padding: 0 30rpx;
-  margin-bottom: 16rpx;
-  opacity: 0.5;
-}
-
-/* 底部按钮 */
-.footer {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  padding: 24rpx 40rpx;
-  padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
-  background: $color-bg-primary;
-  box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.05);
-}
-
-.btn-back {
-  width: 100%;
-  height: 88rpx;
-  background: $color-bg-tertiary;
-  border-radius: 44rpx;
-  border: none;
-  font-size: 32rpx;
-  font-weight: 500;
-  color: $color-text-secondary;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  &:active {
-    background: $color-border;
-    transform: scale(0.98);
-  }
-}
-</style>

+ 0 - 357
haha-mp/src/pages/payscore/settlement.vue

@@ -1,357 +0,0 @@
-<template>
-  <view class="container">
-    <!-- 成功头部 -->
-    <view class="header">
-      <view class="success-icon-wrap">
-        <svg class="success-icon" viewBox="0 0 120 120">
-          <circle cx="60" cy="60" r="52" fill="none" stroke="#FFFFFF" stroke-width="4" opacity="0.3"/>
-          <circle cx="60" cy="60" r="44" fill="rgba(255,255,255,0.15)"/>
-          <path d="M 38 60 L 52 76 L 82 46" fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
-        </svg>
-      </view>
-      <text class="header-title">结算完成</text>
-      <text class="header-subtitle">AI 视觉识别已完成,订单已生成</text>
-    </view>
-
-    <!-- 订单结算卡片 -->
-    <view class="settlement-card">
-      <view class="card-header">
-        <text class="card-title">购买商品</text>
-        <text class="card-order-no">订单号:HHA20260528142001</text>
-      </view>
-
-      <view class="product-list">
-        <view class="product-item">
-          <image class="product-img" src="/static/logo.png" mode="aspectFill"></image>
-          <view class="product-info">
-            <text class="product-name">可口可乐 330ml</text>
-            <text class="product-spec">罐装</text>
-          </view>
-          <view class="product-right">
-            <text class="product-count">×2</text>
-            <text class="product-price">¥7.00</text>
-          </view>
-        </view>
-
-        <view class="product-item">
-          <image class="product-img" src="/static/logo.png" mode="aspectFill"></image>
-          <view class="product-info">
-            <text class="product-name">农夫山泉 550ml</text>
-            <text class="product-spec">瓶装</text>
-          </view>
-          <view class="product-right">
-            <text class="product-count">×1</text>
-            <text class="product-price">¥2.50</text>
-          </view>
-        </view>
-
-        <view class="product-item">
-          <image class="product-img" src="/static/logo.png" mode="aspectFill"></image>
-          <view class="product-info">
-            <text class="product-name">乐事薯片 原味 75g</text>
-            <text class="product-spec">袋装</text>
-          </view>
-          <view class="product-right">
-            <text class="product-count">×1</text>
-            <text class="product-price">¥8.50</text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 支付方式 -->
-      <view class="pay-section">
-        <view class="pay-row">
-          <text class="pay-label">支付方式</text>
-          <view class="pay-method">
-            <image class="pay-icon" src="/static/icons/payscore.svg" mode="aspectFit"></image>
-            <text class="pay-text">微信支付分 · 先享后付</text>
-          </view>
-        </view>
-        <view class="pay-row">
-          <text class="pay-label">扣款方式</text>
-          <text class="pay-value">确认收货后自动扣款</text>
-        </view>
-      </view>
-
-      <!-- 合计 -->
-      <view class="total-row">
-        <text class="total-label">合计</text>
-        <view class="total-right">
-          <text class="total-currency">¥</text>
-          <text class="total-amount">18.00</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 品牌标语 -->
-    <view class="brand-wrap">
-      <BrandSlogan type="orderComplete" serviceType="先享后付" :dark="true" />
-    </view>
-
-    <!-- 底部按钮 -->
-    <view class="footer">
-      <button class="btn-order" @click="goHome">返回首页</button>
-    </view>
-  </view>
-</template>
-
-<script setup lang="ts">
-import BrandSlogan from '@/components/BrandSlogan.vue'
-
-const goHome = () => {
-  uni.reLaunch({
-    url: '/pages/index/index'
-  })
-}
-</script>
-
-<style lang="scss" scoped>
-.container {
-  min-height: 100vh;
-  background: $color-bg-secondary;
-  padding-bottom: 160rpx;
-}
-
-/* 绿色头部 */
-.header {
-  background: $color-wechat-green;
-  padding: 40rpx 40rpx 36rpx;
-  text-align: center;
-}
-
-.success-icon-wrap {
-  display: flex;
-  justify-content: center;
-  margin-bottom: 20rpx;
-}
-
-.success-icon {
-  width: 100rpx;
-  height: 100rpx;
-}
-
-.header-title {
-  font-size: 40rpx;
-  font-weight: 700;
-  color: #FFFFFF;
-  display: block;
-  margin-bottom: 8rpx;
-  letter-spacing: 4rpx;
-}
-
-.header-subtitle {
-  font-size: 24rpx;
-  color: rgba(255, 255, 255, 0.8);
-  display: block;
-}
-
-/* 结算卡片 */
-.settlement-card {
-  margin: -20rpx 30rpx 24rpx;
-  background: $color-bg-primary;
-  border-radius: 20rpx;
-  padding: 32rpx 28rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
-  position: relative;
-  z-index: 1;
-}
-
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 24rpx;
-  padding-bottom: 20rpx;
-  border-bottom: 1rpx solid $color-border;
-}
-
-.card-title {
-  font-size: 30rpx;
-  font-weight: 600;
-  color: $color-text-primary;
-}
-
-.card-order-no {
-  font-size: 22rpx;
-  color: $color-text-tertiary;
-}
-
-/* 商品列表 */
-.product-list {
-  margin-bottom: 8rpx;
-}
-
-.product-item {
-  display: flex;
-  align-items: center;
-  padding: 20rpx 0;
-  border-bottom: 1rpx solid $color-bg-tertiary;
-
-  &:last-child {
-    border-bottom: none;
-  }
-}
-
-.product-img {
-  width: 96rpx;
-  height: 96rpx;
-  border-radius: 12rpx;
-  background: $color-bg-tertiary;
-  flex-shrink: 0;
-}
-
-.product-info {
-  flex: 1;
-  margin-left: 20rpx;
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-}
-
-.product-name {
-  font-size: 28rpx;
-  color: $color-text-primary;
-  font-weight: 500;
-  line-height: 1.4;
-}
-
-.product-spec {
-  font-size: 22rpx;
-  color: $color-text-secondary;
-  margin-top: 4rpx;
-}
-
-.product-right {
-  display: flex;
-  flex-direction: column;
-  align-items: flex-end;
-  flex-shrink: 0;
-  margin-left: 16rpx;
-}
-
-.product-count {
-  font-size: 24rpx;
-  color: $color-text-secondary;
-  margin-bottom: 6rpx;
-}
-
-.product-price {
-  font-size: 30rpx;
-  color: $color-text-primary;
-  font-weight: 600;
-}
-
-/* 支付方式 */
-.pay-section {
-  margin-top: 20rpx;
-  padding-top: 20rpx;
-  border-top: 1rpx solid $color-border;
-}
-
-.pay-row {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 12rpx 0;
-}
-
-.pay-label {
-  font-size: 26rpx;
-  color: $color-text-secondary;
-}
-
-.pay-method {
-  display: flex;
-  align-items: center;
-}
-
-.pay-icon {
-  width: 32rpx;
-  height: 32rpx;
-  margin-right: 8rpx;
-}
-
-.pay-text {
-  font-size: 26rpx;
-  color: $color-text-primary;
-  font-weight: 500;
-}
-
-.pay-value {
-  font-size: 26rpx;
-  color: $color-text-primary;
-}
-
-/* 合计 */
-.total-row {
-  display: flex;
-  justify-content: space-between;
-  align-items: baseline;
-  margin-top: 20rpx;
-  padding-top: 20rpx;
-  border-top: 2rpx solid $color-text-primary;
-}
-
-.total-label {
-  font-size: 30rpx;
-  font-weight: 600;
-  color: $color-text-primary;
-}
-
-.total-right {
-  display: flex;
-  align-items: baseline;
-}
-
-.total-currency {
-  font-size: 28rpx;
-  color: $color-error;
-  font-weight: 600;
-}
-
-.total-amount {
-  font-size: 48rpx;
-  color: $color-error;
-  font-weight: 700;
-  line-height: 1;
-}
-
-/* 品牌标语 */
-.brand-wrap {
-  display: flex;
-  justify-content: center;
-  padding: 0 30rpx;
-  margin-bottom: 16rpx;
-}
-
-/* 底部按钮 */
-.footer {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  padding: 24rpx 40rpx;
-  padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
-  background: $color-bg-primary;
-  box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.05);
-}
-
-.btn-order {
-  width: 100%;
-  height: 88rpx;
-  background: $color-wechat-green;
-  border-radius: 44rpx;
-  border: none;
-  font-size: 32rpx;
-  font-weight: 600;
-  color: #FFFFFF;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  &:active {
-    opacity: 0.9;
-    transform: scale(0.98);
-  }
-}
-</style>

+ 0 - 294
haha-mp/src/pages/payscore/success.vue

@@ -1,294 +0,0 @@
-<template>
-  <view class="container">
-    <!-- 微信绿色头部 -->
-    <view class="header">
-      <view class="header-bg"></view>
-      <!-- 成功图标 -->
-      <view class="success-icon-wrap">
-        <svg class="success-icon" viewBox="0 0 120 120">
-          <circle cx="60" cy="60" r="52" fill="none" stroke="#FFFFFF" stroke-width="4" opacity="0.3"/>
-          <circle cx="60" cy="60" r="44" fill="rgba(255,255,255,0.15)"/>
-          <path d="M 38 60 L 52 76 L 82 46" fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
-        </svg>
-      </view>
-      <text class="header-title">开通成功</text>
-      <text class="header-subtitle">微信支付分已开通</text>
-    </view>
-
-    <!-- 分数卡片 -->
-    <view class="score-card">
-      <text class="score-label">当前微信支付分</text>
-      <view class="score-value-row">
-        <text class="score-number">650</text>
-        <text class="score-unit">分</text>
-      </view>
-      <view class="score-badge">
-        <text class="badge-dot"></text>
-        <text class="badge-text">已达标 · 可享受先享后付服务</text>
-      </view>
-    </view>
-
-    <!-- 权益卡片 -->
-    <view class="benefits-section">
-      <text class="section-title">已解锁权益</text>
-      <view class="benefits-grid">
-        <view class="benefit-card">
-          <view class="benefit-icon benefit-icon-1">
-            <text class="icon-emoji">💳</text>
-          </view>
-          <text class="benefit-title">先享后付</text>
-          <text class="benefit-desc">开门后付款</text>
-        </view>
-
-        <view class="benefit-card">
-          <view class="benefit-icon benefit-icon-2">
-            <text class="icon-emoji">🛡️</text>
-          </view>
-          <text class="benefit-title">免密支付</text>
-          <text class="benefit-desc">小额自动扣款</text>
-        </view>
-
-        <view class="benefit-card">
-          <view class="benefit-icon benefit-icon-3">
-            <text class="icon-emoji">⚡</text>
-          </view>
-          <text class="benefit-title">快速开门</text>
-          <text class="benefit-desc">扫码即开</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 品牌标语 -->
-    <view class="brand-wrap">
-      <BrandSlogan type="score" :score="550" :dark="true" />
-    </view>
-
-    <!-- 底部按钮 -->
-    <view class="footer">
-      <button class="btn-start" @click="handleStart">开始使用</button>
-    </view>
-  </view>
-</template>
-
-<script setup lang="ts">
-import BrandSlogan from '@/components/BrandSlogan.vue'
-
-const handleStart = () => {
-  uni.reLaunch({
-    url: '/pages/index/index'
-  })
-}
-</script>
-
-<style lang="scss" scoped>
-.container {
-  min-height: 100vh;
-  background: $color-bg-secondary;
-  padding-bottom: 160rpx;
-}
-
-/* 绿色头部 */
-.header {
-  background: $color-wechat-green;
-  padding: 48rpx 40rpx 44rpx;
-  text-align: center;
-  position: relative;
-  overflow: hidden;
-}
-
-.header-bg {
-  position: absolute;
-  top: -60rpx;
-  right: -40rpx;
-  width: 240rpx;
-  height: 240rpx;
-  border-radius: 50%;
-  background: rgba(255, 255, 255, 0.06);
-}
-
-.success-icon-wrap {
-  display: flex;
-  justify-content: center;
-  margin-bottom: 24rpx;
-}
-
-.success-icon {
-  width: 120rpx;
-  height: 120rpx;
-}
-
-.header-title {
-  font-size: 44rpx;
-  font-weight: 700;
-  color: #FFFFFF;
-  display: block;
-  margin-bottom: 8rpx;
-  letter-spacing: 4rpx;
-}
-
-.header-subtitle {
-  font-size: 26rpx;
-  color: rgba(255, 255, 255, 0.8);
-  display: block;
-}
-
-/* 分数卡片 */
-.score-card {
-  margin: -30rpx 30rpx 30rpx;
-  background: $color-bg-primary;
-  border-radius: 20rpx;
-  padding: 40rpx 32rpx;
-  text-align: center;
-  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
-  position: relative;
-  z-index: 1;
-}
-
-.score-label {
-  font-size: 26rpx;
-  color: $color-text-secondary;
-  display: block;
-  margin-bottom: 16rpx;
-}
-
-.score-value-row {
-  display: flex;
-  align-items: baseline;
-  justify-content: center;
-  margin-bottom: 20rpx;
-}
-
-.score-number {
-  font-size: 96rpx;
-  font-weight: 800;
-  color: $color-wechat-green;
-  line-height: 1;
-}
-
-.score-unit {
-  font-size: 32rpx;
-  color: $color-text-secondary;
-  margin-left: 8rpx;
-}
-
-.score-badge {
-  display: inline-flex;
-  align-items: center;
-  background: rgba(7, 193, 96, 0.08);
-  padding: 12rpx 28rpx;
-  border-radius: 24rpx;
-}
-
-.badge-dot {
-  width: 12rpx;
-  height: 12rpx;
-  border-radius: 50%;
-  background: $color-wechat-green;
-  margin-right: 10rpx;
-  flex-shrink: 0;
-}
-
-.badge-text {
-  font-size: 24rpx;
-  color: $color-wechat-green;
-}
-
-/* 权益 */
-.benefits-section {
-  padding: 0 30rpx;
-  margin-bottom: 30rpx;
-
-  .section-title {
-    font-size: 30rpx;
-    font-weight: 600;
-    color: $color-text-primary;
-    margin-bottom: 20rpx;
-    display: block;
-  }
-
-  .benefits-grid {
-    display: flex;
-    justify-content: space-between;
-    gap: 20rpx;
-  }
-
-  .benefit-card {
-    flex: 1;
-    background: $color-bg-primary;
-    border-radius: 16rpx;
-    padding: 28rpx 16rpx;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    text-align: center;
-    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
-
-    .benefit-icon {
-      width: 72rpx;
-      height: 72rpx;
-      border-radius: 50%;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      margin-bottom: 16rpx;
-      background: $color-wechat-green;
-
-      .icon-emoji {
-        font-size: 36rpx;
-        color: #FFFFFF;
-      }
-    }
-
-    .benefit-title {
-      font-size: 26rpx;
-      font-weight: 600;
-      color: $color-text-primary;
-      margin-bottom: 6rpx;
-    }
-
-    .benefit-desc {
-      font-size: 22rpx;
-      color: $color-text-secondary;
-    }
-  }
-}
-
-/* 品牌标语 */
-.brand-wrap {
-  display: flex;
-  justify-content: center;
-  padding: 0 30rpx;
-  margin-bottom: 16rpx;
-}
-
-/* 底部按钮 */
-.footer {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  padding: 24rpx 40rpx;
-  padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
-  background: $color-bg-primary;
-  box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.05);
-}
-
-.btn-start {
-  width: 100%;
-  height: 88rpx;
-  background: $color-wechat-green;
-  border-radius: 44rpx;
-  border: none;
-  font-size: 32rpx;
-  font-weight: 600;
-  color: #FFFFFF;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  &:active {
-    opacity: 0.9;
-    transform: scale(0.98);
-  }
-}
-</style>

+ 0 - 17
haha-mp/src/pages/products/products.vue

@@ -116,7 +116,6 @@
 import { ref, computed } from 'vue';
 import { onLoad } from '@dcloudio/uni-app';
 import { getDeviceProducts, scanDoor } from '@/api/device';
-import { checkPayscoreEnabled } from '@/api/payscore';
 import { isLoggedIn } from '@/utils/auth';
 import { logger } from '@/utils/logger';
 import BrandSlogan from '@/components/BrandSlogan.vue';
@@ -208,22 +207,6 @@ const handleOpenDoor = async () => {
     return;
   }
 
-  try {
-    const payscoreResult = await checkPayscoreEnabled();
-    if (!payscoreResult.enabled) {
-      uni.navigateTo({
-        url: '/pages/payscore/enable'
-      });
-      return;
-    }
-  } catch (error: any) {
-    logger.error('检查支付分状态失败:', error);
-    uni.navigateTo({
-      url: '/pages/payscore/enable'
-    });
-    return;
-  }
-
   opening.value = true;
 
   uni.showLoading({

+ 0 - 3
haha-mp/src/utils/config.ts

@@ -47,10 +47,7 @@ export const API_PATHS = {
   cancelOrder: '/order/cancel',
   
   // 支付分相关
-  checkPayscoreEnable: '/payscore/check-enable',
-  enablePayscore: '/payscore/enable',
   createPayscoreOrder: '/payscore/create',
-  confirmEnablePayscore: '/payscore/confirm-enable',
   
   // 优惠券相关
   getMyCoupons: '/coupon/my',

+ 2 - 30
haha-service/src/main/java/com/haha/service/impl/DeviceServiceImpl.java

@@ -43,10 +43,7 @@ import java.util.Map;
 @RequiredArgsConstructor
 public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {
 
-    private static final int PAYSCORE_ENABLED = 1;
-    private static final int PAYSCORE_NOT_ENABLED = 0;
-
-    private final HahaClient hahaClient;
+private final HahaClient hahaClient;
     private final OrderService orderService;
     private final ShopMapper shopMapper;
     private final DeviceMapper deviceMapper;
@@ -231,36 +228,11 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
         return lambdaUpdate().eq(Device::getDeviceId, deviceId).set(Device::getCurrentInventoryHash, inventoryHash).update();
     }
 
-    /**
-     * 检查用户微信支付分开通状态
-     *
-     * @param userId 用户 ID
-     * @throws BusinessException 如果用户不存在或未开通支付分
-     */
-    private void checkUserPayscoreStatus(Long userId) {
-        User user = userService.getById(userId);
-        if (user == null) {
-            log.error("扫码开门用户不存在 - userId: {}", userId);
-            throw new BusinessException(404, "用户不存在");
-        }
-        
-        Integer payscoreEnabled = user.getPayscoreEnabled();
-        if (payscoreEnabled == null || payscoreEnabled != PAYSCORE_ENABLED) {
-            log.warn("用户未开通微信支付分 - userId: {}, payscoreEnabled: {}", userId, payscoreEnabled);
-            throw new BusinessException(403, "请先开通微信支付分后再使用");
-        }
-        
-        log.debug("用户支付分验证通过 - userId: {}", userId);
-    }
-
     @Override
     public OpenDoorVO scanOpenDoor(String deviceId, Long userId) throws HahaException {
         log.info("用户 {} 请求打开设备 {}", userId, deviceId);
 
-        // 1. 检查用户支付分开通状态
-        checkUserPayscoreStatus(userId);
-
-        // 2. 检查设备在线状态
+        // 1. 检查设备在线状态
         DeviceOnlineStatus onlineStatus = hahaClient.getDeviceApi().getOnlineStatus(deviceId);
         if (onlineStatus.getIsOnline() != 1) {
             log.warn("设备 {} 当前离线", deviceId);

+ 10 - 0
haha-service/src/main/java/com/haha/service/impl/HahaCallbackServiceImpl.java

@@ -471,6 +471,16 @@ public class HahaCallbackServiceImpl implements HahaCallbackService {
         if (newOrder != null) {
             doorRecordService.linkOrderId(activityId, newOrder.getId());
             log.info("订单创建成功并已关联开门记录 - orderId: {}", newOrder.getId());
+
+            // 从 Redis 获取预创建的支付分信息并关联到订单(需确认模式)
+            if (payScoreService != null) {
+                try {
+                    ((com.haha.service.payment.payscore.impl.PayScoreServiceImpl) payScoreService)
+                            .applyPreAuthPayScore(newOrder);
+                } catch (Exception e) {
+                    log.warn("关联支付分预授权信息失败 - orderId: {}, error: {}", newOrder.getId(), e.getMessage());
+                }
+            }
         } else {
             log.error("订单创建失败 - activityId: {}", activityId);
         }

+ 11 - 34
haha-service/src/main/java/com/haha/service/payment/payscore/PayScoreService.java

@@ -89,40 +89,6 @@ public interface PayScoreService {
      */
     PayScoreResult syncPayScoreStatus(Long orderId);
 
-    /**
-     * 检查用户支付分开通状态
-     *
-     * @param userId 用户ID
-     * @return 包含 enabled 和 userId 的Map
-     */
-    Map<String, Object> checkPayscoreEnabled(Long userId);
-
-    /**
-     * 开通支付分(生成商户订单号和跳转数据)
-     *
-     * @param userId 用户ID
-     * @return 包含 enabled、userId、outOrderNo 的Map;如已开通则 enabled=true
-     */
-    Map<String, Object> enablePayscore(Long userId);
-
-    /**
-     * 确认开通支付分(用户完成授权后)
-     *
-     * @param userId 用户ID
-     * @param outOrderNo 微信商户订单号
-     * @return 包含 enabled 和 userId 的Map
-     */
-    Map<String, Object> confirmEnablePayscore(Long userId, String outOrderNo);
-
-    /**
-     * 确认开通支付分(从Map params提取outOrderNo)
-     *
-     * @param userId 用户ID
-     * @param params 包含 outOrderNo
-     * @return 开通结果Map
-     */
-    Map<String, Object> confirmUserPayScoreOrder(Long userId, Map<String, Object> params);
-
     /**
      * 创建支付分服务订单(含权限校验)
      *
@@ -244,4 +210,15 @@ public interface PayScoreService {
      * @return 支付分结果
      */
     PayScoreResult queryPayScoreRefund(Long orderId, String outRefundNo);
+
+    /**
+     * 预创建支付分服务订单(需确认模式,开门前调用)
+     * 创建本地订单记录和微信支付分服务订单,返回 package 供前端拉起确认页
+     *
+     * @param userId 用户ID
+     * @param deviceId 设备ID
+     * @param openId 用户微信openId
+     * @return 包含 orderId、outOrderNo、package 的 Map
+     */
+    Map<String, Object> preCreatePayScoreOrder(Long userId, String deviceId, String openId);
 }

+ 130 - 67
haha-service/src/main/java/com/haha/service/payment/payscore/impl/PayScoreServiceImpl.java

@@ -1,6 +1,7 @@
 package com.haha.service.payment.payscore.impl;
 
 import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.haha.common.constant.OrderConstants;
 import com.haha.common.enums.OrderStatus;
 import com.haha.common.enums.PayScoreState;
@@ -16,6 +17,7 @@ import com.haha.service.payment.payscore.PayScoreCreateRequest.PostPayment;
 import com.haha.service.payment.payscore.PayScoreCompleteRequest.PostDiscount;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -26,6 +28,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * 支付分业务服务实现
@@ -47,9 +50,14 @@ public class PayScoreServiceImpl implements PayScoreService {
     @Autowired(required = false)
     private WxPayConfig wxPayConfig;
 
+    @Autowired(required = false)
+    private StringRedisTemplate stringRedisTemplate;
+
+    private static final String REDIS_KEY_PREFIX = "payscore:preauth:";
+    private static final long REDIS_TTL_MINUTES = 10;
+
     private static final BigDecimal DEFAULT_RISK_FUND_AMOUNT = new BigDecimal("99.00");
     private static final String DEFAULT_RISK_FUND_NAME = "DEPOSIT";
-    private static final int PAYSCORE_ENABLED = 1;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -555,50 +563,6 @@ public class PayScoreServiceImpl implements PayScoreService {
 
     // ==================== 用户端支付分业务方法 ====================
 
-    @Override
-    public Map<String, Object> checkPayscoreEnabled(Long userId) {
-        User user = userService.getById(userId);
-        boolean isEnabled = isPayscoreEnabled(user);
-        Map<String, Object> data = new HashMap<>();
-        data.put("enabled", isEnabled);
-        data.put("userId", userId);
-        return data;
-    }
-
-    @Override
-    public Map<String, Object> enablePayscore(Long userId) {
-        User user = userService.getById(userId);
-        if (isPayscoreEnabled(user)) {
-            Map<String, Object> data = new HashMap<>();
-            data.put("enabled", true);
-            data.put("userId", userId);
-            return data;
-        }
-
-        String outOrderNo = generatePayscoreOrderNo(userId);
-        Map<String, Object> data = new HashMap<>();
-        data.put("enabled", false);
-        data.put("userId", userId);
-        data.put("outOrderNo", outOrderNo);
-        return data;
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public Map<String, Object> confirmEnablePayscore(Long userId, String outOrderNo) {
-        User user = userService.getById(userId);
-        PayScoreResult result = queryPayScoreOrder(outOrderNo);
-
-        if (result.isSuccess() && "ACCEPTED".equals(result.getState())) {
-            updateUserPayscoreStatus(user);
-            Map<String, Object> data = new HashMap<>();
-            data.put("enabled", true);
-            data.put("userId", userId);
-            return data;
-        }
-        return null; // 开通失败
-    }
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Map<String, Object> createUserPayScoreOrder(Long userId, Long orderId, String openId,
@@ -746,17 +710,6 @@ public class PayScoreServiceImpl implements PayScoreService {
         return completeUserPayScoreOrder(userId, orderId, totalAmount, payments, discounts);
     }
 
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public Map<String, Object> confirmUserPayScoreOrder(Long userId, Map<String, Object> params) {
-        if (!params.containsKey("outOrderNo")) {
-            return null;
-        }
-
-        String outOrderNo = params.get("outOrderNo").toString();
-        return confirmEnablePayscore(userId, outOrderNo);
-    }
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Map<String, Object> cancelUserPayScoreOrder(Long userId, Map<String, Object> params) {
@@ -770,20 +723,130 @@ public class PayScoreServiceImpl implements PayScoreService {
         return cancelUserPayScoreOrder(userId, orderId, reason);
     }
 
-    private boolean isPayscoreEnabled(User user) {
-        return user != null && user.getPayscoreEnabled() != null && user.getPayscoreEnabled() == PAYSCORE_ENABLED;
-    }
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> preCreatePayScoreOrder(Long userId, String deviceId, String openId) {
+        log.info("[支付分服务] 预创建服务订单(需确认模式)- userId: {}, deviceId: {}", userId, deviceId);
+
+        PayScoreResult checkResult = checkServiceAvailable();
+        if (checkResult != null) {
+            Map<String, Object> errorData = new HashMap<>();
+            errorData.put("success", false);
+            errorData.put("errorMsg", checkResult.getErrorMsg());
+            return errorData;
+        }
+
+        // 如果没有传 openId,从用户表中获取
+        if (openId == null || openId.isEmpty()) {
+            User user = userService.getById(userId);
+            if (user == null || user.getOpenid() == null) {
+                Map<String, Object> errorData = new HashMap<>();
+                errorData.put("success", false);
+                errorData.put("errorMsg", "用户openId未获取,请重新登录");
+                return errorData;
+            }
+            openId = user.getOpenid();
+        }
+
+        // 1. 创建本地预订单(金额为0,识别后回调更新)
+        Order order = new Order();
+        order.setUserId(userId);
+        order.setDeviceId(deviceId);
+        order.setTotalAmount(BigDecimal.ZERO);
+        order.setPayChannel(PaymentChannel.WECHAT_PAYSCORE.getCode());
+        order.setPayType("微信支付分");
+        order.setStatus(OrderStatus.PENDING_PAYMENT.getCode());
+        order.setPayStatus(PayStatus.UNPAID.getCode());
+        orderService.save(order);
+        log.info("[支付分服务] 预订单已创建 - orderId: {}", order.getId());
+
+        // 2. 创建微信支付分服务订单
+        PayScoreCreateRequest request = new PayScoreCreateRequest();
+        request.setOutOrderNo(generateOutOrderNo(order.getId()));
+        request.setServiceIntroduction("智能零售服务");
+        request.setOpenId(openId);
+        request.setRiskFundName("DEPOSIT");
+        request.setRiskFundAmount(DEFAULT_RISK_FUND_AMOUNT);
+        request.setStartTime("OnAccept");
+        request.setStartDeviceId(deviceId);
 
-    private void updateUserPayscoreStatus(User user) {
-        user.setPayscoreEnabled(PAYSCORE_ENABLED);
-        user.setUpdateTime(LocalDateTime.now());
-        boolean updated = userService.updateById(user);
-        if (!updated) {
-            throw new RuntimeException("更新用户状态失败");
+        PayScoreResult result = payScoreStrategy.createServiceOrder(request);
+
+        if (result.isSuccess()) {
+            // 更新订单的支付分字段
+            order.setPayScoreOrderId(result.getOutOrderNo());
+            order.setPayScoreState(result.getState());
+            if (wxPayConfig != null) {
+                order.setServiceId(wxPayConfig.getServiceId());
+            }
+            order.setServiceStartTime(LocalDateTime.now());
+            orderService.updateById(order);
+
+            // 3. 将支付分信息存入 Redis,供回调创建订单时关联
+            if (stringRedisTemplate != null) {
+                String redisKey = REDIS_KEY_PREFIX + deviceId + ":" + userId;
+                JSONObject json = new JSONObject();
+                json.put("payScoreOrderId", result.getOutOrderNo());
+                json.put("payScoreState", result.getState());
+                if (wxPayConfig != null) {
+                    json.put("serviceId", wxPayConfig.getServiceId());
+                }
+                json.put("orderId", order.getId());
+                stringRedisTemplate.opsForValue().set(redisKey, json.toJSONString(), REDIS_TTL_MINUTES, TimeUnit.MINUTES);
+                log.info("[支付分服务] 支付分信息已存入Redis - key: {}, ttl: {}min", redisKey, REDIS_TTL_MINUTES);
+            }
+
+            Map<String, Object> data = new HashMap<>();
+            data.put("success", true);
+            data.put("orderId", order.getId());
+            data.put("outOrderNo", result.getOutOrderNo());
+            data.put("package", result.getPackageStr());
+            log.info("[支付分服务] 预创建服务订单成功 - orderId: {}, outOrderNo: {}", order.getId(), result.getOutOrderNo());
+            return data;
+        } else {
+            log.error("[支付分服务] 预创建服务订单失败 - orderId: {}, error: {}", order.getId(), result.getErrorMsg());
+            Map<String, Object> errorData = new HashMap<>();
+            errorData.put("success", false);
+            errorData.put("errorMsg", result.getErrorMsg());
+            return errorData;
         }
     }
 
-    private String generatePayscoreOrderNo(Long userId) {
-        return "PS" + System.currentTimeMillis() + String.format("%04d", userId % 10000);
+    /**
+     * 从 Redis 获取预创建的支付分信息并关联到订单
+     * 由 HahaCallbackServiceImpl 在创建订单后调用
+     */
+    public void applyPreAuthPayScore(Order order) {
+        if (stringRedisTemplate == null || order == null) {
+            return;
+        }
+
+        String redisKey = REDIS_KEY_PREFIX + order.getDeviceId() + ":" + order.getUserId();
+        String jsonStr = stringRedisTemplate.opsForValue().get(redisKey);
+        if (jsonStr == null) {
+            return;
+        }
+
+        try {
+            JSONObject json = JSON.parseObject(jsonStr);
+            String payScoreOrderId = json.getString("payScoreOrderId");
+            String payScoreState = json.getString("payScoreState");
+            String serviceId = json.getString("serviceId");
+
+            if (payScoreOrderId != null) {
+                order.setPayScoreOrderId(payScoreOrderId);
+                order.setPayScoreState(payScoreState != null ? payScoreState : PayScoreState.CREATED.getCode());
+                order.setServiceId(serviceId);
+                order.setPayChannel(PaymentChannel.WECHAT_PAYSCORE.getCode());
+                order.setPayType("微信支付分");
+                orderService.updateById(order);
+
+                // 删除 Redis 记录,防止重复使用
+                stringRedisTemplate.delete(redisKey);
+                log.info("[支付分服务] 预授权信息已关联到订单 - orderId: {}, payScoreOrderId: {}", order.getId(), payScoreOrderId);
+            }
+        } catch (Exception e) {
+            log.error("[支付分服务] 关联预授权信息失败 - orderId: {}", order.getId(), e);
+        }
     }
 }