import { router } from 'expo-router'; import { Alert } from 'react-native'; import { authClient } from '../auth/client'; export interface UserBalance { remainingTokenBalance: number; totalTokenBalance: number; usedTokenBalance: number; } export interface BalanceResponse { success: boolean; data: UserBalance; message?: string; } export interface TokenUsageRequest { price: number; name: string; metadata?: Record; } export interface TokenUsageResponse { success: boolean; data?: { identifier: string; remainingBalance: number; }; message?: string; } export interface BalanceCheckResult { hasEnough: boolean; currentBalance: number; isLoading: boolean; message?: string; } /** * 获取用户余额 * 从 metered 类型的订阅中获取 creditBalance */ export async function getUserBalance(): Promise { try { const { data, error } = await authClient.subscription.list({}); if (error) { return { success: false, data: { remainingTokenBalance: 0, totalTokenBalance: 0, usedTokenBalance: 0, }, message: error.message || '获取余额失败', }; } // 找到 metered 类型的订阅 const meteredSubscriptions = data?.filter((sub: any) => sub.type === 'metered') || []; if (meteredSubscriptions.length === 0) { return { success: false, data: { remainingTokenBalance: 0, totalTokenBalance: 0, usedTokenBalance: 0, }, message: '未找到计费订阅', }; } const creditBalance = (meteredSubscriptions[0] as any)?.creditBalance || {}; return { success: true, data: { remainingTokenBalance: creditBalance.remainingTokenBalance || 0, totalTokenBalance: creditBalance.totalTokenBalance || 0, usedTokenBalance: creditBalance.usedTokenBalance || 0, }, }; } catch (error) { console.error('Failed to get user balance:', error); return { success: false, data: { remainingTokenBalance: 0, totalTokenBalance: 0, usedTokenBalance: 0, }, message: '网络错误,请稍后重试', }; } } /** * 检查用户余额是否足够 */ export async function checkTokenBalance(tokens: number): Promise { const balanceResponse = await getUserBalance(); if (!balanceResponse.success) { return { hasEnough: false, currentBalance: 0, isLoading: false, message: balanceResponse.message || '无法获取余额', }; } const currentBalance = balanceResponse.data.remainingTokenBalance; const hasEnough = currentBalance >= tokens; return { hasEnough, currentBalance, isLoading: false, message: hasEnough ? undefined : `余额不足,当前: ${currentBalance.toLocaleString()}, 需要: ${tokens.toLocaleString()}`, }; } /** * 扣除 Token 使用量 * 参考 usePricing 中的 recordTokenUsage 方法 */ export async function recordTokenUsage( request: TokenUsageRequest ): Promise { const { price, name, metadata } = request; // price 的单位就是 token const tokens = Math.ceil(price); try { // 1. 检查余额是否足够 const balanceCheck = await checkTokenBalance(tokens); if (balanceCheck.isLoading) { return { success: false, message: '正在检查余额...', }; } if (!balanceCheck.hasEnough) { // 余额不足,提示用户并引导充值 Alert.alert( '余额不足', `当前余额: ${balanceCheck.currentBalance}\n需要费用: ${tokens}\n请先充值`, [ { text: '取消', style: 'cancel' }, { text: '去充值', onPress: () => { router.push('/exchange'); }, }, ] ); return { success: false, message: balanceCheck.message || '余额不足', }; } // 2. 余额充足,执行消费操作 const { data, error } = await authClient.subscription.meterEvent({ event_name: 'token_usage', payload: { value: tokens.toString(), ...(metadata && { metadata }), } as any, }); if (error) { Alert.alert('扣费失败', error.message || '请稍后重试'); return { success: false, message: error.message || '扣费失败', }; } // 3. 扣费成功,返回结果 const balanceAfter = await getUserBalance(); return { success: true, data: { identifier: data?.identifier || '', remainingBalance: balanceAfter.data.remainingTokenBalance, }, message: '扣费成功', }; } catch (error) { console.error('Failed to record token usage:', error); const errorMessage = error instanceof Error ? error.message : '扣费失败,请稍后重试'; Alert.alert('错误', errorMessage); return { success: false, message: errorMessage, }; } } /** * 跳转到充值页面 */ export function redirectToPricePage() { router.push('/exchange'); }