/** * Payment Service * API client for subscriptions, payments, billing, and wallet */ import axios from 'axios'; import type { PricingPlan, Subscription, SubscriptionWithPlan, Payment, PaymentMethod, Invoice, Wallet, WalletTransaction, CreateSubscriptionInput, CheckoutSession, SubscriptionPreview, BillingInfo, UsageStats, ApiResponse, SubscriptionResponse, WalletResponse, PlanInterval, } from '../types/payment.types'; const API_BASE_URL = import.meta.env?.VITE_API_URL || '/api/v1'; const api = axios.create({ baseURL: API_BASE_URL, headers: { 'Content-Type': 'application/json', }, }); // Add auth token to requests api.interceptors.request.use((config) => { const token = localStorage.getItem('auth_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // ============================================================================ // Pricing Plans // ============================================================================ export async function getPlans(): Promise { const response = await api.get>('/payments/plans'); return response.data.data; } export async function getPlanBySlug(slug: string): Promise { const response = await api.get>(`/payments/plans/${slug}`); return response.data.data; } // Alias for backwards compatibility - backend uses slug for lookup export async function getPlanById(planId: string): Promise { return getPlanBySlug(planId); } // ============================================================================ // Subscriptions // ============================================================================ export async function getCurrentSubscription(): Promise { try { const response = await api.get>( '/payments/subscription' ); return response.data.data; } catch { return null; } } export async function getSubscriptionHistory(): Promise { const response = await api.get>('/payments/subscription/history'); return response.data.data; } export async function createSubscription( input: CreateSubscriptionInput ): Promise { const response = await api.post>( '/payments/subscriptions', input ); return response.data.data; } export async function cancelSubscription( _subscriptionId: string, immediately = false ): Promise { const response = await api.post>( '/payments/subscription/cancel', { immediately } ); return response.data.data; } export async function reactivateSubscription(_subscriptionId: string): Promise { const response = await api.post>( '/payments/subscription/resume' ); return response.data.data; } export async function changeSubscriptionPlan( _subscriptionId: string, newPlanId: string, newInterval: PlanInterval ): Promise { const response = await api.post>( '/payments/subscription/change-plan', { planId: newPlanId, billingCycle: newInterval === 'year' ? 'yearly' : 'monthly' } ); return response.data.data; } export async function previewSubscriptionChange( planId: string, interval: PlanInterval, couponCode?: string ): Promise { // Note: Backend doesn't have a preview endpoint, so we return a mock for now // This would need to be implemented in the backend return { subtotal: 0, discount: 0, tax: 0, total: 0, currency: 'USD', interval, }; } // ============================================================================ // Checkout // ============================================================================ export async function createCheckoutSession( planId: string, interval: PlanInterval, successUrl?: string, cancelUrl?: string ): Promise { const response = await api.post>('/payments/checkout', { planId, billingCycle: interval === 'year' ? 'yearly' : 'monthly', successUrl: successUrl || `${window.location.origin}/settings/billing?success=true`, cancelUrl: cancelUrl || `${window.location.origin}/pricing?canceled=true`, }); return response.data.data; } export async function createPortalSession(returnUrl?: string): Promise<{ url: string }> { const response = await api.post>('/payments/billing-portal', { returnUrl: returnUrl || `${window.location.origin}/settings/billing`, }); return response.data.data; } // ============================================================================ // Payment Methods // ============================================================================ export async function getPaymentMethods(): Promise { const response = await api.get>('/payments/methods'); return response.data.data; } export async function addPaymentMethod(paymentMethodId: string): Promise { const response = await api.post>('/payments/methods', { paymentMethodId, }); return response.data.data; } export async function setDefaultPaymentMethod(paymentMethodId: string): Promise { const response = await api.post>( '/payments/methods/default', { paymentMethodId } ); return response.data.data; } export async function removePaymentMethod(paymentMethodId: string): Promise { await api.delete(`/payments/methods/${paymentMethodId}`); } // ============================================================================ // Payments & Invoices // ============================================================================ export async function getPaymentHistory( limit = 20, offset = 0 ): Promise<{ payments: Payment[]; total: number }> { const response = await api.get>( '/payments/history', { params: { limit, offset } } ); return response.data.data; } export async function getInvoices( limit = 20, offset = 0 ): Promise<{ invoices: Invoice[]; total: number }> { const response = await api.get>( '/payments/invoices', { params: { limit, offset } } ); return response.data.data; } export async function getInvoiceById(invoiceId: string): Promise { const response = await api.get>(`/payments/invoices/${invoiceId}`); return response.data.data; } export async function downloadInvoice(invoiceId: string): Promise { const response = await api.get(`/payments/invoices/${invoiceId}/pdf`, { responseType: 'blob', }); return response.data; } // ============================================================================ // Billing Info // ============================================================================ export async function getBillingInfo(): Promise { try { const response = await api.get>('/payments/billing-info'); return response.data.data; } catch { return null; } } export async function updateBillingInfo(info: BillingInfo): Promise { const response = await api.put>('/payments/billing-info', info); return response.data.data; } // ============================================================================ // Usage Stats // ============================================================================ export async function getUsageStats(): Promise { const response = await api.get>('/payments/usage'); return response.data.data; } // ============================================================================ // Wallet // ============================================================================ export async function getWallet(): Promise { const response = await api.get>('/payments/wallet'); return response.data.data; } export async function getWalletTransactions( limit = 20, offset = 0, type?: string ): Promise<{ transactions: WalletTransaction[]; total: number }> { const response = await api.get< ApiResponse<{ transactions: WalletTransaction[]; total: number }> >('/payments/wallet/transactions', { params: { limit, offset, type } }); return response.data.data; } export async function depositToWallet( amount: number, paymentMethodId: string ): Promise<{ transaction: WalletTransaction; clientSecret?: string }> { const response = await api.post< ApiResponse<{ transaction: WalletTransaction; clientSecret?: string }> >('/payments/wallet/deposit', { amount, paymentMethodId }); return response.data.data; } export async function withdrawFromWallet( amount: number, destination: { type: 'bank_account'; accountId: string } ): Promise { const response = await api.post>('/payments/wallet/withdraw', { amount, destination, }); return response.data.data; } // ============================================================================ // Coupons // ============================================================================ export async function validateCoupon( code: string, planId: string ): Promise<{ valid: boolean; discount: number; discountType: 'percent' | 'amount'; message?: string; }> { const response = await api.post< ApiResponse<{ valid: boolean; discount: number; discountType: 'percent' | 'amount'; message?: string; }> >('/payments/coupons/validate', { code, planId }); return response.data.data; } // ============================================================================ // Summary & Dashboard // ============================================================================ export async function getBillingSummary(): Promise<{ subscription: SubscriptionWithPlan | null; nextBillingDate: string | null; nextBillingAmount: number | null; usage: UsageStats; recentPayments: Payment[]; wallet: Wallet | null; }> { const response = await api.get< ApiResponse<{ subscription: SubscriptionWithPlan | null; nextBillingDate: string | null; nextBillingAmount: number | null; usage: UsageStats; recentPayments: Payment[]; wallet: Wallet | null; }> >('/payments/summary'); return response.data.data; } // Export service object for convenience export const paymentService = { // Plans getPlans, getPlanById, getPlanBySlug, // Subscriptions getCurrentSubscription, getSubscriptionHistory, createSubscription, cancelSubscription, reactivateSubscription, changeSubscriptionPlan, previewSubscriptionChange, // Checkout createCheckoutSession, createPortalSession, // Payment Methods getPaymentMethods, addPaymentMethod, setDefaultPaymentMethod, removePaymentMethod, // Payments & Invoices getPaymentHistory, getInvoices, getInvoiceById, downloadInvoice, // Billing Info getBillingInfo, updateBillingInfo, // Usage getUsageStats, // Wallet getWallet, getWalletTransactions, depositToWallet, withdrawFromWallet, // Coupons validateCoupon, // Summary getBillingSummary, }; export default paymentService;