frontend_nyla/src/api/subscriptionApi.ts
2026-03-22 17:23:47 +01:00

143 lines
4 KiB
TypeScript

import { ApiRequestOptions } from '../hooks/useApi';
// ============================================================================
// TYPES — aligned with State Machine (wiki/concepts/Subscription-State-Machine.md)
// ============================================================================
export type SubscriptionStatus = 'PENDING' | 'SCHEDULED' | 'TRIALING' | 'ACTIVE' | 'PAST_DUE' | 'EXPIRED';
export type BillingPeriod = 'MONTHLY' | 'YEARLY' | 'NONE';
export interface SubscriptionPlan {
planKey: string;
selectableByUser: boolean;
title: Record<string, string>;
description: Record<string, string>;
currency: string;
billingPeriod: BillingPeriod;
pricePerUserCHF: number;
pricePerFeatureInstanceCHF: number;
autoRenew: boolean;
maxUsers: number | null;
maxFeatureInstances: number | null;
trialDays: number | null;
successorPlanKey: string | null;
}
export interface MandateSubscription {
id: string;
mandateId: string;
planKey: string;
status: SubscriptionStatus;
recurring: boolean;
startedAt: string;
effectiveFrom: string | null;
endedAt: string | null;
currentPeriodStart: string | null;
currentPeriodEnd: string | null;
trialEndsAt: string | null;
snapshotPricePerUserCHF: number;
snapshotPricePerInstanceCHF: number;
stripeSubscriptionId: string | null;
}
export interface SubscriptionStatusResponse {
active: boolean;
subscription: MandateSubscription | null;
plan: SubscriptionPlan | null;
scheduled: MandateSubscription | null;
}
export interface ActivatePlanResponse {
redirectUrl?: string;
[key: string]: unknown;
}
export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<any>;
// ============================================================================
// Helpers
// ============================================================================
function _mandateConfig(mandateId?: string): Record<string, any> {
if (!mandateId) return {};
return { headers: { 'X-Mandate-Id': mandateId } };
}
// ============================================================================
// API FUNCTIONS
// ============================================================================
export async function fetchSelectablePlans(
request: ApiRequestFunction,
mandateId?: string,
): Promise<SubscriptionPlan[]> {
return await request({
url: '/api/subscription/plans',
method: 'get',
additionalConfig: _mandateConfig(mandateId),
});
}
export async function fetchSubscriptionStatus(
request: ApiRequestFunction,
mandateId?: string,
): Promise<SubscriptionStatusResponse> {
return await request({
url: '/api/subscription/status',
method: 'get',
additionalConfig: _mandateConfig(mandateId),
});
}
export async function activatePlan(
request: ApiRequestFunction,
planKey: string,
mandateId?: string,
returnUrl?: string,
): Promise<ActivatePlanResponse> {
return await request({
url: '/api/subscription/activate',
method: 'post',
data: { planKey, returnUrl: returnUrl || '' },
additionalConfig: _mandateConfig(mandateId),
});
}
export async function cancelSubscription(
request: ApiRequestFunction,
subscriptionId: string,
mandateId?: string,
): Promise<Record<string, unknown>> {
return await request({
url: '/api/subscription/cancel',
method: 'post',
data: { subscriptionId },
additionalConfig: _mandateConfig(mandateId),
});
}
export async function reactivateSubscription(
request: ApiRequestFunction,
subscriptionId: string,
mandateId?: string,
): Promise<Record<string, unknown>> {
return await request({
url: '/api/subscription/reactivate',
method: 'post',
data: { subscriptionId },
additionalConfig: _mandateConfig(mandateId),
});
}
export async function verifyCheckout(
request: ApiRequestFunction,
sessionId: string,
mandateId?: string,
): Promise<{ status: string; message: string }> {
return await request({
url: '/api/subscription/checkout/verify',
method: 'post',
data: { sessionId },
additionalConfig: _mandateConfig(mandateId),
});
}