ui-nyla/src/api/billingApi.ts

454 lines
12 KiB
TypeScript

import { ApiRequestOptions } from '../hooks/useApi';
// ============================================================================
// TYPES & INTERFACES
// ============================================================================
export type TransactionType = 'CREDIT' | 'DEBIT' | 'ADJUSTMENT';
export type ReferenceType = 'WORKFLOW' | 'PAYMENT' | 'ADMIN' | 'SYSTEM' | 'STORAGE' | 'SUBSCRIPTION';
export interface BillingBalance {
mandateId: string;
mandateName: string;
balance: number;
currency: string;
warningThreshold: number;
isWarning: boolean;
}
export interface BillingTransaction {
id: string;
accountId: string;
transactionType: TransactionType;
amount: number;
description: string;
referenceType?: ReferenceType;
workflowId?: string;
featureInstanceId?: string;
featureCode?: string;
aicoreProvider?: string;
aicoreModel?: string;
createdByUserId?: string;
sysCreatedAt?: string;
mandateId?: string;
mandateName?: string;
userId?: string;
userName?: string;
}
/** Pagination request for GET /api/billing/transactions with `pagination` JSON (table + grouping). */
export interface BillingTransactionsPaginationParams {
page?: number;
pageSize?: number;
sort?: Array<{ field: string; direction: 'asc' | 'desc' }>;
filters?: Record<string, any>;
search?: string;
viewKey?: string;
groupByLevels?: Array<{ field: string; nullLabel?: string; direction?: 'asc' | 'desc' }>;
}
export interface BillingTransactionsPaginatedResponse {
items: BillingTransaction[];
pagination?: {
currentPage: number;
pageSize: number;
totalItems: number;
totalPages: number;
};
groupLayout?: import('./connectionApi').GroupLayout;
appliedView?: { viewKey?: string; displayName?: string };
}
export interface BillingSettings {
id: string;
mandateId: string;
warningThresholdPercent: number;
notifyOnWarning: boolean;
notifyEmails: string[];
autoRechargeEnabled?: boolean;
rechargeAmountCHF?: number;
rechargeMaxPerMonth?: number;
}
export interface BillingSettingsUpdate {
warningThresholdPercent?: number;
notifyOnWarning?: boolean;
notifyEmails?: string[];
autoRechargeEnabled?: boolean;
rechargeAmountCHF?: number;
rechargeMaxPerMonth?: number;
}
export type BillingBucketSize = 'day' | 'month' | 'year';
export interface UsageReport {
dateFrom: string;
dateTo: string;
bucketSize: BillingBucketSize;
totalCost: number;
transactionCount: number;
costByProvider: Record<string, number>;
costByModel: Record<string, number>;
costByFeature: Record<string, number>;
}
export interface StatisticsRangeRequest {
dateFrom: string;
dateTo: string;
bucketSize: BillingBucketSize;
}
export interface AccountSummary {
id: string;
mandateId: string;
userId?: string;
balance: number;
warningThreshold: number;
enabled: boolean;
}
export interface CreditAddRequest {
userId?: string;
amount: number;
description?: string;
}
export interface CheckoutCreateRequest {
userId?: string;
amount: number;
returnUrl: string;
}
export interface CheckoutCreateResponse {
redirectUrl: string;
}
// Type for the request function passed to API functions
export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<any>;
// ============================================================================
// USER API FUNCTIONS
// ============================================================================
/**
* Fetch billing balances for all mandates the user belongs to
* Endpoint: GET /api/billing/balance
*/
export async function fetchBalances(
request: ApiRequestFunction
): Promise<BillingBalance[]> {
return await request({
url: '/api/billing/balance',
method: 'get'
});
}
/**
* Fetch billing balance for a specific mandate
* Endpoint: GET /api/billing/balance/{mandateId}
*/
export async function fetchBalanceForMandate(
request: ApiRequestFunction,
mandateId: string
): Promise<BillingBalance> {
return await request({
url: `/api/billing/balance/${mandateId}`,
method: 'get'
});
}
/**
* Fetch transaction history (table UI: pagination, filters, sort, saved views, grouping).
* Endpoint: GET /api/billing/transactions?pagination=...
*/
export async function fetchTransactionsPaginated(
request: ApiRequestFunction,
params?: BillingTransactionsPaginationParams
): Promise<BillingTransactionsPaginatedResponse> {
const paginationObj: Record<string, unknown> = {};
if (params?.page !== undefined) paginationObj.page = params.page;
if (params?.pageSize !== undefined) paginationObj.pageSize = params.pageSize;
if (params?.sort?.length) paginationObj.sort = params.sort;
if (params?.filters && Object.keys(params.filters).length > 0) paginationObj.filters = params.filters;
if (params?.search) paginationObj.search = params.search;
if (params?.viewKey) paginationObj.viewKey = params.viewKey;
if (params?.groupByLevels !== undefined) paginationObj.groupByLevels = params.groupByLevels;
return await request({
url: '/api/billing/transactions',
method: 'get',
params: { pagination: JSON.stringify(paginationObj) },
});
}
/**
* Fetch transaction history (legacy array window)
* Endpoint: GET /api/billing/transactions
*/
export async function fetchTransactions(
request: ApiRequestFunction,
limit: number = 50,
offset: number = 0
): Promise<BillingTransaction[]> {
return await request({
url: '/api/billing/transactions',
method: 'get',
params: { limit, offset }
});
}
/**
* Fetch usage statistics for an explicit date range.
* Endpoint: GET /api/billing/statistics
*/
export async function fetchStatistics(
request: ApiRequestFunction,
range: StatisticsRangeRequest
): Promise<UsageReport> {
return await request({
url: '/api/billing/statistics',
method: 'get',
params: {
dateFrom: range.dateFrom,
dateTo: range.dateTo,
bucketSize: range.bucketSize,
},
});
}
/**
* Fetch allowed AICore providers
* Endpoint: GET /api/billing/providers
*/
export async function fetchAllowedProviders(
request: ApiRequestFunction
): Promise<string[]> {
return await request({
url: '/api/billing/providers',
method: 'get'
});
}
// ============================================================================
// ADMIN API FUNCTIONS
// ============================================================================
/**
* Fetch billing settings for a mandate (Admin)
* Endpoint: GET /api/billing/admin/settings/{mandateId}
*/
export async function fetchSettingsAdmin(
request: ApiRequestFunction,
mandateId: string
): Promise<BillingSettings> {
return await request({
url: `/api/billing/admin/settings/${mandateId}`,
method: 'get'
});
}
/**
* Create or update billing settings (Admin)
* Endpoint: POST /api/billing/admin/settings/{mandateId}
*/
export async function updateSettingsAdmin(
request: ApiRequestFunction,
mandateId: string,
settings: BillingSettingsUpdate
): Promise<BillingSettings> {
return await request({
url: `/api/billing/admin/settings/${mandateId}`,
method: 'post',
data: settings
});
}
/**
* Add credit to an account (Admin)
* Endpoint: POST /api/billing/admin/credit/{mandateId}
*/
export async function addCreditAdmin(
request: ApiRequestFunction,
mandateId: string,
creditRequest: CreditAddRequest
): Promise<BillingTransaction> {
return await request({
url: `/api/billing/admin/credit/${mandateId}`,
method: 'post',
data: creditRequest
});
}
/**
* Fetch the server-side allow-list of CHF top-up amounts
* Endpoint: GET /api/billing/checkout/amounts
*/
export async function fetchCheckoutAmounts(
request: ApiRequestFunction
): Promise<number[]> {
return await request({
url: '/api/billing/checkout/amounts',
method: 'get'
});
}
/**
* Create Stripe Checkout Session for credit top-up
* Endpoint: POST /api/billing/checkout/create/{mandateId}
*/
export async function createCheckoutSession(
request: ApiRequestFunction,
mandateId: string,
checkoutRequest: CheckoutCreateRequest
): Promise<CheckoutCreateResponse> {
return await request({
url: `/api/billing/checkout/create/${mandateId}`,
method: 'post',
data: checkoutRequest
});
}
/**
* Fetch all accounts for a mandate (Admin)
* Endpoint: GET /api/billing/admin/accounts/{mandateId}
*/
export async function fetchAccountsAdmin(
request: ApiRequestFunction,
mandateId: string
): Promise<AccountSummary[]> {
return await request({
url: `/api/billing/admin/accounts/${mandateId}`,
method: 'get'
});
}
/**
* Fetch all transactions for a mandate (Admin)
* Endpoint: GET /api/billing/admin/transactions/{mandateId}
*/
export async function fetchTransactionsAdmin(
request: ApiRequestFunction,
mandateId: string,
limit: number = 100
): Promise<BillingTransaction[]> {
return await request({
url: `/api/billing/admin/transactions/${mandateId}`,
method: 'get',
params: { limit }
});
}
/**
* User summary for billing admin
*/
export interface MandateUserSummary {
id: string;
username?: string;
email?: string;
firstName?: string;
lastName?: string;
displayName?: string;
}
/**
* Fetch all users for a mandate (Admin)
* Endpoint: GET /api/billing/admin/users/{mandateId}
*/
export async function fetchUsersForMandateAdmin(
request: ApiRequestFunction,
mandateId: string
): Promise<MandateUserSummary[]> {
return await request({
url: `/api/billing/admin/users/${mandateId}`,
method: 'get'
});
}
// ============================================================================
// MANDATE VIEW TYPES & API FUNCTIONS
// ============================================================================
export interface MandateBalance {
mandateId: string;
mandateName: string;
totalBalance: number;
userCount: number;
warningThresholdPercent: number;
}
/**
* Fetch mandate-level balances (SysAdmin only)
* Endpoint: GET /api/billing/view/mandates/balances
*/
export async function fetchMandateViewBalances(
request: ApiRequestFunction
): Promise<MandateBalance[]> {
return await request({
url: '/api/billing/view/mandates/balances',
method: 'get'
});
}
/**
* Fetch mandate-level transactions (SysAdmin only)
* Endpoint: GET /api/billing/view/mandates/transactions
*/
export async function fetchMandateViewTransactions(
request: ApiRequestFunction,
limit: number = 100
): Promise<BillingTransaction[]> {
return await request({
url: '/api/billing/view/mandates/transactions',
method: 'get',
params: { limit }
});
}
// ============================================================================
// USER VIEW TYPES & API FUNCTIONS
// ============================================================================
export interface UserBalance {
accountId: string;
mandateId: string;
mandateName: string;
userId: string;
userName: string;
balance: number;
warningThreshold: number;
isWarning: boolean;
enabled: boolean;
}
export interface UserTransaction extends BillingTransaction {
userId?: string;
userName?: string;
}
/**
* Fetch user-level balances (RBAC-based)
* Endpoint: GET /api/billing/view/users/balances
*/
export async function fetchUserViewBalances(
request: ApiRequestFunction
): Promise<UserBalance[]> {
return await request({
url: '/api/billing/view/users/balances',
method: 'get'
});
}
/**
* Fetch user-level transactions (RBAC-based)
* Endpoint: GET /api/billing/view/users/transactions
*/
export async function fetchUserViewTransactions(
request: ApiRequestFunction,
limit: number = 100
): Promise<UserTransaction[]> {
return await request({
url: '/api/billing/view/users/transactions',
method: 'get',
params: { limit }
});
}