feat(web): add billing/subscription client stub for Pro tier

- billing-client.ts: getMySubscription, cancelSubscription, getUsageSummary
- Calls platform-service /subscriptions + /usage endpoints
This commit is contained in:
saravanakumardb1 2026-02-28 14:24:55 -08:00
parent 95f71f4625
commit a5aec74e4d

View File

@ -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<T>(path: string, options?: RequestInit): Promise<T> {
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<string, string>),
},
});
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<Subscription | null> {
try {
return await billingFetch<Subscription>(`/subscriptions/${userId}`);
} catch {
return null;
}
}
export async function cancelSubscription(userId: string): Promise<Subscription | null> {
try {
return await billingFetch<Subscription>(`/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<UsageSummary | null> {
try {
const data = await billingFetch<{ summary: UsageSummary }>(`/usage/summary?userId=${userId}&days=${days}`);
return data.summary as UsageSummary;
} catch {
return null;
}
}