forked from 77media/video-flow
212 lines
7.5 KiB
TypeScript
212 lines
7.5 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect, useMemo } from 'react';
|
||
import { useRouter } from 'next/navigation';
|
||
import { Check, ArrowLeft } from 'lucide-react';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||
import { fetchSubscriptionPlans, SubscriptionPlan } from '@/lib/stripe';
|
||
|
||
export default function PricingPage() {
|
||
|
||
return (
|
||
<div className="w-full h-full overflow-y-auto bg-black text-white pb-[10rem]">
|
||
{/* Main Content */}
|
||
<HomeModule5 />
|
||
</div>
|
||
);
|
||
}
|
||
/**价格方案 */
|
||
function HomeModule5() {
|
||
const [billingType, setBillingType] = useState<'month' | 'year'>(
|
||
"month"
|
||
);
|
||
|
||
const [plans, setPlans] = useState<SubscriptionPlan[]>([]);
|
||
|
||
// 从后端获取订阅计划数据
|
||
useEffect(() => {
|
||
const loadPlans = async () => {
|
||
try {
|
||
const plansData = await fetchSubscriptionPlans();
|
||
setPlans(plansData);
|
||
} catch (err) {
|
||
console.error('加载订阅计划失败:', err);
|
||
}
|
||
};
|
||
|
||
loadPlans();
|
||
}, []);
|
||
|
||
const pricingPlans = useMemo<{
|
||
title: string;
|
||
price: number;
|
||
credits: string;
|
||
buttonText: string;
|
||
features: string[];
|
||
}[]>(() => {
|
||
return plans.map((plan) => {
|
||
return {
|
||
title: plan.display_name || plan.name,
|
||
price: billingType === "month" ? plan.price_month/100 : plan.price_year/100,
|
||
credits: plan.description,
|
||
buttonText: plan.is_free ? "Try For Free" : "Subscribe Now",
|
||
features: plan.features || [],
|
||
};
|
||
});
|
||
}, [plans, billingType]);
|
||
|
||
const handleSubscribe = async (planName: string) => {
|
||
if (planName === 'hobby') {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 使用新的Checkout Session方案(更简单!)
|
||
const { createCheckoutSession, redirectToCheckout } = await import('@/lib/stripe');
|
||
|
||
// 从localStorage获取当前用户信息
|
||
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
||
|
||
if (!User.id) {
|
||
throw new Error("无法获取用户ID,请重新登录");
|
||
}
|
||
|
||
// 1. 创建Checkout Session
|
||
const result = await createCheckoutSession({
|
||
user_id: String(User.id),
|
||
plan_name: planName,
|
||
billing_cycle: billingType
|
||
});
|
||
|
||
if (!result.successful || !result.data) {
|
||
throw new Error("create checkout session failed");
|
||
}
|
||
|
||
// 2. 直接跳转到Stripe托管页面(就这么简单!)
|
||
redirectToCheckout(result.data.checkout_url);
|
||
|
||
} catch (error) {
|
||
throw new Error("create checkout session failed, please try again later");
|
||
}
|
||
};
|
||
return (
|
||
<div
|
||
data-alt="core-value-section"
|
||
className="home-module5 h-[1600px] relative flex flex-col items-center justify-center w-full bg-black snap-start"
|
||
>
|
||
<div
|
||
data-alt="core-value-content"
|
||
className="center z-10 flex flex-col items-center mb-[8rem]"
|
||
>
|
||
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal mb-[1.5rem]">
|
||
Start Creating
|
||
</h2>
|
||
|
||
{/* 计费切换 */}
|
||
<div className="h-[3.375rem] flex bg-black rounded-full p-[0.0625rem] mt-[1.5rem] border border-white/20">
|
||
<button
|
||
onClick={() => setBillingType("month")}
|
||
className={`box-border flex justify-center items-center w-[6rem] text-base rounded-full transition-all duration-300 ${
|
||
billingType === "month"
|
||
? "bg-white text-black"
|
||
: "text-white hover:text-gray-300"
|
||
}`}
|
||
>
|
||
Monthly
|
||
</button>
|
||
<button
|
||
onClick={() => setBillingType("year")}
|
||
className={`box-border flex justify-center items-center w-[7.125rem] text-base rounded-full transition-all duration-300 ${
|
||
billingType === "year"
|
||
? "bg-white text-black"
|
||
: "text-white hover:text-gray-300"
|
||
}`}
|
||
>
|
||
Yearly - <span className="text-[#FFCC6D]">10%</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 主要价格卡片 */}
|
||
<div className="flex justify-between w-[90%] px-[12rem] mb-[2rem]">
|
||
{pricingPlans.map((plan, index) => (
|
||
<div
|
||
key={index}
|
||
className=" w-[26rem] h-[38.125rem] bg-black rounded-lg p-[1.5rem] border border-white/20"
|
||
>
|
||
<h3 className="text-white text-2xl font-normal mb-[1rem]">
|
||
{plan.title}
|
||
</h3>
|
||
<div className="mb-[1rem]">
|
||
<span className="text-white text-[3.375rem] font-bold">
|
||
${plan.price}
|
||
</span>
|
||
<span className="text-white text-xs ml-[0.5rem]">/month</span>
|
||
</div>
|
||
<p className="text-white text-[0.875rem] mb-[1rem]">
|
||
{plan.credits}
|
||
</p>
|
||
<button onClick={() => handleSubscribe(plan.title)} className="w-full bg-white text-black py-[0.75rem] rounded-full mb-[1rem] hover:bg-black hover:text-white transition-colors border border-white/20">
|
||
{plan.buttonText}
|
||
</button>
|
||
<p className="w-full text-center text-white/60 text-[0.75rem] mb-[2rem]">
|
||
* Billed monthly until cancelled
|
||
</p>
|
||
<ul className="space-y-[1rem]">
|
||
{plan.features.map((feature, featureIndex) => (
|
||
<li
|
||
key={featureIndex}
|
||
className="flex items-center text-white text-[0.875rem]"
|
||
>
|
||
<span className="text-[#C73BFF] mr-[0.5rem]">✓</span>
|
||
{feature}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 额外价格卡片 */}
|
||
<div className="flex gap-[1.5rem] w-[90%] px-[12rem]">
|
||
<div className="flex-1 bg-black rounded-lg p-[1.5rem] border border-white/20">
|
||
<h3 className="text-white text-[1.5rem] font-bold mb-[0.5rem]">
|
||
Free
|
||
</h3>
|
||
<div className="mb-[1rem]">
|
||
<span className="text-white text-[2.5rem] font-bold">$0</span>
|
||
</div>
|
||
<p className="text-white text-[0.875rem] mb-[1.5rem] leading-relaxed">
|
||
10 Video mins and 1 AI credit per week, 1 Express avatar, 4 Exports
|
||
per week with invideo watermark.
|
||
</p>
|
||
<p className="text-white text-[0.875rem] mb-[1.5rem] leading-relaxed">
|
||
No access to generative features.
|
||
</p>
|
||
<button className="w-[9rem] bg-[#262626] text-white py-[0.75rem] rounded-full hover:bg-white hover:text-black transition-colors border border-white/20">
|
||
Try For Free
|
||
</button>
|
||
</div>
|
||
|
||
<div className="flex-1 bg-black rounded-lg p-[1.5rem] border border-white/20">
|
||
<h3 className="text-white text-[1.5rem] font-bold mb-[0.5rem]">
|
||
Enterprise
|
||
</h3>
|
||
<p className="text-white text-[2.5rem] mb-[1rem]">Custom</p>
|
||
<p className="text-white text-[0.875rem] mb-[1.5rem] leading-relaxed">
|
||
Custom solutions for large organizations. Advanced security and
|
||
flexible pricing based on your needs.
|
||
</p>
|
||
<p className="text-white text-[0.875rem] mb-[1.5rem] leading-relaxed">
|
||
on your needs.
|
||
</p>
|
||
<button className="w-[9rem] bg-[#262626] text-white py-[0.75rem] rounded-full hover:bg-white hover:text-black transition-colors border border-white/20">
|
||
Contact Us
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|