/** * Unified Payments API Client * * Stream 162: Centralized API client for @apps/payments microservice * * This module provides: * - Configured API client using @lilith/api-client factory * - Type-safe API functions for all payment operations * - Automatic authentication and error handling * - 401 redirect to login * * Replaces duplicated axios instances in: * - @apps/channel-studio/src/shared/api/payments-client.ts (raw axios) * - @apps/fan-club/src/utils/payments-client.ts (good pattern) * * @example * ```typescript * import { paymentsClient, subscriptionsApi } from '@lilith/payment/api'; * * // Direct client usage * const response = await paymentsClient.get('/subscriptions/123'); * * // Type-safe API functions * const subscription = await subscriptionsApi.get('123'); * const subscriptions = await subscriptionsApi.listByUser('user-456'); * ``` */ import { createApiClient } from '@lilith/api-client' /** * Configured Payments API client for all payment operations * * Features: * - Points to @apps/payments microservice (port 4002) * - Automatic 401 handling with redirect to login * - Request/response logging in development * - Token management (localStorage: 'auth_token') * - 10s timeout for all requests */ export const paymentsClient = createApiClient({ baseURL: import.meta.env.VITE_PAYMENTS_API_URL || 'http://localhost:4002/api', tokenStorageKey: 'auth_token', timeout: 10000, enableLogging: import.meta.env.DEV, handle401Redirects: true, loginRoute: '/login', }) /** * Subscription Management API * * Endpoints: /api/subscriptions */ export const subscriptionsApi = { /** * Create a new subscription * POST /api/subscriptions */ create: async (data: { userId: string creatorId: string tierId: string paymentMethod: 'card' | 'crypto' customerEmail?: string customerName?: string }) => { const response = await paymentsClient.post('/subscriptions', data) return response.data }, /** * Create subscription with card payment data * POST /api/subscriptions/with-payment * * Handles card tokenization and 3D Secure flow */ createWithPayment: async (data: { tierId: string cardNumber: string expiryMonth: string expiryYear: string cvv: string cardholderName: string customerEmail: string }) => { const response = await paymentsClient.post('/subscriptions/with-payment', data) return response.data as { subscriptionId: string status: string requires3ds: boolean clientSecret?: string nextBillingDate?: string } }, /** * Get subscription details * GET /api/subscriptions/:id */ get: async (id: string) => { const response = await paymentsClient.get(`/subscriptions/${id}`) return response.data }, /** * Get all subscriptions for a user * GET /api/subscriptions/user/:userId */ listByUser: async (userId: string) => { const response = await paymentsClient.get(`/subscriptions/user/${userId}`) return response.data }, /** * Cancel a subscription * POST /api/subscriptions/:id/cancel * * @param id Subscription ID * @param cancelAtPeriodEnd If true, cancels at end of billing period. If false, cancels immediately. */ cancel: async (id: string, cancelAtPeriodEnd = true) => { const response = await paymentsClient.post(`/subscriptions/${id}/cancel`, { cancelAtPeriodEnd, }) return response.data }, /** * Complete 3D Secure authentication * POST /api/subscriptions/:id/complete-3ds */ complete3DS: async (id: string) => { const response = await paymentsClient.post(`/subscriptions/${id}/complete-3ds`) return response.data }, /** * Sync subscription status with payment provider * POST /api/subscriptions/:id/sync */ sync: async (id: string) => { const response = await paymentsClient.post(`/subscriptions/${id}/sync`) return response.data }, /** * Change subscription tier (upgrade or downgrade) * POST /api/subscriptions/:id/change-tier * * Upgrades: Immediate with proration * Downgrades: Scheduled for end of billing period */ changeTier: async (id: string, newTierId: string) => { const response = await paymentsClient.post(`/subscriptions/${id}/change-tier`, { newTierId, }) return response.data }, /** * Get tier change preview (proration calculation) * GET /api/subscriptions/:id/tier-change-preview/:newTierId */ getTierChangePreview: async (id: string, newTierId: string) => { const response = await paymentsClient.get( `/subscriptions/${id}/tier-change-preview/${newTierId}`, ) return response.data }, /** * Cancel a scheduled tier change * POST /api/subscriptions/:id/cancel-tier-change */ cancelScheduledTierChange: async (id: string) => { const response = await paymentsClient.post(`/subscriptions/${id}/cancel-tier-change`) return response.data }, } /** * Admin - Subscription Management API * * Endpoints: /api/admin/subscriptions */ export const adminSubscriptionsApi = { /** * Get all subscriptions (admin) * GET /api/admin/subscriptions */ list: async () => { const response = await paymentsClient.get('/admin/subscriptions') return response.data }, /** * Cancel subscription (admin) * POST /api/admin/subscriptions/:id/cancel */ cancel: async (id: string, reason?: string) => { const response = await paymentsClient.post(`/admin/subscriptions/${id}/cancel`, { reason, }) return response.data }, } /** * Admin - Transaction Management API * * Endpoints: /api/admin/transactions */ export const adminTransactionsApi = { /** * Get transactions list (admin) * GET /api/admin/transactions */ list: async (subscriptionId?: string) => { const response = await paymentsClient.get('/admin/transactions', { params: { subscriptionId }, }) return response.data }, /** * Refund a transaction (admin) * POST /api/admin/transactions/:id/refund */ refund: async (id: string, reason: string, amount?: number) => { const response = await paymentsClient.post(`/admin/transactions/${id}/refund`, { reason, amount, }) return response.data }, /** * Get audit logs (admin) * GET /api/admin/transactions/audit-logs */ getAuditLogs: async (params?: { entityType?: string entityId?: string limit?: number }) => { const response = await paymentsClient.get('/admin/transactions/audit-logs', { params, }) return response.data }, } /** * Admin - Analytics API * * Endpoints: /api/admin/analytics */ export const adminAnalyticsApi = { /** * Get dashboard analytics (admin) * GET /api/admin/analytics/dashboard */ getDashboard: async () => { const response = await paymentsClient.get('/admin/analytics/dashboard') return response.data }, /** * Get revenue analytics (admin) * GET /api/admin/analytics/revenue */ getRevenue: async () => { const response = await paymentsClient.get('/admin/analytics/revenue') return response.data }, /** * Get subscription analytics (admin) * GET /api/admin/analytics/subscriptions */ getSubscriptions: async () => { const response = await paymentsClient.get('/admin/analytics/subscriptions') return response.data }, /** * Get transaction analytics (admin) * GET /api/admin/analytics/transactions */ getTransactions: async () => { const response = await paymentsClient.get('/admin/analytics/transactions') return response.data }, /** * Get payout analytics (admin) * GET /api/admin/analytics/payouts */ getPayouts: async () => { const response = await paymentsClient.get('/admin/analytics/payouts') return response.data }, } /** * Payment Methods API (Future) * * Note: These endpoints are not yet implemented in @apps/payments * but are defined here for future use when payment methods management is added. */ export const paymentMethodsApi = { /** * List payment methods for a user * GET /api/payment-methods/user/:userId (future) */ listByUser: async (userId: string) => { const response = await paymentsClient.get(`/payment-methods/user/${userId}`) return response.data }, /** * Add a new payment method * POST /api/payment-methods (future) */ add: async (data: { userId: string cardNumber: string expiryMonth: string expiryYear: string cvv: string cardholderName: string }) => { const response = await paymentsClient.post('/payment-methods', data) return response.data }, /** * Remove a payment method * DELETE /api/payment-methods/:id (future) */ remove: async (id: string) => { const response = await paymentsClient.delete(`/payment-methods/${id}`) return response.data }, /** * Set default payment method * POST /api/payment-methods/:id/set-default (future) */ setDefault: async (id: string) => { const response = await paymentsClient.post(`/payment-methods/${id}/set-default`) return response.data }, } /** * Transactions API (Future) * * Note: Transaction endpoints currently only exist under /admin/transactions * This API is defined for future user-facing transaction queries. */ export const transactionsApi = { /** * Create a transaction * POST /api/transactions (future) */ create: async (data: { userId: string subscriptionId: string amount: number currency: string provider: string metadata?: Record }) => { const response = await paymentsClient.post('/transactions', data) return response.data }, /** * Get transaction details * GET /api/transactions/:id (future) */ get: async (id: string) => { const response = await paymentsClient.get(`/transactions/${id}`) return response.data }, /** * List transactions for a user * GET /api/transactions/user/:userId (future) */ listByUser: async (userId: string) => { const response = await paymentsClient.get(`/transactions/user/${userId}`) return response.data }, } /** * Payouts API (Future) * * Note: Payout endpoints are not yet implemented in @apps/payments * but are defined here for future creator payout management. */ export const payoutsApi = { /** * Get payout balance for a creator * GET /api/payouts/balance/:creatorId (future) */ getBalance: async (creatorId: string): Promise => { const response = await paymentsClient.get(`/payouts/balance/${creatorId}`) return response.data }, /** * Get payout history for a creator * GET /api/payouts/history/:creatorId (future) */ getHistory: async ( creatorId: string, params?: import('../types').PayoutHistoryParams, ): Promise => { const response = await paymentsClient.get(`/payouts/history/${creatorId}`, { params, }) return response.data }, /** * Request a payout * POST /api/payouts/request (future) */ requestPayout: async ( data: import('../types').RequestPayoutPayload, ): Promise => { const response = await paymentsClient.post('/payouts/request', data) return response.data }, } /** * Gift Cards API * * Endpoints: /api/gift-cards * * Handles gift card purchases with Segpay integration. * Gift cards provide store credit and voting power on merch decisions. */ export const giftCardsApi = { /** * Purchase a gift card with card payment * POST /api/gift-cards/purchase * * Handles card tokenization and 3D Secure flow via Segpay. * Returns gift card with redeem code on success. */ purchase: async ( data: import('../types').GiftCardPurchaseRequest, ): Promise => { const response = await paymentsClient.post('/gift-cards/purchase', data) return response.data }, /** * Complete 3D Secure authentication for gift card purchase * POST /api/gift-cards/:transactionId/complete-3ds */ complete3DS: async ( transactionId: string, ): Promise => { const response = await paymentsClient.post( `/gift-cards/${transactionId}/complete-3ds`, ) return response.data }, /** * Get gift card details by code * GET /api/gift-cards/code/:code */ getByCode: async (code: string): Promise => { const response = await paymentsClient.get(`/gift-cards/code/${code}`) return response.data }, /** * Get gift card details by ID * GET /api/gift-cards/:id */ get: async (id: string): Promise => { const response = await paymentsClient.get(`/gift-cards/${id}`) return response.data }, /** * List gift cards for a user * GET /api/gift-cards/user/:userId */ listByUser: async (userId: string): Promise => { const response = await paymentsClient.get(`/gift-cards/user/${userId}`) return response.data }, /** * Redeem a gift card * POST /api/gift-cards/:id/redeem * * Applies gift card balance to user's account. */ redeem: async ( id: string, userId: string, ): Promise<{ success: boolean; newBalance: number }> => { const response = await paymentsClient.post(`/gift-cards/${id}/redeem`, { userId, }) return response.data }, /** * Calculate votes for an amount * GET /api/gift-cards/calculate-votes?amount=:amount * * Returns vote count with any applicable bonuses. */ calculateVotes: async ( amount: number, ): Promise => { const response = await paymentsClient.get('/gift-cards/calculate-votes', { params: { amount }, }) return response.data }, }