forked from 77media/video-flow
update for pay manage
This commit is contained in:
parent
0032ff1aa9
commit
865defe2ec
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,7 +217,10 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
||||
<User className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
{mounted && isOpen ? ReactDOM.createPortal(
|
||||
{mounted && (
|
||||
ReactDOM.createPortal(
|
||||
(<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
ref={menuRef}
|
||||
initial={{ opacity: 0, scale: 0.95, y: -20 }}
|
||||
@ -233,6 +266,7 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
||||
<Sparkles className="h-4 w-4" />
|
||||
<span className="text-white underline text-sm">100 credits</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@ -241,6 +275,16 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
||||
>
|
||||
Upgrade
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-white hover:bg-white/10 rounded-full px-4 text-xs"
|
||||
onClick={handleManageSubscription}
|
||||
disabled={isManagingSubscription}
|
||||
>
|
||||
{isManagingSubscription ? 'Loading...' : 'Manage Subscription'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Menu Items */}
|
||||
@ -275,8 +319,11 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
, document.body)
|
||||
: null}
|
||||
)}
|
||||
</AnimatePresence>) as React.ReactElement,
|
||||
document.body
|
||||
) as React.ReactNode
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -47,6 +47,19 @@ export interface CreateCheckoutSessionData {
|
||||
|
||||
export type CreateCheckoutSessionResponse = ApiResponse<CreateCheckoutSessionData>;
|
||||
|
||||
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<CreatePortalSessionData>;
|
||||
|
||||
/**
|
||||
* 获取订阅计划列表
|
||||
* 从后端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<CreatePortalSessionResponse> {
|
||||
try {
|
||||
return await post<CreatePortalSessionResponse>('/api/payment/portal-session', request);
|
||||
} catch (error) {
|
||||
console.error('创建Customer Portal Session失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的跳转到Checkout页面的工具函数
|
||||
*/
|
||||
@ -112,3 +147,12 @@ export function redirectToCheckout(checkoutUrl: string) {
|
||||
window.location.href = checkoutUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的跳转到Customer Portal页面的工具函数
|
||||
*/
|
||||
export function redirectToPortal(portalUrl: string) {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = portalUrl;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user