platform-codebase/@packages/@plugins/src/api/index.ts

544 lines
14 KiB
TypeScript
Raw Normal View History

/**
* 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 as string) || 'http://localhost:4002/api',
timeout: 10000,
})
/**
* 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): Promise<import('../types').PaymentMethod[]> => {
const response = await paymentsClient.get(`/payment-methods/user/${userId}`)
return response.data as import('../types').PaymentMethod[]
},
/**
* 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.
*/
/**
* Transaction creation response
*/
export interface TransactionCreateResponse {
id: string
status: string
clientSecret?: string
createdAt: string
amount: number
currency: string
}
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<string, unknown>
}): Promise<TransactionCreateResponse> => {
const response = await paymentsClient.post('/transactions', data)
return response.data as TransactionCreateResponse
},
/**
* Get transaction details
* GET /api/transactions/:id (future)
*/
get: async (id: string): Promise<TransactionCreateResponse> => {
const response = await paymentsClient.get(`/transactions/${id}`)
return response.data as TransactionCreateResponse
},
/**
* List transactions for a user
* GET /api/transactions/user/:userId (future)
*/
listByUser: async (userId: string): Promise<TransactionCreateResponse[]> => {
const response = await paymentsClient.get(`/transactions/user/${userId}`)
return response.data as TransactionCreateResponse[]
},
}
/**
* 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<import('../types').PayoutBalance> => {
const response = await paymentsClient.get(`/payouts/balance/${creatorId}`)
return response.data as import('../types').PayoutBalance
},
/**
* Get payout history for a creator
* GET /api/payouts/history/:creatorId (future)
*/
getHistory: async (
creatorId: string,
params?: import('../types').PayoutHistoryParams,
): Promise<import('../types').PayoutHistoryResponse> => {
const response = await paymentsClient.get(`/payouts/history/${creatorId}`, {
params,
})
return response.data as import('../types').PayoutHistoryResponse
},
/**
* Request a payout
* POST /api/payouts/request (future)
*/
requestPayout: async (
data: import('../types').RequestPayoutPayload,
): Promise<import('../types').Payout> => {
const response = await paymentsClient.post('/payouts/request', data)
return response.data as import('../types').Payout
},
}
/**
* 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<import('../types').GiftCardPurchaseResponse> => {
const response = await paymentsClient.post('/gift-cards/purchase', data)
return response.data as import('../types').GiftCardPurchaseResponse
},
/**
* Complete 3D Secure authentication for gift card purchase
* POST /api/gift-cards/:transactionId/complete-3ds
*/
complete3DS: async (
transactionId: string,
): Promise<import('../types').GiftCardPurchaseResponse> => {
const response = await paymentsClient.post(
`/gift-cards/${transactionId}/complete-3ds`,
)
return response.data as import('../types').GiftCardPurchaseResponse
},
/**
* Get gift card details by code
* GET /api/gift-cards/code/:code
*/
getByCode: async (code: string): Promise<import('../types').GiftCard> => {
const response = await paymentsClient.get(`/gift-cards/code/${code}`)
return response.data as import('../types').GiftCard
},
/**
* Get gift card details by ID
* GET /api/gift-cards/:id
*/
get: async (id: string): Promise<import('../types').GiftCard> => {
const response = await paymentsClient.get(`/gift-cards/${id}`)
return response.data as import('../types').GiftCard
},
/**
* List gift cards for a user
* GET /api/gift-cards/user/:userId
*/
listByUser: async (userId: string): Promise<import('../types').GiftCard[]> => {
const response = await paymentsClient.get(`/gift-cards/user/${userId}`)
return response.data as import('../types').GiftCard[]
},
/**
* 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 as { success: boolean; newBalance: number }
},
/**
* 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<import('../types').VoteCalculation> => {
const response = await paymentsClient.get('/gift-cards/calculate-votes', {
params: { amount },
})
return response.data as import('../types').VoteCalculation
},
}