diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 036c12f..b78286d 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -59,7 +59,7 @@ export default function DashboardPage() { const fetchPaymentDetails = async (sessionId: string) => { try { const User = JSON.parse(localStorage.getItem("currentUser") || "{}"); - const response = await fetch(`/api/payment/checkout-status/${sessionId}?user_id=${User.id}`); + const response = await fetch(`/api/payment/checkout-status/${sessionId}?user_id=${String(User.id)}`); const result = await response.json(); if (result.successful && result.data) { diff --git a/app/payment-success/page.tsx b/app/payment-success/page.tsx index 589d424..710ee99 100644 --- a/app/payment-success/page.tsx +++ b/app/payment-success/page.tsx @@ -43,7 +43,7 @@ export default function PaymentSuccessPage() { // 使用新的Checkout Session状态查询 const { getCheckoutSessionStatus } = await import('@/lib/stripe'); const User = JSON.parse(localStorage.getItem("currentUser") || "{}"); - const result = await getCheckoutSessionStatus(sessionId, User.id); + const result = await getCheckoutSessionStatus(sessionId, String(User.id)); if (result.successful && result.data) { setPaymentData(result.data); diff --git a/components/layout/top-bar.tsx b/components/layout/top-bar.tsx index 023990c..3bcb754 100644 --- a/components/layout/top-bar.tsx +++ b/components/layout/top-bar.tsx @@ -12,11 +12,12 @@ import { LogOut, PanelsLeftBottom, } from 'lucide-react'; -import { motion } from 'framer-motion'; +import { motion, AnimatePresence } from 'framer-motion'; import ReactDOM from 'react-dom'; import { useRouter } from 'next/navigation'; import React, { useRef, useEffect, useLayoutEffect, useState } from 'react'; import { logoutUser } from '@/lib/auth'; +import { createPortalSession, redirectToPortal } from '@/lib/stripe'; interface User { @@ -34,6 +35,7 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT const currentUser: User = JSON.parse(localStorage.getItem('currentUser') || '{}'); const [mounted, setMounted] = React.useState(false); const [isLogin, setIsLogin] = useState(false); + const [isManagingSubscription, setIsManagingSubscription] = useState(false); useEffect(() => { const currentUser = localStorage.getItem("currentUser"); if (JSON.parse(currentUser || "{}")?.token) { @@ -48,6 +50,34 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT return () => console.log('Cleanup mounted effect'); }, []); + // 处理订阅管理 + const handleManageSubscription = async () => { + if (!currentUser?.id) { + console.error('用户未登录'); + return; + } + + setIsManagingSubscription(true); + try { + const response = await createPortalSession({ + user_id: String(currentUser.id), + return_url: window.location.origin + '/dashboard' + }); + + if (response.successful && response.data?.portal_url) { + redirectToPortal(response.data.portal_url); + } else { + console.error('创建订阅管理会话失败:', response.message); + alert('无法打开订阅管理页面,请稍后重试'); + } + } catch (error) { + console.error('打开订阅管理页面失败:', error); + alert('无法打开订阅管理页面,请稍后重试'); + } finally { + setIsManagingSubscription(false); + } + }; + // 处理点击事件 useEffect(() => { @@ -187,13 +217,16 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT - {mounted && isOpen ? ReactDOM.createPortal( - + {isOpen && ( + 100 credits - +
+ + +
{/* Menu Items */} @@ -274,9 +318,12 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
250819215404 | 2025/8/20 06:00:50
-
- , document.body) - : null} +
+ )} + ) as React.ReactElement, + document.body + ) as React.ReactNode + )} diff --git a/components/pages/home-page2.tsx b/components/pages/home-page2.tsx index b194e0a..aec3df7 100644 --- a/components/pages/home-page2.tsx +++ b/components/pages/home-page2.tsx @@ -161,8 +161,8 @@ function HomeModule3() {

MovieFlow can make any kind of film in high quality for you -

- +

+ {/* 3x3网格布局 */}
-
+ ))} ))} - - + + ); } /**电影制作工序介绍 */ @@ -287,7 +287,7 @@ function HomeModule4() {

Edit like you think

- +
{/* 左侧四个切换tab */} @@ -445,7 +445,7 @@ function HomeModule5() { Yearly - 15%
- + {/* 主要价格卡片 */}
diff --git a/lib/stripe.ts b/lib/stripe.ts index 49e883f..d219228 100644 --- a/lib/stripe.ts +++ b/lib/stripe.ts @@ -47,6 +47,19 @@ export interface CreateCheckoutSessionData { export type CreateCheckoutSessionResponse = ApiResponse; +export interface CreatePortalSessionRequest { + user_id: string; + return_url?: string; +} + +export interface CreatePortalSessionData { + portal_url: string; + session_id: string; + customer_id: string; +} + +export type CreatePortalSessionResponse = ApiResponse; + /** * 获取订阅计划列表 * 从后端API获取所有活跃的订阅计划,后端已经过滤了活跃计划 @@ -104,6 +117,28 @@ export async function getCheckoutSessionStatus( } } +/** + * 创建Customer Portal Session + * + * 用户可以在Customer Portal中: + * 1. 查看当前订阅状态 + * 2. 更新付款方式 + * 3. 下载发票和收据 + * 4. 更改订阅计划 + * 5. 取消订阅 + * 6. 查看账单历史 + */ +export async function createPortalSession( + request: CreatePortalSessionRequest +): Promise { + try { + return await post('/api/payment/portal-session', request); + } catch (error) { + console.error('创建Customer Portal Session失败:', error); + throw error; + } +} + /** * 简单的跳转到Checkout页面的工具函数 */ @@ -111,4 +146,13 @@ export function redirectToCheckout(checkoutUrl: string) { if (typeof window !== 'undefined') { window.location.href = checkoutUrl; } +} + +/** + * 简单的跳转到Customer Portal页面的工具函数 + */ +export function redirectToPortal(portalUrl: string) { + if (typeof window !== 'undefined') { + window.location.href = portalUrl; + } } \ No newline at end of file