diff --git a/web/src/lib/billing-client.ts b/web/src/lib/billing-client.ts new file mode 100644 index 0000000..6e69e7f --- /dev/null +++ b/web/src/lib/billing-client.ts @@ -0,0 +1,83 @@ +/** + * Billing / Subscription client stub for ChronoMind. + * + * Calls platform-service subscription + usage endpoints via the auth-api base URL. + * Client-side only — uses the stored auth token for authorization. + */ + +import { PRODUCT_ID, getBaseUrl } from './auth-api'; + +function getToken(): string | null { + if (typeof window === 'undefined') return null; + return localStorage.getItem('chronomind_access_token'); +} + +async function billingFetch(path: string, options?: RequestInit): Promise { + const token = getToken(); + const res = await fetch(`${getBaseUrl()}${path}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + 'x-product-id': PRODUCT_ID, + ...(token ? { Authorization: `Bearer ${token}` } : {}), + ...(options?.headers as Record), + }, + }); + if (!res.ok) throw new Error(`Billing API error: ${res.status}`); + return res.json(); +} + +// ── Types ────────────────────────────────────────────────────────── + +export interface Subscription { + id: string; + productId: string; + userId: string; + plan: 'free' | 'pro'; + status: 'active' | 'cancelled' | 'past_due' | 'trialing'; + currentPeriodStart: string; + currentPeriodEnd: string; + cancelAtPeriodEnd: boolean; + stripeCustomerId?: string; + stripeSubscriptionId?: string; + createdAt: string; + updatedAt: string; +} + +// ── Subscription API ─────────────────────────────────────────────── + +export async function getMySubscription(userId: string): Promise { + try { + return await billingFetch(`/subscriptions/${userId}`); + } catch { + return null; + } +} + +export async function cancelSubscription(userId: string): Promise { + try { + return await billingFetch(`/subscriptions/${userId}`, { + method: 'PUT', + body: JSON.stringify({ cancelAtPeriodEnd: true }), + }); + } catch { + return null; + } +} + +// ── Usage API ────────────────────────────────────────────────────── + +export interface UsageSummary { + timersCreated: number; + routinesCompleted: number; + focusMinutes: number; +} + +export async function getUsageSummary(userId: string, days = 30): Promise { + try { + const data = await billingFetch<{ summary: UsageSummary }>(`/usage/summary?userId=${userId}&days=${days}`); + return data.summary as UsageSummary; + } catch { + return null; + } +}