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) => {
|
const fetchPaymentDetails = async (sessionId: string) => {
|
||||||
try {
|
try {
|
||||||
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
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();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.successful && result.data) {
|
if (result.successful && result.data) {
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export default function PaymentSuccessPage() {
|
|||||||
// 使用新的Checkout Session状态查询
|
// 使用新的Checkout Session状态查询
|
||||||
const { getCheckoutSessionStatus } = await import('@/lib/stripe');
|
const { getCheckoutSessionStatus } = await import('@/lib/stripe');
|
||||||
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
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) {
|
if (result.successful && result.data) {
|
||||||
setPaymentData(result.data);
|
setPaymentData(result.data);
|
||||||
|
|||||||
@ -12,11 +12,12 @@ import {
|
|||||||
LogOut,
|
LogOut,
|
||||||
PanelsLeftBottom,
|
PanelsLeftBottom,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import React, { useRef, useEffect, useLayoutEffect, useState } from 'react';
|
import React, { useRef, useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { logoutUser } from '@/lib/auth';
|
import { logoutUser } from '@/lib/auth';
|
||||||
|
import { createPortalSession, redirectToPortal } from '@/lib/stripe';
|
||||||
|
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
@ -34,6 +35,7 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
|||||||
const currentUser: User = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
const currentUser: User = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||||||
const [mounted, setMounted] = React.useState(false);
|
const [mounted, setMounted] = React.useState(false);
|
||||||
const [isLogin, setIsLogin] = useState(false);
|
const [isLogin, setIsLogin] = useState(false);
|
||||||
|
const [isManagingSubscription, setIsManagingSubscription] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentUser = localStorage.getItem("currentUser");
|
const currentUser = localStorage.getItem("currentUser");
|
||||||
if (JSON.parse(currentUser || "{}")?.token) {
|
if (JSON.parse(currentUser || "{}")?.token) {
|
||||||
@ -48,6 +50,34 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
|||||||
return () => console.log('Cleanup mounted effect');
|
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(() => {
|
useEffect(() => {
|
||||||
@ -187,7 +217,10 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
|||||||
<User className="h-4 w-4" />
|
<User className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{mounted && isOpen ? ReactDOM.createPortal(
|
{mounted && (
|
||||||
|
ReactDOM.createPortal(
|
||||||
|
(<AnimatePresence>
|
||||||
|
{isOpen && (
|
||||||
<motion.div
|
<motion.div
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
initial={{ opacity: 0, scale: 0.95, y: -20 }}
|
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" />
|
<Sparkles className="h-4 w-4" />
|
||||||
<span className="text-white underline text-sm">100 credits</span>
|
<span className="text-white underline text-sm">100 credits</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -241,6 +275,16 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
|||||||
>
|
>
|
||||||
Upgrade
|
Upgrade
|
||||||
</Button>
|
</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>
|
</div>
|
||||||
|
|
||||||
{/* Menu Items */}
|
{/* Menu Items */}
|
||||||
@ -275,8 +319,11 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
, document.body)
|
)}
|
||||||
: null}
|
</AnimatePresence>) as React.ReactElement,
|
||||||
|
document.body
|
||||||
|
) as React.ReactNode
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -47,6 +47,19 @@ export interface CreateCheckoutSessionData {
|
|||||||
|
|
||||||
export type CreateCheckoutSessionResponse = ApiResponse<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获取所有活跃的订阅计划,后端已经过滤了活跃计划
|
* 从后端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页面的工具函数
|
* 简单的跳转到Checkout页面的工具函数
|
||||||
*/
|
*/
|
||||||
@ -112,3 +147,12 @@ export function redirectToCheckout(checkoutUrl: string) {
|
|||||||
window.location.href = checkoutUrl;
|
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