xpeditis2.0/apps/frontend/src/lib/api/subscriptions.ts
2026-01-20 11:28:54 +01:00

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';
}
}