227 lines
4.9 KiB
TypeScript
227 lines
4.9 KiB
TypeScript
/**
|
|
* Subscriptions API Client
|
|
*
|
|
* API functions for subscription and license management
|
|
*/
|
|
|
|
import { get, post } from './client';
|
|
|
|
/**
|
|
* Subscription plan types
|
|
*/
|
|
export type SubscriptionPlan = 'FREE' | 'STARTER' | 'PRO' | 'ENTERPRISE';
|
|
|
|
/**
|
|
* Subscription status types
|
|
*/
|
|
export type SubscriptionStatus =
|
|
| 'ACTIVE'
|
|
| 'PAST_DUE'
|
|
| 'CANCELED'
|
|
| 'INCOMPLETE'
|
|
| 'INCOMPLETE_EXPIRED'
|
|
| 'TRIALING'
|
|
| 'UNPAID'
|
|
| 'PAUSED';
|
|
|
|
/**
|
|
* Billing interval types
|
|
*/
|
|
export type BillingInterval = 'monthly' | 'yearly';
|
|
|
|
/**
|
|
* Plan details
|
|
*/
|
|
export interface PlanDetails {
|
|
plan: SubscriptionPlan;
|
|
name: string;
|
|
maxLicenses: number;
|
|
monthlyPriceEur: number;
|
|
yearlyPriceEur: number;
|
|
features: string[];
|
|
}
|
|
|
|
/**
|
|
* License response
|
|
*/
|
|
export interface LicenseResponse {
|
|
id: string;
|
|
userId: string;
|
|
userEmail: string;
|
|
userName: string;
|
|
userRole: string;
|
|
status: 'ACTIVE' | 'REVOKED';
|
|
assignedAt: string;
|
|
revokedAt?: string;
|
|
}
|
|
|
|
/**
|
|
* Subscription overview response
|
|
*/
|
|
export interface SubscriptionOverviewResponse {
|
|
id: string;
|
|
organizationId: string;
|
|
plan: SubscriptionPlan;
|
|
planDetails: PlanDetails;
|
|
status: SubscriptionStatus;
|
|
usedLicenses: number;
|
|
maxLicenses: number;
|
|
availableLicenses: number;
|
|
cancelAtPeriodEnd: boolean;
|
|
currentPeriodStart?: string;
|
|
currentPeriodEnd?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
licenses: LicenseResponse[];
|
|
}
|
|
|
|
/**
|
|
* Can invite response
|
|
*/
|
|
export interface CanInviteResponse {
|
|
canInvite: boolean;
|
|
availableLicenses: number;
|
|
usedLicenses: number;
|
|
maxLicenses: number;
|
|
message?: string;
|
|
}
|
|
|
|
/**
|
|
* All plans response
|
|
*/
|
|
export interface AllPlansResponse {
|
|
plans: PlanDetails[];
|
|
}
|
|
|
|
/**
|
|
* Checkout session request
|
|
*/
|
|
export interface CreateCheckoutSessionRequest {
|
|
plan: SubscriptionPlan;
|
|
billingInterval: BillingInterval;
|
|
successUrl?: string;
|
|
cancelUrl?: string;
|
|
}
|
|
|
|
/**
|
|
* Checkout session response
|
|
*/
|
|
export interface CheckoutSessionResponse {
|
|
sessionId: string;
|
|
sessionUrl: string;
|
|
}
|
|
|
|
/**
|
|
* Portal session request
|
|
*/
|
|
export interface CreatePortalSessionRequest {
|
|
returnUrl?: string;
|
|
}
|
|
|
|
/**
|
|
* Portal session response
|
|
*/
|
|
export interface PortalSessionResponse {
|
|
sessionUrl: string;
|
|
}
|
|
|
|
/**
|
|
* Get subscription overview for current organization
|
|
*/
|
|
export async function getSubscriptionOverview(): Promise<SubscriptionOverviewResponse> {
|
|
return get<SubscriptionOverviewResponse>('/api/v1/subscriptions');
|
|
}
|
|
|
|
/**
|
|
* Get all available plans
|
|
*/
|
|
export async function getAllPlans(): Promise<AllPlansResponse> {
|
|
return get<AllPlansResponse>('/api/v1/subscriptions/plans');
|
|
}
|
|
|
|
/**
|
|
* Check if organization can invite more users
|
|
*/
|
|
export async function canInviteUser(): Promise<CanInviteResponse> {
|
|
return get<CanInviteResponse>('/api/v1/subscriptions/can-invite');
|
|
}
|
|
|
|
/**
|
|
* Create a Stripe Checkout session for subscription upgrade
|
|
*/
|
|
export async function createCheckoutSession(
|
|
data: CreateCheckoutSessionRequest,
|
|
): Promise<CheckoutSessionResponse> {
|
|
return post<CheckoutSessionResponse>('/api/v1/subscriptions/checkout', data);
|
|
}
|
|
|
|
/**
|
|
* Create a Stripe Customer Portal session
|
|
*/
|
|
export async function createPortalSession(
|
|
data?: CreatePortalSessionRequest,
|
|
): Promise<PortalSessionResponse> {
|
|
return post<PortalSessionResponse>('/api/v1/subscriptions/portal', data || {});
|
|
}
|
|
|
|
/**
|
|
* Sync subscription from Stripe
|
|
* Useful when webhooks are not available (e.g., local development)
|
|
* @param sessionId - Optional Stripe checkout session ID (pass after checkout completes)
|
|
*/
|
|
export async function syncSubscriptionFromStripe(sessionId?: string): Promise<SubscriptionOverviewResponse> {
|
|
return post<SubscriptionOverviewResponse>('/api/v1/subscriptions/sync', { sessionId });
|
|
}
|
|
|
|
/**
|
|
* Format price for display
|
|
*/
|
|
export function formatPrice(amount: number, currency = 'EUR'): string {
|
|
return new Intl.NumberFormat('fr-FR', {
|
|
style: 'currency',
|
|
currency,
|
|
minimumFractionDigits: 0,
|
|
maximumFractionDigits: 0,
|
|
}).format(amount);
|
|
}
|
|
|
|
/**
|
|
* Get plan badge color class
|
|
*/
|
|
export function getPlanBadgeColor(plan: SubscriptionPlan): string {
|
|
switch (plan) {
|
|
case 'FREE':
|
|
return 'bg-gray-100 text-gray-800';
|
|
case 'STARTER':
|
|
return 'bg-blue-100 text-blue-800';
|
|
case 'PRO':
|
|
return 'bg-purple-100 text-purple-800';
|
|
case 'ENTERPRISE':
|
|
return 'bg-amber-100 text-amber-800';
|
|
default:
|
|
return 'bg-gray-100 text-gray-800';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get status badge color class
|
|
*/
|
|
export function getStatusBadgeColor(status: SubscriptionStatus): string {
|
|
switch (status) {
|
|
case 'ACTIVE':
|
|
case 'TRIALING':
|
|
return 'bg-green-100 text-green-800';
|
|
case 'PAST_DUE':
|
|
return 'bg-yellow-100 text-yellow-800';
|
|
case 'CANCELED':
|
|
case 'INCOMPLETE_EXPIRED':
|
|
case 'UNPAID':
|
|
return 'bg-red-100 text-red-800';
|
|
case 'INCOMPLETE':
|
|
case 'PAUSED':
|
|
return 'bg-gray-100 text-gray-800';
|
|
default:
|
|
return 'bg-gray-100 text-gray-800';
|
|
}
|
|
}
|