forked from 77media/video-flow
230 lines
5.8 KiB
TypeScript
230 lines
5.8 KiB
TypeScript
/**
|
||
* Stripe 支付相关工具函数
|
||
*/
|
||
import { post, get } from '@/api/request';
|
||
import { ApiResponse } from '@/api/common';
|
||
|
||
export interface SubscriptionPlan {
|
||
id: number;
|
||
name: string;
|
||
display_name: string;
|
||
description: string;
|
||
price_month: number;
|
||
price_year: number;
|
||
features: string[];
|
||
is_free: boolean;
|
||
is_popular: boolean;
|
||
is_subscribed: boolean;
|
||
sort_order: number;
|
||
}
|
||
|
||
export interface PaymentStatusData {
|
||
payment_status: 'pending' | 'success' | 'fail';
|
||
biz_order_no: string;
|
||
pay_time?: string;
|
||
subscription?: {
|
||
plan_name: string;
|
||
plan_display_name: string;
|
||
status: string;
|
||
current_period_end?: string;
|
||
};
|
||
}
|
||
|
||
export type PaymentStatusResponse = ApiResponse<PaymentStatusData>;
|
||
|
||
export interface CreateCheckoutSessionRequest {
|
||
user_id: string;
|
||
plan_name: string;
|
||
billing_cycle: 'month' | 'year';
|
||
}
|
||
|
||
export interface CreateCheckoutSessionData {
|
||
checkout_url: string;
|
||
session_id: string;
|
||
biz_order_no: string;
|
||
amount: number;
|
||
currency: string;
|
||
}
|
||
|
||
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>;
|
||
|
||
export interface BuyTokensRequest {
|
||
token_amount: number;
|
||
package_type?: string;
|
||
}
|
||
|
||
export interface BuyTokensData {
|
||
checkout_url: string;
|
||
session_id: string;
|
||
biz_order_no: string;
|
||
amount: number;
|
||
currency: string;
|
||
token_amount: number;
|
||
}
|
||
|
||
export type BuyTokensResponse = ApiResponse<BuyTokensData>;
|
||
|
||
/**
|
||
* 获取订阅计划列表
|
||
* 从后端API获取所有活跃的订阅计划,后端已经过滤了活跃计划
|
||
*/
|
||
export async function fetchSubscriptionPlans(): Promise<SubscriptionPlan[]> {
|
||
try {
|
||
const response = await get<ApiResponse<SubscriptionPlan[]>>('/api/subscription/plans');
|
||
|
||
if (!response.successful || !response.data) {
|
||
throw new Error(response.message || '获取订阅计划失败');
|
||
}
|
||
|
||
// 后端已经过滤了活跃计划,直接按排序顺序排列
|
||
const sortedPlans = response.data.sort((a, b) => a.sort_order - b.sort_order);
|
||
|
||
return sortedPlans;
|
||
} catch (error) {
|
||
console.error('获取订阅计划失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建Checkout Session(推荐方案)
|
||
*
|
||
* 这是更简单的支付方案:
|
||
* 1. 调用此函数获取checkout_url
|
||
* 2. 直接跳转到checkout_url
|
||
* 3. 用户在Stripe页面完成支付
|
||
* 4. 支付成功后自动跳转回success_url
|
||
*/
|
||
export async function createCheckoutSession(
|
||
request: CreateCheckoutSessionRequest
|
||
): Promise<CreateCheckoutSessionResponse> {
|
||
try {
|
||
return await post<CreateCheckoutSessionResponse>('/api/payment/checkoutDeepControl', request);
|
||
} catch (error) {
|
||
console.error('创建Checkout Session失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询Checkout Session状态
|
||
*/
|
||
export async function getCheckoutSessionStatus(
|
||
sessionId: string,
|
||
userId: string
|
||
): Promise<PaymentStatusResponse> {
|
||
try {
|
||
return await get<PaymentStatusResponse>(`/api/payment/checkout-status/${sessionId}?user_id=${userId}`);
|
||
} catch (error) {
|
||
console.error('查询Checkout Session状态失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建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;
|
||
}
|
||
}
|
||
/**
|
||
* 获取用户订阅信息
|
||
* @param {string} userId - 用户ID
|
||
* @returns {Promise<UserSubscriptionInfoResponse>} - 用户订阅信息
|
||
* @throws {Error} - 请求失败时抛出异常
|
||
*/
|
||
export async function getUserSubscriptionInfo(
|
||
userId: string
|
||
): Promise<UserSubscriptionInfoResponse> {
|
||
if (!userId) {
|
||
throw new Error('userId不能为空');
|
||
}
|
||
try {
|
||
return await get<UserSubscriptionInfoResponse>(`/api/subscription/user/info?user_id=${userId}`);
|
||
} catch (error) {
|
||
console.error('获取用户订阅信息失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/** 用户订阅信息响应体 */
|
||
export interface UserSubscriptionInfoResponse {
|
||
/** 状态码 */
|
||
code: number;
|
||
/** 消息 */
|
||
message: string;
|
||
/** 数据 */
|
||
data: {
|
||
/** 剩余点数 */
|
||
credits: number;
|
||
/** 订阅状态 */
|
||
subscription_status: string;
|
||
/** 订阅计划名 */
|
||
plan_name: string;
|
||
};
|
||
/** 是否成功 */
|
||
successful: boolean;
|
||
}
|
||
|
||
/**
|
||
* 简单的跳转到Checkout页面的工具函数
|
||
*/
|
||
export function redirectToCheckout(checkoutUrl: string) {
|
||
if (typeof window !== 'undefined') {
|
||
window.open(checkoutUrl, '_blank');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 购买Token
|
||
*
|
||
* 创建Token购买的Stripe Checkout Session
|
||
* 定价规则:1美元 = 100 Token
|
||
*/
|
||
export async function buyTokens(
|
||
request: BuyTokensRequest
|
||
): Promise<BuyTokensResponse> {
|
||
try {
|
||
return await post<BuyTokensResponse>('/api/payment/buy-tokens', request);
|
||
} catch (error) {
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 简单的跳转到Customer Portal页面的工具函数
|
||
*/
|
||
export function redirectToPortal(portalUrl: string) {
|
||
if (typeof window !== 'undefined') {
|
||
window.open(portalUrl, '_blank');
|
||
}
|
||
}
|