Forráskód Böngészése

✨ feat(payment): 实现支付流程和支付成功页面

- 添加payment和payment-success页面
- 实现微信支付调用和状态管理
- 添加支付频率限制和状态流转控制
- 优化订单取消后的退款提示逻辑
- 更新订单列表和详情页的支付跳转链接,增加金额参数

🐛 fix(order): 修复订单取消和支付流程问题

- 完善订单取消成功后的提示信息
- 为已支付订单添加退款流程提示
- 修复支付跳转链接缺少金额参数的问题

♻️ refactor(payment): 优化支付状态管理逻辑

- 增强PaymentStateManager,添加状态历史记录
- 实现支付状态流转验证机制
- 添加PaymentStateFlowManager统一管理支付流程
- 优化支付频率限制算法
yourname 1 hónapja
szülő
commit
e6d8e44ee6

+ 2 - 0
mini/src/app.config.ts

@@ -13,6 +13,8 @@ export default defineAppConfig({
     'pages/order-list/index',
     'pages/order-detail/index',
     'pages/order-submit/index',
+    'pages/payment/index',
+    'pages/payment-success/index',
     'pages/address-manage/index',
     'pages/address-edit/index'
   ],

+ 20 - 4
mini/src/pages/order-detail/index.tsx

@@ -44,18 +44,34 @@ export default function OrderDetailPage() {
       }
       return response.json()
     },
-    onSuccess: () => {
+    onSuccess: (data) => {
       // 取消成功后刷新订单数据
       queryClient.invalidateQueries({ queryKey: ['order', orderId] })
+
+      // 显示取消成功信息
       Taro.showToast({
         title: '订单取消成功',
-        icon: 'success'
+        icon: 'success',
+        duration: 2000
       })
+
+      // 如果订单已支付,显示退款流程信息
+      if (order?.payState === 2) {
+        setTimeout(() => {
+          Taro.showModal({
+            title: '退款处理中',
+            content: '您的退款申请已提交,退款金额将在1-3个工作日内原路退回。',
+            showCancel: false,
+            confirmText: '知道了'
+          })
+        }, 1500)
+      }
     },
     onError: (error) => {
       Taro.showToast({
         title: error.message,
-        icon: 'error'
+        icon: 'error',
+        duration: 3000
       })
     }
   })
@@ -233,7 +249,7 @@ export default function OrderDetailPage() {
               </Button>
               <Button onClick={() => {
                 Taro.navigateTo({
-                  url: `/pages/payment/index?orderId=${order.id}`
+                  url: `/pages/payment/index?orderId=${order.id}&amount=${order.payAmount}`
                 })
               }}>
                 立即支付

+ 1 - 1
mini/src/pages/order-list/index.tsx

@@ -247,7 +247,7 @@ export default function OrderListPage() {
                               onClick={() => {
                                 // 跳转到支付页面
                                 Taro.navigateTo({
-                                  url: `/pages/payment/index?orderId=${order.id}`
+                                  url: `/pages/payment/index?orderId=${order.id}&amount=${order.payAmount}`
                                 })
                               }}
                             >

+ 2 - 2
mini/src/pages/order-submit/index.tsx

@@ -81,9 +81,9 @@ export default function OrderSubmitPage() {
         icon: 'success'
       })
       
-      // 跳转到订单详情页
+      // 跳转到支付页面
       Taro.redirectTo({
-        url: `/pages/order-detail/index?id=${data.orderId}`
+        url: `/pages/payment/index?orderId=${data.orderId}&amount=${totalAmount}`
       })
     },
     onError: (error) => {

+ 154 - 0
mini/src/pages/payment-success/index.tsx

@@ -0,0 +1,154 @@
+/**
+ * 支付成功页面
+ * 显示支付成功信息和后续操作
+ */
+
+import Taro from '@tarojs/taro'
+import { useEffect, useState } from 'react'
+import { View, Text, Button } from '@tarojs/components'
+import { useQuery } from '@tanstack/react-query'
+import { orderClient } from '@/api'
+
+interface PaymentSuccessParams {
+  orderId: number
+  amount: number
+}
+
+const PaymentSuccessPage = () => {
+  const [params, setParams] = useState<PaymentSuccessParams | null>(null)
+
+  // 获取页面参数
+  useEffect(() => {
+    const currentPage = Taro.getCurrentPages().pop()
+    if (currentPage?.options) {
+      const { orderId, amount } = currentPage.options
+      if (orderId && amount) {
+        setParams({
+          orderId: parseInt(orderId),
+          amount: parseFloat(amount)
+        })
+      }
+    }
+  }, [])
+
+  // 查询订单详情
+  const { data: orderDetail } = useQuery({
+    queryKey: ['order', params?.orderId],
+    queryFn: async () => {
+      if (!params?.orderId) throw new Error('订单ID无效')
+      const response = await orderClient[':id'].$get({ param: { id: params.orderId } })
+      if (response.status !== 200) {
+        throw new Error('获取订单详情失败')
+      }
+      const data = await response.json()
+      return data
+    },
+    enabled: !!params?.orderId
+  })
+
+  // 查看订单详情
+  const handleViewOrderDetail = () => {
+    if (params?.orderId) {
+      Taro.redirectTo({
+        url: `/pages/order-detail/index?orderId=${params.orderId}`
+      })
+    }
+  }
+
+  // 返回首页
+  const handleBackToHome = () => {
+    Taro.switchTab({
+      url: '/pages/index/index'
+    })
+  }
+
+  // 查看订单列表
+  const handleViewOrderList = () => {
+    Taro.switchTab({
+      url: '/pages/order-list/index'
+    })
+  }
+
+  if (!params) {
+    return (
+      <View className="min-h-screen bg-gray-50 flex flex-col items-center justify-center">
+        <View className="bg-white rounded-2xl p-8 text-center">
+          <Text className="text-xl text-red-500 mb-4 block">参数错误</Text>
+          <Button onClick={handleBackToHome} className="w-48 h-18 bg-blue-500 text-white rounded-full text-sm">
+            返回首页
+          </Button>
+        </View>
+      </View>
+    )
+  }
+
+  return (
+    <View className="min-h-screen bg-gray-50 p-5">
+      {/* 成功图标 */}
+      <View className="flex justify-center mb-6">
+        <View className="w-24 h-24 bg-green-500 rounded-full flex items-center justify-center">
+          <Text className="text-white text-4xl font-bold">✓</Text>
+        </View>
+      </View>
+
+      {/* 成功信息 */}
+      <View className="bg-white rounded-2xl p-8 mb-5 text-center">
+        <Text className="text-2xl font-bold text-green-500 block mb-4">支付成功</Text>
+        <Text className="text-3xl font-bold text-orange-500 block mb-2">¥{params.amount.toFixed(2)}</Text>
+        <Text className="text-sm text-gray-600 block">订单支付成功,感谢您的购买</Text>
+      </View>
+
+      {/* 订单信息 */}
+      <View className="bg-white rounded-2xl p-6 mb-5">
+        <View className="flex justify-between items-center py-3 border-b border-gray-100">
+          <Text className="text-sm text-gray-600">订单号:</Text>
+          <Text className="text-sm text-gray-800">{orderDetail?.orderNo || `ORD${params.orderId}`}</Text>
+        </View>
+        <View className="flex justify-between items-center py-3 border-b border-gray-100">
+          <Text className="text-sm text-gray-600">支付时间:</Text>
+          <Text className="text-sm text-gray-800">{new Date().toLocaleString()}</Text>
+        </View>
+        <View className="flex justify-between items-center py-3">
+          <Text className="text-sm text-gray-600">支付方式:</Text>
+          <Text className="text-sm text-gray-800">微信支付</Text>
+        </View>
+      </View>
+
+      {/* 操作按钮 */}
+      <View className="space-y-3 mb-5">
+        <Button
+          onClick={handleViewOrderDetail}
+          className="w-full h-22 bg-gradient-to-r from-blue-500 to-blue-400 text-white rounded-full text-lg font-bold"
+        >
+          查看订单详情
+        </Button>
+        <Button
+          onClick={handleViewOrderList}
+          className="w-full h-22 bg-white text-blue-500 border border-blue-500 rounded-full text-lg font-bold"
+        >
+          查看订单列表
+        </Button>
+        <Button
+          onClick={handleBackToHome}
+          className="w-full h-22 bg-gray-100 text-gray-600 border border-gray-300 rounded-full text-sm"
+        >
+          返回首页
+        </Button>
+      </View>
+
+      {/* 温馨提示 */}
+      <View className="bg-white rounded-2xl p-6">
+        <Text className="text-sm font-bold text-gray-800 block mb-4">温馨提示</Text>
+        <Text className="text-xs text-gray-600 leading-relaxed whitespace-pre-line">
+          • 订单详情可在订单列表中查看
+          {'\n'}
+          • 如有问题请联系客服
+          {'\n'}
+          • 感谢您的支持
+        </Text>
+      </View>
+    </View>
+  )
+}
+
+export default PaymentSuccessPage

+ 314 - 0
mini/src/pages/payment/index.tsx

@@ -0,0 +1,314 @@
+/**
+ * 支付页面
+ * 处理微信支付流程和状态管理
+ */
+
+import Taro from '@tarojs/taro'
+import { useEffect, useState } from 'react'
+import { View, Text, Button } from '@tarojs/components'
+import { useQuery } from '@tanstack/react-query'
+import {
+  requestWechatPayment,
+  PaymentStatus,
+  PaymentStateManager,
+  PaymentRateLimiter,
+  retryPayment
+} from '@/utils/payment'
+
+interface PaymentPageParams {
+  orderId: number
+  amount: number
+  orderNo?: string
+}
+
+interface PaymentData {
+  timeStamp: string
+  nonceStr: string
+  package: string
+  signType: string
+  paySign: string
+}
+
+const PaymentPage = () => {
+  const [params, setParams] = useState<PaymentPageParams | null>(null)
+  const [paymentStatus, setPaymentStatus] = useState<PaymentStatus>(PaymentStatus.PENDING)
+  const [isProcessing, setIsProcessing] = useState(false)
+  const [errorMessage, setErrorMessage] = useState('')
+
+  // 获取页面参数
+  useEffect(() => {
+    const currentPage = Taro.getCurrentPages().pop()
+    if (currentPage?.options) {
+      const { orderId, amount, orderNo } = currentPage.options
+      if (orderId && amount) {
+        setParams({
+          orderId: parseInt(orderId),
+          amount: parseFloat(amount),
+          orderNo
+        })
+      }
+    }
+  }, [])
+
+
+  // 获取支付参数
+  const { data: paymentData, isLoading: paymentLoading } = useQuery({
+    queryKey: ['payment-params', params?.orderId],
+    queryFn: async () => {
+      if (!params?.orderId) throw new Error('订单ID无效')
+
+      // 这里应该调用后端API获取微信支付参数
+      // 暂时模拟返回支付参数
+      const mockPaymentData: PaymentData = {
+        timeStamp: Math.floor(Date.now() / 1000).toString(),
+        nonceStr: Math.random().toString(36).substring(2, 15),
+        package: 'prepay_id=wx' + Math.random().toString(36).substring(2, 15),
+        signType: 'RSA',
+        paySign: 'mock_sign_' + Math.random().toString(36).substring(2, 15)
+      }
+
+      return mockPaymentData
+    },
+    enabled: !!params?.orderId && paymentStatus === PaymentStatus.PENDING
+  })
+
+  // 支付状态管理
+  const paymentStateManager = PaymentStateManager.getInstance()
+  const rateLimiter = PaymentRateLimiter.getInstance()
+
+  // 处理支付
+  const handlePayment = async () => {
+    if (!params || !paymentData) {
+      setErrorMessage('支付参数不完整')
+      return
+    }
+
+    // 检查频率限制
+    const rateLimit = rateLimiter.isRateLimited(params.orderId)
+    if (rateLimit.limited) {
+      setErrorMessage(`支付频率过高,请${Math.ceil(rateLimit.remainingTime! / 1000)}秒后重试`)
+      return
+    }
+
+    setIsProcessing(true)
+    setErrorMessage('')
+    setPaymentStatus(PaymentStatus.PROCESSING)
+    paymentStateManager.setPaymentState(params.orderId, PaymentStatus.PROCESSING)
+
+    try {
+      // 记录支付尝试
+      rateLimiter.recordAttempt(params.orderId)
+
+      // 调用微信支付
+      const paymentResult = await requestWechatPayment(paymentData)
+
+      if (paymentResult.success) {
+        // 支付成功
+        setPaymentStatus(PaymentStatus.SUCCESS)
+        paymentStateManager.setPaymentState(params.orderId, PaymentStatus.SUCCESS)
+
+        // 清除频率限制记录
+        rateLimiter.clearAttempts(params.orderId)
+
+        // 跳转到支付成功页面
+        setTimeout(() => {
+          Taro.redirectTo({
+            url: `/pages/payment-success/index?orderId=${params.orderId}&amount=${params.amount}`
+          })
+        }, 1500)
+      } else {
+        // 支付失败
+        setPaymentStatus(PaymentStatus.FAILED)
+        paymentStateManager.setPaymentState(params.orderId, PaymentStatus.FAILED)
+
+        if (paymentResult.type === 'cancel') {
+          setErrorMessage('用户取消支付')
+        } else {
+          setErrorMessage(paymentResult.message || '支付失败')
+        }
+      }
+    } catch (error: any) {
+      console.error('支付处理异常:', error)
+      setPaymentStatus(PaymentStatus.FAILED)
+      paymentStateManager.setPaymentState(params.orderId, PaymentStatus.FAILED)
+      setErrorMessage(error.message || '支付异常')
+    } finally {
+      setIsProcessing(false)
+    }
+  }
+
+  // 重试支付
+  const handleRetryPayment = async () => {
+    if (!params || !paymentData) return
+
+    setIsProcessing(true)
+    setErrorMessage('')
+
+    try {
+      const retryResult = await retryPayment(
+        () => requestWechatPayment(paymentData),
+        3,
+        1000
+      )
+
+      if (retryResult.success) {
+        setPaymentStatus(PaymentStatus.SUCCESS)
+        paymentStateManager.setPaymentState(params.orderId, PaymentStatus.SUCCESS)
+
+        // 跳转到支付成功页面
+        setTimeout(() => {
+          Taro.redirectTo({
+            url: `/pages/payment-success/index?orderId=${params.orderId}&amount=${params.amount}`
+          })
+        }, 1500)
+      } else {
+        setPaymentStatus(PaymentStatus.FAILED)
+        setErrorMessage(retryResult.message || '支付重试失败')
+      }
+    } catch (error: any) {
+      console.error('支付重试异常:', error)
+      setPaymentStatus(PaymentStatus.FAILED)
+      setErrorMessage(error.message || '支付重试异常')
+    } finally {
+      setIsProcessing(false)
+    }
+  }
+
+  // 取消支付
+  const handleCancelPayment = () => {
+    if (params?.orderId) {
+      paymentStateManager.clearPaymentState(params.orderId)
+      rateLimiter.clearAttempts(params.orderId)
+    }
+
+    // 返回上一页
+    Taro.navigateBack()
+  }
+
+  // 渲染支付状态
+  const renderPaymentStatus = () => {
+    switch (paymentStatus) {
+      case PaymentStatus.PENDING:
+        return (
+          <View>
+            <Text className="text-xl font-bold text-orange-500 block mb-2">待支付</Text>
+            <Text className="text-sm text-gray-600 block">请确认支付信息</Text>
+          </View>
+        )
+      case PaymentStatus.PROCESSING:
+        return (
+          <View>
+            <Text className="text-xl font-bold text-blue-500 block mb-2">支付中...</Text>
+            <Text className="text-sm text-gray-600 block">请稍候</Text>
+          </View>
+        )
+      case PaymentStatus.SUCCESS:
+        return (
+          <View>
+            <Text className="text-xl font-bold text-green-500 block mb-2">支付成功</Text>
+            <Text className="text-sm text-gray-600 block">正在跳转...</Text>
+          </View>
+        )
+      case PaymentStatus.FAILED:
+        return (
+          <View>
+            <Text className="text-xl font-bold text-red-500 block mb-2">支付失败</Text>
+            <Text className="text-sm text-gray-600 block">{errorMessage}</Text>
+          </View>
+        )
+      default:
+        return null
+    }
+  }
+
+  if (!params) {
+    return (
+      <View className="min-h-screen bg-gray-50 flex flex-col items-center justify-center">
+        <Text className="text-xl text-red-500 mb-8">参数错误</Text>
+        <Button onClick={() => Taro.navigateBack()} className="w-48 h-18 bg-blue-500 text-white rounded-full text-sm">
+          返回
+        </Button>
+      </View>
+    )
+  }
+
+  return (
+    <View className="min-h-screen bg-gray-50 p-5">
+      {/* 头部 */}
+      <View className="text-center py-6 bg-white rounded-2xl mb-5">
+        <Text className="text-2xl font-bold text-gray-800">支付订单</Text>
+      </View>
+
+      {/* 订单信息 */}
+      <View className="bg-white rounded-2xl p-6 mb-5">
+        <View className="flex justify-between items-center mb-4">
+          <Text className="text-sm text-gray-600">订单号:</Text>
+          <Text className="text-sm text-gray-800">{params.orderNo || `ORD${params.orderId}`}</Text>
+        </View>
+        <View className="flex justify-between items-center">
+          <Text className="text-sm text-gray-600">支付金额:</Text>
+          <Text className="text-2xl font-bold text-orange-500">¥{params.amount.toFixed(2)}</Text>
+        </View>
+      </View>
+
+      {/* 支付状态 */}
+      <View className="bg-white rounded-2xl p-8 mb-5 text-center">
+        {renderPaymentStatus()}
+      </View>
+
+      {/* 支付按钮 */}
+      <View className="mb-5">
+        {paymentStatus === PaymentStatus.PENDING && (
+          <Button
+            onClick={handlePayment}
+            disabled={isProcessing || paymentLoading}
+            className={`w-full h-22 bg-gradient-to-r from-orange-500 to-orange-400 text-white rounded-full text-lg font-bold ${
+              isProcessing ? 'bg-gray-400' : ''
+            }`}
+          >
+            {isProcessing ? '支付中...' : `确认支付 ¥${params.amount.toFixed(2)}`}
+          </Button>
+        )}
+
+        {paymentStatus === PaymentStatus.FAILED && (
+          <View className="flex gap-4">
+            <Button onClick={handleRetryPayment} className="flex-1 h-22 bg-blue-500 text-white rounded-full text-sm">
+              重试支付
+            </Button>
+            <Button onClick={handleCancelPayment} className="flex-1 h-22 bg-gray-100 text-gray-600 border border-gray-300 rounded-full text-sm">
+              取消支付
+            </Button>
+          </View>
+        )}
+
+        {paymentStatus === PaymentStatus.PROCESSING && (
+          <Button disabled className="w-full h-22 bg-gray-100 text-gray-500 rounded-full text-sm">
+            支付处理中...
+          </Button>
+        )}
+
+        {paymentStatus === PaymentStatus.SUCCESS && (
+          <Button disabled className="w-full h-22 bg-gray-100 text-gray-500 rounded-full text-sm">
+            支付成功
+          </Button>
+        )}
+      </View>
+
+      {/* 支付说明 */}
+      <View className="bg-white rounded-2xl p-6">
+        <Text className="text-sm font-bold text-gray-800 block mb-4">支付说明</Text>
+        <Text className="text-xs text-gray-600 leading-relaxed whitespace-pre-line">
+          • 请确保网络连接正常
+          {'\n'}
+          • 支付过程中请勿关闭页面
+          {'\n'}
+          • 如遇支付问题,请尝试重新支付
+          {'\n'}
+          • 支付成功后会自动跳转
+        </Text>
+      </View>
+    </View>
+  )
+}
+
+export default PaymentPage

+ 199 - 1
mini/src/utils/payment.ts

@@ -395,6 +395,7 @@ export enum PaymentStatus {
 export class PaymentStateManager {
   private static instance: PaymentStateManager
   private state: Map<number, PaymentStatus> = new Map()
+  private stateHistory: Map<number, { status: PaymentStatus; timestamp: number }[]> = new Map()
 
   private constructor() {}
 
@@ -409,8 +410,18 @@ export class PaymentStateManager {
    * 设置支付状态
    */
   setPaymentState(orderId: number, status: PaymentStatus): void {
+    const previousStatus = this.state.get(orderId)
     this.state.set(orderId, status)
-    console.log(`订单 ${orderId} 支付状态更新为: ${status}`)
+
+    // 记录状态历史
+    const history = this.stateHistory.get(orderId) || []
+    history.push({
+      status,
+      timestamp: Date.now()
+    })
+    this.stateHistory.set(orderId, history)
+
+    console.log(`订单 ${orderId} 支付状态流转: ${previousStatus || '初始'} -> ${status}`)
   }
 
   /**
@@ -420,6 +431,13 @@ export class PaymentStateManager {
     return this.state.get(orderId)
   }
 
+  /**
+   * 获取支付状态历史
+   */
+  getPaymentStateHistory(orderId: number): { status: PaymentStatus; timestamp: number }[] {
+    return this.stateHistory.get(orderId) || []
+  }
+
   /**
    * 检查是否重复支付
    */
@@ -433,6 +451,7 @@ export class PaymentStateManager {
    */
   clearPaymentState(orderId: number): void {
     this.state.delete(orderId)
+    this.stateHistory.delete(orderId)
   }
 
   /**
@@ -441,6 +460,52 @@ export class PaymentStateManager {
   getAllPaymentStates(): Map<number, PaymentStatus> {
     return new Map(this.state)
   }
+
+  /**
+   * 验证状态流转是否合法
+   */
+  validateStateTransition(orderId: number, newStatus: PaymentStatus): { valid: boolean; reason?: string } {
+    const currentStatus = this.getPaymentState(orderId)
+
+    // 状态流转规则
+    const allowedTransitions: Record<PaymentStatus, PaymentStatus[]> = {
+      [PaymentStatus.PENDING]: [PaymentStatus.PROCESSING, PaymentStatus.CLOSED],
+      [PaymentStatus.PROCESSING]: [PaymentStatus.SUCCESS, PaymentStatus.FAILED, PaymentStatus.CLOSED],
+      [PaymentStatus.SUCCESS]: [PaymentStatus.REFUNDED],
+      [PaymentStatus.FAILED]: [PaymentStatus.PROCESSING, PaymentStatus.CLOSED],
+      [PaymentStatus.REFUNDED]: [],
+      [PaymentStatus.CLOSED]: []
+    }
+
+    // 初始状态允许任何流转
+    if (!currentStatus) {
+      return { valid: true }
+    }
+
+    // 检查是否允许流转
+    const allowed = allowedTransitions[currentStatus] || []
+    if (!allowed.includes(newStatus)) {
+      return {
+        valid: false,
+        reason: `状态流转不合法: ${currentStatus} -> ${newStatus}`
+      }
+    }
+
+    return { valid: true }
+  }
+
+  /**
+   * 安全设置支付状态(带验证)
+   */
+  safeSetPaymentState(orderId: number, status: PaymentStatus): { success: boolean; reason?: string } {
+    const validation = this.validateStateTransition(orderId, status)
+    if (!validation.valid) {
+      return { success: false, reason: validation.reason }
+    }
+
+    this.setPaymentState(orderId, status)
+    return { success: true }
+  }
 }
 
 /**
@@ -497,4 +562,137 @@ export const syncPaymentStatus = async (
   }
 
   return { synced: false }
+}
+
+/**
+ * 支付状态流转管理器
+ */
+export class PaymentStateFlowManager {
+  private static instance: PaymentStateFlowManager
+  private stateManager: PaymentStateManager
+  private rateLimiter: PaymentRateLimiter
+
+  private constructor() {
+    this.stateManager = PaymentStateManager.getInstance()
+    this.rateLimiter = PaymentRateLimiter.getInstance()
+  }
+
+  static getInstance(): PaymentStateFlowManager {
+    if (!PaymentStateFlowManager.instance) {
+      PaymentStateFlowManager.instance = new PaymentStateFlowManager()
+    }
+    return PaymentStateFlowManager.instance
+  }
+
+  /**
+   * 开始支付流程
+   */
+  async startPaymentFlow(orderId: number): Promise<{ success: boolean; reason?: string }> {
+    // 检查频率限制
+    const rateLimit = this.rateLimiter.isRateLimited(orderId)
+    if (rateLimit.limited) {
+      return {
+        success: false,
+        reason: `支付频率过高,请${Math.ceil(rateLimit.remainingTime! / 1000)}秒后重试`
+      }
+    }
+
+    // 检查是否重复支付
+    if (this.stateManager.isDuplicatePayment(orderId)) {
+      return {
+        success: false,
+        reason: '订单正在支付中或已支付成功,请勿重复操作'
+      }
+    }
+
+    // 设置支付中状态
+    const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.PROCESSING)
+    if (!setResult.success) {
+      return setResult
+    }
+
+    // 记录支付尝试
+    this.rateLimiter.recordAttempt(orderId)
+
+    return { success: true }
+  }
+
+  /**
+   * 处理支付成功
+   */
+  handlePaymentSuccess(orderId: number): { success: boolean; reason?: string } {
+    const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.SUCCESS)
+    if (setResult.success) {
+      // 清除频率限制记录
+      this.rateLimiter.clearAttempts(orderId)
+    }
+    return setResult
+  }
+
+  /**
+   * 处理支付失败
+   */
+  handlePaymentFailure(orderId: number): { success: boolean; reason?: string } {
+    const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.FAILED)
+    return setResult
+  }
+
+  /**
+   * 处理支付取消
+   */
+  handlePaymentCancel(orderId: number): { success: boolean; reason?: string } {
+    const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.CLOSED)
+    if (setResult.success) {
+      // 清除频率限制记录
+      this.rateLimiter.clearAttempts(orderId)
+    }
+    return setResult
+  }
+
+  /**
+   * 处理退款
+   */
+  handleRefund(orderId: number): { success: boolean; reason?: string } {
+    return this.stateManager.safeSetPaymentState(orderId, PaymentStatus.REFUNDED)
+  }
+
+  /**
+   * 获取支付状态历史
+   */
+  getPaymentHistory(orderId: number): { status: PaymentStatus; timestamp: number }[] {
+    return this.stateManager.getPaymentStateHistory(orderId)
+  }
+
+  /**
+   * 检查是否可以重试支付
+   */
+  canRetryPayment(orderId: number): { canRetry: boolean; reason?: string } {
+    const currentStatus = this.stateManager.getPaymentState(orderId)
+
+    if (currentStatus === PaymentStatus.FAILED) {
+      return { canRetry: true }
+    }
+
+    if (currentStatus === PaymentStatus.PROCESSING) {
+      return { canRetry: false, reason: '订单正在支付中,请勿重复操作' }
+    }
+
+    if (currentStatus === PaymentStatus.SUCCESS) {
+      return { canRetry: false, reason: '订单已支付成功,无需重复支付' }
+    }
+
+    if (currentStatus === PaymentStatus.CLOSED) {
+      return { canRetry: false, reason: '订单已关闭,无法支付' }
+    }
+
+    return { canRetry: true }
+  }
+
+  /**
+   * 重置支付状态
+   */
+  resetPaymentState(orderId: number): void {
+    this.stateManager.clearPaymentState(orderId)
+    this.rateLimiter.clearAttempts(orderId)
+  }
 }