From 865defe2ec78e2b46c3386b87a2e5f7adedd2c63 Mon Sep 17 00:00:00 2001
From: Zixin Zhou
Date: Thu, 28 Aug 2025 17:04:00 +0800
Subject: [PATCH] update for pay manage
---
app/dashboard/page.tsx | 2 +-
app/payment-success/page.tsx | 2 +-
components/layout/top-bar.tsx | 85 +++++++++++++++++++++++++--------
components/pages/home-page2.tsx | 14 +++---
lib/stripe.ts | 44 +++++++++++++++++
5 files changed, 119 insertions(+), 28 deletions(-)
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