xpeditis2.0/apps/frontend/src/lib/api/subscriptions.ts
2026-06-18 00:17:44 +02:00

243 lines
5.2 KiB
TypeScript

/**
* Subscriptions API Client
*
* API functions for subscription and license management
*/
import { get, post } from './client';
/**
* Subscription plan types
*/
export type SubscriptionPlan = 'BRONZE' | 'SILVER' | 'GOLD' | 'PLATINIUM';
export type PlanFeature =
| 'dashboard'
| 'wiki'
| 'user_management'
| 'csv_export'
| 'api_access'
| 'custom_interface'
| 'dedicated_kam';
/**
* 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;
maxShipmentsPerYear: number;
bookingFeeEur: number;
supportLevel: 'none' | 'email' | 'direct' | 'dedicated_kam';
statusBadge: 'none' | 'silver' | 'gold' | 'platinium';
planFeatures: PlanFeature[];
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 'BRONZE':
return 'bg-orange-100 text-orange-800';
case 'SILVER':
return 'bg-slate-100 text-slate-800';
case 'GOLD':
return 'bg-yellow-100 text-yellow-800';
case 'PLATINIUM':
return 'bg-purple-100 text-purple-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';
}
}