video-flow-b/lib/stripe.ts
2025-09-09 21:14:06 +08:00

230 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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');
}
}