diff --git a/src/components/Subscription/SubscriptionContentNew.tsx b/src/components/Subscription/SubscriptionContentNew.tsx
index f1d856eb..f92fb3a3 100644
--- a/src/components/Subscription/SubscriptionContentNew.tsx
+++ b/src/components/Subscription/SubscriptionContentNew.tsx
@@ -47,37 +47,37 @@ export default function SubscriptionContentNew() {
const subscriptionEvents = useSubscriptionEvents({
currentSubscription: {
plan: user?.subscription_plan || 'free',
- status: user?.subscription_status || 'none',
+ status: user?.subscription_status || 'inactive',
},
});
- const toast = useToast();
- const { isOpen: isPaymentModalOpen, onOpen: onPaymentModalOpen, onClose: onPaymentModalClose } = useDisclosure();
-
- // State
+ const [selectedCycle, setSelectedCycle] = useState('yearly');
const [selectedPlan, setSelectedPlan] = useState(null);
- const [selectedCycle, setSelectedCycle] = useState('yearly'); // 默认年付
- const [paymentOrder, setPaymentOrder] = useState(null);
+ const [priceInfo, setPriceInfo] = useState(null);
const [loading, setLoading] = useState(false);
- const [paymentCountdown, setPaymentCountdown] = useState(0);
- const [checkingPayment, setCheckingPayment] = useState(false);
- const [autoCheckInterval, setAutoCheckInterval] = useState(null);
- const [forceUpdating, setForceUpdating] = useState(false);
- const [openFaqIndex, setOpenFaqIndex] = useState(null);
const [promoCode, setPromoCode] = useState('');
const [promoCodeApplied, setPromoCodeApplied] = useState(false);
const [promoCodeError, setPromoCodeError] = useState('');
const [validatingPromo, setValidatingPromo] = useState(false);
- const [priceInfo, setPriceInfo] = useState(null);
+
+ 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;
+ let timer: any;
if (paymentCountdown > 0) {
timer = setInterval(() => {
setPaymentCountdown((prev) => {
if (prev <= 1) {
- handlePaymentExpired();
+ handlePaymentExpire();
return 0;
}
return prev - 1;
@@ -94,9 +94,7 @@ export default function SubscriptionContentNew() {
};
}, []);
- const handlePaymentExpired = () => {
- setPaymentOrder(null);
- setPaymentCountdown(0);
+ const handlePaymentExpire = () => {
stopAutoPaymentCheck();
toast({
title: '支付二维码已过期',
@@ -114,14 +112,14 @@ export default function SubscriptionContentNew() {
}
};
- const formatTime = (seconds) => {
+ 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, cycle, promoCodeValue = null) => {
+ const calculatePrice = async (plan: any, cycle: string, promoCodeValue: any = null) => {
try {
const validPromoCode = promoCodeValue && typeof promoCodeValue === 'string' && promoCodeValue.trim()
? promoCodeValue.trim()
@@ -201,10 +199,11 @@ export default function SubscriptionContentNew() {
}
};
- const handleSubscribe = async (plan) => {
+ const handleSubscribe = async (plan: any) => {
if (!user) {
toast({
title: '请先登录',
+ description: '登录后即可订阅',
status: 'warning',
duration: 3000,
isClosable: true,
@@ -220,11 +219,11 @@ export default function SubscriptionContentNew() {
setSelectedPlan(plan);
await calculatePrice(plan, selectedCycle, promoCodeApplied ? promoCode : null);
- onPaymentModalOpen();
+ onOpen();
};
- const handleCreateOrder = async () => {
- if (!selectedPlan) return;
+ const handleCreatePaymentOrder = async () => {
+ if (!selectedPlan || !user) return;
setLoading(true);
try {
@@ -235,7 +234,6 @@ export default function SubscriptionContentNew() {
paymentMethod: 'wechat_pay',
amount: price,
billingCycle: selectedCycle,
- orderId: null,
});
const response = await fetch('/api/payment/create-order', {
@@ -245,7 +243,7 @@ export default function SubscriptionContentNew() {
},
credentials: 'include',
body: JSON.stringify({
- plan_name: selectedPlan.name,
+ plan_type: selectedPlan.name,
billing_cycle: selectedCycle,
promo_code: promoCodeApplied ? promoCode : null,
}),
@@ -255,12 +253,12 @@ export default function SubscriptionContentNew() {
const data = await response.json();
if (data.success) {
setPaymentOrder(data.data);
- setPaymentCountdown(30 * 60);
- startAutoPaymentCheck(data.data.id);
+ setPaymentCountdown(300);
+ startAutoPaymentCheck(data.data.order_id);
toast({
- title: '订单创建成功',
- description: '请使用微信扫描二维码完成支付',
+ title: '订单已创建',
+ description: '请使用微信扫码支付',
status: 'success',
duration: 3000,
isClosable: true,
@@ -269,9 +267,9 @@ export default function SubscriptionContentNew() {
throw new Error(data.message || '创建订单失败');
}
} else {
- throw new Error('网络错误');
+ throw new Error('网络请求失败');
}
- } catch (error) {
+ } catch (error: any) {
subscriptionEvents.trackPaymentFailed(
{
planName: selectedPlan.name,
@@ -293,7 +291,7 @@ export default function SubscriptionContentNew() {
}
};
- const startAutoPaymentCheck = (orderId) => {
+ const startAutoPaymentCheck = (orderId: string) => {
const checkInterval = setInterval(async () => {
try {
const response = await fetch(`/api/payment/order/${orderId}/status`, {
@@ -302,7 +300,7 @@ export default function SubscriptionContentNew() {
if (response.ok) {
const data = await response.json();
- if (data.success && data.payment_success) {
+ if (data.success && data.data.status === 'paid') {
clearInterval(checkInterval);
setAutoCheckInterval(null);
@@ -310,129 +308,65 @@ export default function SubscriptionContentNew() {
planName: selectedPlan?.name,
paymentMethod: 'wechat_pay',
amount: paymentOrder?.amount,
- billingCycle: selectedCycle,
orderId: orderId,
- transactionId: data.transaction_id,
+ transactionId: data.data.transaction_id,
});
toast({
title: '支付成功!',
- description: '订阅已激活,正在跳转...',
+ description: '您的订阅已激活',
status: 'success',
- duration: 3000,
- isClosable: true,
- });
-
- setTimeout(() => {
- onPaymentModalClose();
- window.location.reload();
- }, 2000);
- }
- }
- } catch (error) {
- logger.error('SubscriptionContent', 'startAutoPaymentCheck', error);
- }
- }, 10000);
-
- setAutoCheckInterval(checkInterval);
- };
-
- const handleCheckPaymentStatus = async () => {
- if (!paymentOrder) return;
-
- setCheckingPayment(true);
- try {
- const response = await fetch(`/api/payment/order/${paymentOrder.id}/status`, {
- credentials: 'include',
- });
-
- if (response.ok) {
- const data = await response.json();
- if (data.success) {
- if (data.payment_success) {
- stopAutoPaymentCheck();
- toast({
- title: '支付成功!',
- description: '订阅已激活,正在跳转...',
- status: 'success',
- duration: 3000,
- isClosable: true,
- });
-
- setTimeout(() => {
- onPaymentModalClose();
- window.location.reload();
- }, 2000);
- } else {
- toast({
- title: '支付状态检查',
- description: data.message || '还未检测到支付,请继续等待',
- status: 'info',
duration: 5000,
isClosable: true,
});
+
+ onClose();
+ setTimeout(() => window.location.reload(), 2000);
}
- } else {
- throw new Error(data.error || '查询失败');
}
- } else {
- throw new Error('网络错误');
+ } catch (error) {
+ logger.error('SubscriptionContent', 'checkPaymentStatus', error);
}
- } catch (error) {
- toast({
- title: '查询失败',
- description: error.message,
- status: 'error',
- duration: 3000,
- isClosable: true,
- });
- } finally {
- setCheckingPayment(false);
- }
+ }, 3000);
+
+ setAutoCheckInterval(checkInterval as any);
};
- const handleForceUpdatePayment = async () => {
+ const handleForceUpdate = async () => {
if (!paymentOrder) return;
setForceUpdating(true);
try {
- const response = await fetch(`/api/payment/order/${paymentOrder.id}/force-update`, {
- method: 'POST',
+ 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.payment_success) {
- stopAutoPaymentCheck();
-
+ if (data.success && data.data.status === 'paid') {
toast({
- title: '状态更新成功!',
- description: '订阅已激活,正在刷新页面...',
+ title: '支付成功!',
+ description: '您的订阅已激活',
status: 'success',
- duration: 3000,
- isClosable: true,
- });
-
- setTimeout(() => {
- onPaymentModalClose();
- window.location.reload();
- }, 2000);
- } else {
- toast({
- title: '无法更新状态',
- description: data.error || '支付状态未更新',
- status: 'warning',
duration: 5000,
isClosable: true,
});
+
+ onClose();
+ setTimeout(() => window.location.reload(), 2000);
+ } else {
+ toast({
+ title: '未检测到支付',
+ description: '请确认已完成支付后重试',
+ status: 'info',
+ duration: 3000,
+ isClosable: true,
+ });
}
- } else {
- throw new Error('网络错误');
}
- } catch (error) {
+ } catch (error: any) {
toast({
- title: '强制更新失败',
+ title: '查询失败',
description: error.message,
status: 'error',
duration: 3000,
@@ -443,22 +377,22 @@ export default function SubscriptionContentNew() {
}
};
- const getCurrentPrice = (plan) => {
+ const getCurrentPrice = (plan: any) => {
if (!plan || plan.name === 'free') return 0;
const option = plan.pricingOptions?.find(
- (opt) => opt.cycleKey === selectedCycle
+ (opt: any) => opt.cycleKey === selectedCycle
);
return option ? option.price : plan.pricingOptions[0]?.price || 0;
};
- const getCurrentPriceOption = (plan) => {
+ const getCurrentPriceOption = (plan: any) => {
if (!plan || plan.name === 'free') return null;
- return plan.pricingOptions?.find((opt) => opt.cycleKey === selectedCycle);
+ return plan.pricingOptions?.find((opt: any) => opt.cycleKey === selectedCycle);
};
- const getIconComponent = (iconName) => {
- const icons = {
+ const getIconComponent = (iconName: string) => {
+ const icons: any = {
star: FaStar,
gem: FaGem,
crown: FaCrown,
@@ -469,225 +403,171 @@ export default function SubscriptionContentNew() {
return (
-
- {/* 标题区域 */}
-
-
-
-
-
- PRICING
-
-
+ {/* 背景光晕 */}
+
+
+
+ {/* 标题区域 */}
+
+
+ 订阅方案
+
+
+
+
+
- 选择适合你的方案
+ 立即开启智能决策
-
-
- 解锁专业级投资分析工具,让数据为你的决策赋能
-
-
-
-
- {/* 当前订阅状态 */}
- {user && (
-
-
-
-
-
- 当前订阅:
-
-
- {user.subscription_type === 'free'
- ? '基础版'
- : user.subscription_type === 'pro'
- ? 'Pro 专业版'
- : 'Max 旗舰版'}
-
-
- {user.subscription_status === 'active' ? '已激活' : '未激活'}
-
-
-
- {user.subscription_end_date && (
-
- 到期时间: {new Date(user.subscription_end_date).toLocaleDateString('zh-CN')}
-
- )}
-
-
- )}
+
- {/* 计费周期选择 */}
-
-
-
- 选择计费周期 · 时长越长优惠越大
-
+ {/* 计费周期选择器 */}
+
+
+ 选择计费周期 · 时长越长优惠越大
+
-
- {subscriptionConfig.plans[1]?.pricingOptions?.map((option, index) => (
-
- {option.discountPercent > 0 && (
-
- 省{option.discountPercent}%
-
- )}
-
-
-
- ))}
-
+ 省{option.discountPercent}%
+
+ )}
- {(() => {
- const currentOption = subscriptionConfig.plans[1]?.pricingOptions?.find(
- (opt) => opt.cycleKey === selectedCycle
+
+
+ ))}
+
+
+ {(() => {
+ const currentOption = subscriptionConfig.plans[1]?.pricingOptions?.find(
+ (opt: any) => opt.cycleKey === selectedCycle
+ );
+ if (currentOption && currentOption.discountPercent > 0) {
+ return (
+
+
+
+ 当前选择可节省 {currentOption.discountPercent}% 的费用
+
+
);
- if (currentOption && currentOption.discountPercent > 0) {
- return (
-
-
-
- 当前选择可节省 {currentOption.discountPercent}% 的费用
-
-
- );
- }
- return null;
- })()}
-
-
+ }
+ return null;
+ })()}
+
- {/* 套餐卡片 */}
-
- {subscriptionConfig.plans.map((plan, index) => {
+ {subscriptionConfig.plans.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' &&
- (plan.name === 'free' || user?.billing_cycle === selectedCycle);
+ user?.subscription_status === 'active';
+
+ const isPremium = plan.name === 'max';
return (
{/* 推荐标签 */}
{plan.badge && (
-
- {plan.badge}
-
+ {plan.displayName}
)}
- {/* 套餐头部 */}
-
-
-
-
- {plan.displayName}
-
-
-
- {plan.description}
-
-
-
- {/* 价格展示 */}
- {plan.name === 'free' ? (
-
-
- 免费
+ {/* 价格卡片 */}
+
+
+
+ ¥{getCurrentPrice(plan)}
-
- ) : (
-
-
-
- ¥
-
-
- {getCurrentPrice(plan)}
-
-
- /{currentPriceOption?.label || '月'}
-
-
+
+ /{currentPriceOption?.label || '月'}
+
+
- {currentPriceOption?.originalPrice && (
-
-
- 原价 ¥{currentPriceOption.originalPrice}
-
-
- 立省 ¥{currentPriceOption.originalPrice - currentPriceOption.price}
-
-
- )}
-
- )}
-
-
+
+
{/* 功能列表 */}
-
- {plan.features.map((feature, idx) => (
-
-
+ {plan.features.map((feature: any, idx: number) => (
+
+
+ >
+
+
{feature.name}
{feature.limit && (
-
+
({feature.limit})
)}
@@ -867,54 +759,12 @@ export default function SubscriptionContentNew() {
))}
-
- {/* 订阅按钮 */}
-
);
})}
-
+
{/* FAQ 区域 */}
-
+
常见问题
- {subscriptionConfig.faqs.map((faq, index) => (
+ {subscriptionConfig.faqs.map((faq: any, index: number) => (
setOpenFaqIndex(openFaqIndex === index ? null : index)}
justify="space-between"
align="center"
+ cursor="pointer"
+ onClick={() => setOpenFaqIndex(openFaqIndex === index ? null : index)}
>
-
+
{faq.question}
@@ -979,8 +826,8 @@ export default function SubscriptionContentNew() {
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3 }}
>
-
- {faq.answer.split('\n').map((line, idx) => (
+
+ {faq.answer.split('\n').map((line: string, idx: number) => (
{line}
@@ -997,307 +844,57 @@ export default function SubscriptionContentNew() {
{/* 支付模态框 */}
- {
- stopAutoPaymentCheck();
- setPaymentOrder(null);
- setPaymentCountdown(0);
- setPromoCode('');
- setPromoCodeApplied(false);
- setPromoCodeError('');
- setPriceInfo(null);
- onPaymentModalClose();
- }}
- size="lg"
- closeOnOverlayClick={false}
- >
-
-
-
-
-
- 微信支付
-
-
-
+
+
+
+ 微信支付
+
{!paymentOrder ? (
- /* 订单确认 */
-
- {selectedPlan && (
-
-
- 订单确认
-
-
-
- 套餐:
-
- {selectedPlan.displayName}
-
-
-
- 计费周期:
-
- {getCurrentPriceOption(selectedPlan)?.label || '月付'}
-
-
-
-
-
- {priceInfo && priceInfo.is_upgrade && (
-
-
-
-
- {priceInfo.upgrade_type === 'plan_upgrade'
- ? '套餐升级'
- : priceInfo.upgrade_type === 'cycle_change'
- ? '周期变更'
- : '套餐和周期调整'}
-
-
-
-
- 剩余价值抵扣:
- -¥{priceInfo.remaining_value.toFixed(2)}
-
-
-
- )}
-
-
- 套餐价格:
-
- ¥{priceInfo ? priceInfo.new_plan_price.toFixed(2) : getCurrentPrice(selectedPlan).toFixed(2)}
-
-
-
- {priceInfo && priceInfo.discount_amount > 0 && (
-
- 优惠码折扣:
- -¥{priceInfo.discount_amount.toFixed(2)}
-
- )}
-
-
-
-
-
- 实付金额:
-
-
- ¥{priceInfo ? priceInfo.final_amount.toFixed(2) : getCurrentPrice(selectedPlan).toFixed(2)}
-
-
-
-
- )}
-
- {/* 优惠码输入 */}
- {selectedPlan && (
-
-
- {
- setPromoCode(e.target.value.toUpperCase());
- setPromoCodeError('');
- }}
- size="md"
- isDisabled={promoCodeApplied}
- bg="rgba(255, 255, 255, 0.05)"
- border="1px solid"
- borderColor={themeColors.border.default}
- color={themeColors.text.primary}
- _focus={{
- borderColor: themeColors.border.gold,
- }}
- />
-
-
- {promoCodeError && (
-
- {promoCodeError}
-
- )}
- {promoCodeApplied && priceInfo && (
-
-
-
- 优惠码已应用!节省 ¥{priceInfo.discount_amount.toFixed(2)}
-
-
-
- )}
-
- )}
-
+
+
+ 套餐: {selectedPlan?.displayName} · {selectedCycle === 'monthly' ? '月付' : selectedCycle === 'quarterly' ? '季付' : selectedCycle === 'semiannual' ? '半年付' : '年付'}
+
+
+ ¥{priceInfo?.final_amount || getCurrentPrice(selectedPlan)}
+
}
- onClick={handleCreateOrder}
- isLoading={loading}
- loadingText="创建订单中..."
- isDisabled={!selectedPlan}
fontWeight="bold"
- _hover={{
- transform: 'scale(1.02)',
- shadow: '0 0 30px rgba(212, 175, 55, 0.4)',
- }}
- transition="all 0.3s"
+ onClick={handleCreatePaymentOrder}
+ isLoading={loading}
>
- 创建微信支付订单
+ 创建支付订单
) : (
- /* 支付二维码 */
-
-
- 请使用微信扫码支付
+
+
+ 请使用微信扫描二维码完成支付
-
- {/* 倒计时 */}
+
+ 剩余时间: {formatTime(paymentCountdown)}
+
+
+
-
- {/* 二维码 */}
-
- {paymentOrder.qr_code_url ? (
-
- ) : (
-
-
-
- )}
-
-
- {/* 订单信息 */}
-
-
- 订单号: {paymentOrder.order_no}
-
-
- 支付金额:
-
- ¥{paymentOrder.amount}
-
-
-
-
- {/* 操作按钮 */}
-
- }
- onClick={handleCheckPaymentStatus}
- isLoading={checkingPayment}
- loadingText="检查中..."
- _hover={{
- bg: 'rgba(212, 175, 55, 0.1)',
- }}
- >
- 检查支付状态
-
-
-
-
-
- 支付完成但页面未更新?点击上方"强制更新"按钮
-
-
-
- {/* 支付说明 */}
-
- • 使用微信"扫一扫"功能扫描上方二维码
- • 支付完成后系统将自动检测并激活订阅
- • 系统每10秒自动检查一次支付状态
- • 如遇问题请联系客服支持
-
+ 已完成支付
+
)}