import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Box, Button, Flex, Text, Badge, VStack, HStack, useToast, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, useDisclosure, Image, Progress, Divider, Input, Icon, Container, } from '@chakra-ui/react'; import { FaWeixin, FaGem, FaCheck, FaQrcode, FaClock, FaRedo, FaCrown, FaStar, FaTimes, FaChevronDown, FaChevronUp, } from 'react-icons/fa'; import { logger } from '../../utils/logger'; import { useAuth } from '../../contexts/AuthContext'; import { useSubscriptionEvents } from '../../hooks/useSubscriptionEvents'; import { subscriptionConfig, themeColors } from '../../views/Pages/Account/subscription-content'; export default function SubscriptionContentNew() { const { user } = useAuth(); const subscriptionEvents = useSubscriptionEvents({ currentSubscription: { plan: user?.subscription_plan || 'free', status: user?.subscription_status || 'inactive', }, }); const [selectedCycle, setSelectedCycle] = useState('monthly'); const [selectedPlan, setSelectedPlan] = useState(null); const [subscriptionPlans, setSubscriptionPlans] = useState([]); const [priceInfo, setPriceInfo] = useState(null); const [loading, setLoading] = useState(false); const [promoCode, setPromoCode] = useState(''); const [promoCodeApplied, setPromoCodeApplied] = useState(false); const [promoCodeError, setPromoCodeError] = useState(''); const [validatingPromo, setValidatingPromo] = useState(false); const [paymentOrder, setPaymentOrder] = useState(null); const [paymentCountdown, setPaymentCountdown] = useState(300); const [autoCheckInterval, setAutoCheckInterval] = useState(null); const [forceUpdating, setForceUpdating] = useState(false); const [openFaqIndex, setOpenFaqIndex] = useState(null); const { isOpen, onOpen, onClose } = useDisclosure(); const toast = useToast(); // 倒计时更新 useEffect(() => { let timer: any; if (paymentCountdown > 0) { timer = setInterval(() => { setPaymentCountdown((prev) => { if (prev <= 1) { handlePaymentExpire(); return 0; } return prev - 1; }); }, 1000); } return () => clearInterval(timer); }, [paymentCountdown]); // 组件卸载时清理定时器 useEffect(() => { return () => { stopAutoPaymentCheck(); }; }, []); // 组件加载时获取套餐数据 useEffect(() => { fetchSubscriptionPlans(); }, []); const fetchSubscriptionPlans = async () => { try { logger.debug('SubscriptionContentNew', '正在获取订阅套餐'); const response = await fetch('/api/subscription/plans'); if (response.ok) { const data = await response.json(); if (data.success && Array.isArray(data.data)) { const validPlans = data.data.filter( (plan: any) => plan && plan.name && typeof plan.monthly_price === 'number' && typeof plan.yearly_price === 'number' ); logger.debug('SubscriptionContentNew', '套餐加载成功', { status: response.status, validPlansCount: validPlans.length, }); setSubscriptionPlans(validPlans); } else { logger.warn('SubscriptionContentNew', '套餐数据格式异常', { data }); setSubscriptionPlans([]); } } else { logger.error('SubscriptionContentNew', 'fetchSubscriptionPlans', new Error(`HTTP ${response.status}`)); setSubscriptionPlans([]); } } catch (error) { logger.error('SubscriptionContentNew', 'fetchSubscriptionPlans', error); setSubscriptionPlans([]); } }; const handlePaymentExpire = () => { stopAutoPaymentCheck(); toast({ title: '支付二维码已过期', description: '请重新创建订单', status: 'warning', duration: 3000, isClosable: true, }); }; const stopAutoPaymentCheck = () => { if (autoCheckInterval) { clearInterval(autoCheckInterval); setAutoCheckInterval(null); } }; const formatTime = (seconds: number) => { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; }; // 计算价格 const calculatePrice = async (plan: any, cycle: string, promoCodeValue: any = null) => { try { const validPromoCode = promoCodeValue && typeof promoCodeValue === 'string' && promoCodeValue.trim() ? promoCodeValue.trim() : null; const response = await fetch('/api/subscription/calculate-price', { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ to_plan: plan.name, to_cycle: cycle, promo_code: validPromoCode, }), }); if (response.ok) { const data = await response.json(); if (data.success) { setPriceInfo(data.data); return data.data; } } return null; } catch (error) { logger.error('SubscriptionContent', 'calculatePrice', error); return null; } }; // 验证优惠码 const handleValidatePromoCode = async () => { const trimmedCode = promoCode.trim(); if (!trimmedCode) { setPromoCodeError('请输入优惠码'); return; } if (!selectedPlan) { setPromoCodeError('请先选择套餐'); return; } setValidatingPromo(true); setPromoCodeError(''); try { const result = await calculatePrice(selectedPlan, selectedCycle, trimmedCode); if (result && !result.promo_error) { setPromoCodeApplied(true); toast({ title: '优惠码已应用', description: `节省 ¥${result.discount_amount.toFixed(2)}`, status: 'success', duration: 3000, isClosable: true, }); } else { setPromoCodeError(result?.promo_error || '优惠码无效'); setPromoCodeApplied(false); } } catch (error) { setPromoCodeError('验证失败,请重试'); setPromoCodeApplied(false); } finally { setValidatingPromo(false); } }; const handleRemovePromoCode = async () => { setPromoCode(''); setPromoCodeApplied(false); setPromoCodeError(''); if (selectedPlan) { await calculatePrice(selectedPlan, selectedCycle, null); } }; const handleSubscribe = async (plan: any) => { if (!user) { toast({ title: '请先登录', description: '登录后即可订阅', status: 'warning', duration: 3000, isClosable: true, }); return; } subscriptionEvents.trackPricingPlanSelected( plan.name, selectedCycle, getCurrentPrice(plan) ); setSelectedPlan(plan); await calculatePrice(plan, selectedCycle, promoCodeApplied ? promoCode : null); onOpen(); }; const handleCreatePaymentOrder = async () => { if (!selectedPlan || !user) return; setLoading(true); try { const price = priceInfo?.final_amount || getCurrentPrice(selectedPlan); // 检查是否为免费升级(剩余价值足够抵扣新套餐价格) if (price === 0 && priceInfo?.is_upgrade) { const response = await fetch('/api/subscription/free-upgrade', { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ plan_name: selectedPlan.name, billing_cycle: selectedCycle, }), }); const data = await response.json(); if (data.success) { subscriptionEvents.trackPaymentSuccessful({ planName: selectedPlan.name, paymentMethod: 'free_upgrade', amount: 0, orderId: 'free_upgrade', transactionId: 'free_upgrade', }); toast({ title: '升级成功!', description: data.message, status: 'success', duration: 5000, isClosable: true, }); onClose(); setTimeout(() => window.location.reload(), 2000); return; } else { throw new Error(data.error || '免费升级失败'); } } subscriptionEvents.trackPaymentInitiated({ planName: selectedPlan.name, paymentMethod: 'wechat_pay', amount: price, billingCycle: selectedCycle, }); const response = await fetch('/api/payment/create-order', { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ plan_name: selectedPlan.name, billing_cycle: selectedCycle, promo_code: promoCodeApplied ? promoCode : null, }), }); if (response.ok) { const data = await response.json(); if (data.success) { setPaymentOrder(data.data); setPaymentCountdown(30 * 60); // 30分钟 startAutoPaymentCheck(data.data.id); toast({ title: '订单创建成功', description: '请使用微信扫描二维码完成支付', status: 'success', duration: 3000, isClosable: true, }); } else { throw new Error(data.message || '创建订单失败'); } } else { throw new Error('网络请求失败'); } } catch (error: any) { subscriptionEvents.trackPaymentFailed( { planName: selectedPlan.name, paymentMethod: 'wechat_pay', amount: getCurrentPrice(selectedPlan), }, error.message ); toast({ title: '创建订单失败', description: error.message, status: 'error', duration: 3000, isClosable: true, }); } finally { setLoading(false); } }; const startAutoPaymentCheck = (orderId: string) => { const checkInterval = setInterval(async () => { try { const response = await fetch(`/api/payment/order/${orderId}/status`, { credentials: 'include', }); if (response.ok) { const data = await response.json(); if (data.success && data.data.status === 'paid') { clearInterval(checkInterval); setAutoCheckInterval(null); subscriptionEvents.trackPaymentSuccessful({ planName: selectedPlan?.name, paymentMethod: 'wechat_pay', amount: paymentOrder?.amount, orderId: orderId, transactionId: data.data.transaction_id, }); toast({ title: '支付成功!', description: '您的订阅已激活', status: 'success', duration: 5000, isClosable: true, }); onClose(); setTimeout(() => window.location.reload(), 2000); } } } catch (error) { logger.error('SubscriptionContent', 'checkPaymentStatus', error); } }, 3000); setAutoCheckInterval(checkInterval as any); }; const handleForceUpdate = async () => { if (!paymentOrder) return; setForceUpdating(true); try { const response = await fetch(`/api/payment/order/${paymentOrder.order_id}/status`, { credentials: 'include', }); if (response.ok) { const data = await response.json(); if (data.success && data.data.status === 'paid') { toast({ title: '支付成功!', description: '您的订阅已激活', status: 'success', duration: 5000, isClosable: true, }); onClose(); setTimeout(() => window.location.reload(), 2000); } else { toast({ title: '未检测到支付', description: '请确认已完成支付后重试', status: 'info', duration: 3000, isClosable: true, }); } } } catch (error: any) { toast({ title: '查询失败', description: error.message, status: 'error', duration: 3000, isClosable: true, }); } finally { setForceUpdating(false); } }; // 合并数据库数据和前端配置 const getMergedPlans = () => { // 如果数据库还没有加载数据,使用静态配置 if (subscriptionPlans.length === 0) { return subscriptionConfig.plans; } // 合并数据库价格和前端UI配置 return subscriptionConfig.plans.map((configPlan: any) => { const dbPlan = subscriptionPlans.find((p: any) => p.name === configPlan.name); if (!dbPlan) { return configPlan; // 如果数据库中没有,使用前端配置 } // 解析数据库中的 pricing_options JSON let pricingOptions = configPlan.pricingOptions; if (dbPlan.pricing_options) { try { const parsedOptions = typeof dbPlan.pricing_options === 'string' ? JSON.parse(dbPlan.pricing_options) : dbPlan.pricing_options; if (Array.isArray(parsedOptions) && parsedOptions.length > 0) { pricingOptions = parsedOptions.map((opt: any) => ({ cycleKey: opt.cycle_key, label: opt.label, months: opt.months, price: parseFloat(opt.price), originalPrice: opt.original_price ? parseFloat(opt.original_price) : null, discountPercent: opt.discount_percent || 0, })); } } catch (error) { logger.error('SubscriptionContentNew', '解析pricing_options失败', error); } } // 合并数据,数据库价格优先 return { ...configPlan, monthly_price: dbPlan.monthly_price, yearly_price: dbPlan.yearly_price, pricingOptions: pricingOptions, displayName: dbPlan.display_name || configPlan.displayName, description: dbPlan.description || configPlan.description, }; }); }; const getCurrentPrice = (plan: any) => { if (!plan || plan.name === 'free') return 0; const option = plan.pricingOptions?.find( (opt: any) => opt.cycleKey === selectedCycle ); return option ? option.price : plan.pricingOptions?.[0]?.price || 0; }; const getCurrentPriceOption = (plan: any) => { if (!plan || plan.name === 'free') return null; return plan.pricingOptions?.find((opt: any) => opt.cycleKey === selectedCycle); }; const getIconComponent = (iconName: string) => { const icons: any = { star: FaStar, gem: FaGem, crown: FaCrown, }; return icons[iconName] || FaStar; }; // 获取按钮文字 const getButtonText = (plan: any) => { const currentPlanName = user?.subscription_type; const isActive = user?.subscription_status === 'active'; if (!isActive || !currentPlanName || currentPlanName === 'free') { return `选择${plan.displayName}`; } if (currentPlanName === plan.name) { // 同级续费 return `续费${plan.displayName}`; } // 升级或降级 if (currentPlanName === 'pro' && plan.name === 'max') { return `升级为${plan.displayName}`; } if (currentPlanName === 'max' && plan.name === 'pro') { return `到期后切换到${plan.displayName}`; } return `选择${plan.displayName}`; }; // 判断按钮是否可点击 const isButtonDisabled = (plan: any) => { return false; // 所有套餐都可以选择,包括当前套餐(续费) }; return ( {/* 背景光晕 */} {/* 标题区域 */} 订阅方案 立即开启智能决策 {/* 当前订阅状态 */} {user && user.subscription_type && user.subscription_type !== 'free' && user.subscription_status === 'active' && ( 当前订阅: {user.subscription_type === 'max' ? 'Max 旗舰版' : 'Pro 专业版'} {user.billing_cycle && ( {user.billing_cycle === 'monthly' ? '月付' : user.billing_cycle === 'quarterly' ? '季付' : user.billing_cycle === 'semiannual' ? '半年付' : user.billing_cycle === 'yearly' ? '年付' : user.billing_cycle} )} 使用中 到期时间 {user.subscription_end_date ? new Date(user.subscription_end_date).toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }) : '永久有效' } {user.subscription_end_date && (() => { const endDate = new Date(user.subscription_end_date); const today = new Date(); const daysLeft = Math.ceil((endDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); if (daysLeft > 0 && daysLeft <= 30) { return ( ⚠️ 还有 {daysLeft} 天到期,记得及时续费哦 ); } return null; })()} )} {/* 计费周期选择器 */} 选择计费周期 · 时长越长优惠越大 {getMergedPlans()[1]?.pricingOptions?.map((option: any, index: number) => ( {option.discountPercent > 0 && ( 省{option.discountPercent}% )} ))} {(() => { const currentOption = getMergedPlans()[1]?.pricingOptions?.find( (opt: any) => opt.cycleKey === selectedCycle ); if (currentOption && currentOption.discountPercent > 0) { return ( 当前选择可节省 {currentOption.discountPercent}% 的费用 ); } return null; })()} {/* 套餐卡片 - 借鉴 index.pug 设计 */} {getMergedPlans().slice(1).map((plan: any, index: number) => { const IconComponent = getIconComponent(plan.icon); const currentPriceOption = getCurrentPriceOption(plan); const isCurrentPlan = user?.subscription_type === plan.name && user?.subscription_status === 'active'; const isPremium = plan.name === 'max'; return ( {/* 套餐标题 */} {plan.displayName} {/* 价格卡片 */} ¥{getCurrentPrice(plan)} / {currentPriceOption?.label || '月'} {/* 功能列表 */} {plan.features.map((feature: any, idx: number) => ( {feature.name} {feature.limit && ( ({feature.limit}) )} ))} ); })} {/* FAQ 区域 */} 常见问题 {subscriptionConfig.faqs.map((faq: any, index: number) => ( setOpenFaqIndex(openFaqIndex === index ? null : index)} > {faq.question} {openFaqIndex === index && ( {faq.answer.split('\n').map((line: string, idx: number) => ( {line} ))} )} ))} {/* 支付模态框 */} 微信支付 {!paymentOrder ? ( {/* 订阅类型提示 */} {selectedPlan && priceInfo && ( <> {priceInfo.is_upgrade && ( {priceInfo.final_amount === 0 ? `恭喜!您的当前订阅剩余价值足够直接升级到${selectedPlan.displayName},无需支付额外费用!` : `升级到${selectedPlan.displayName},立即生效!按差价补缴费用`} )} {priceInfo.is_downgrade && ( 当前{priceInfo.current_plan?.toUpperCase()}订阅到期后自动切换到{selectedPlan.displayName} )} {priceInfo.is_renewal && ( 续费{selectedPlan.displayName},在当前到期日基础上延长时长 )} )} {/* 价格明细 */} {selectedPlan && priceInfo && ( {selectedPlan.displayName} · {selectedCycle === 'monthly' ? '月付' : selectedCycle === 'quarterly' ? '季付' : selectedCycle === 'semiannual' ? '半年付' : '年付'} ¥{priceInfo.original_price?.toFixed(2) || getCurrentPrice(selectedPlan).toFixed(2)} {/* 升级抵扣价值 */} {priceInfo.is_upgrade && priceInfo.remaining_value > 0 && ( 当前订阅剩余价值抵扣 -¥{priceInfo.remaining_value.toFixed(2)} )} {/* 优惠码折扣 */} {promoCodeApplied && priceInfo.discount_amount > 0 && ( 优惠码折扣 -¥{priceInfo.discount_amount.toFixed(2)} )} 实付金额: ¥{priceInfo.final_amount.toFixed(2)} )} {/* 优惠码输入 */} {selectedPlan && ( { setPromoCode(e.target.value.toUpperCase()); setPromoCodeError(''); }} size="md" isDisabled={promoCodeApplied} bg="rgba(255, 255, 255, 0.05)" border="1px solid rgba(255, 255, 255, 0.1)" color="white" _placeholder={{ color: 'rgba(255, 255, 255, 0.4)' }} _hover={{ borderColor: 'rgba(212, 175, 55, 0.3)' }} _focus={{ borderColor: '#D4AF37', boxShadow: '0 0 0 1px #D4AF37' }} /> {promoCodeError && ( {promoCodeError} )} {promoCodeApplied && priceInfo && ( 优惠码已应用!节省 ¥{priceInfo.discount_amount.toFixed(2)} )} )} ) : ( 请使用微信扫描二维码完成支付 {/* 倒计时 */} 二维码有效时间: {formatTime(paymentCountdown)} {/* 二维码 */} {paymentOrder.qr_code_url ? ( 微信支付二维码 ) : ( )} {/* 订单信息 */} 订单号: {paymentOrder.order_no} 支付金额: ¥{paymentOrder.amount} {/* 操作按钮 */} )} ); }