Phase1: Schema + skill event tracking
Some checks failed
Build and Push / build (push) Failing after 38s

This commit is contained in:
2026-05-23 23:45:04 +02:00
parent 55571b1e59
commit ce70fe8480
8 changed files with 1406 additions and 59 deletions

View File

@@ -412,3 +412,111 @@ export interface SupportTicketDetail {
ticket: SupportTicket;
comments: SupportTicketComment[];
}
// ---------------------------------------------------------------------------
// Billing — Phase 1: pricing, lifecycle, and events
// ---------------------------------------------------------------------------
//
// All money values are numbers (CHF). The DB stores NUMERIC and the
// helpers coerce to Number on read. JS floats are exact for integers
// up to 2^53; at this domain (CHF amounts to 2 decimals, unit prices
// to 5 decimals) precision is fine. The Phase 2 billing computation
// will still do arithmetic carefully (sum in cents, round at the
// end) to avoid 0.1 + 0.2 surprises.
/**
* Single-row platform pricing config. Editable via the admin
* pricing page (Phase 2). The `vatRateChli` field is the rate
* applied to invoices whose billing address resolves to CH/LI;
* foreign customers' rates are decided per-invoice from address +
* VAT number, not from this config.
*/
export interface PlatformPricing {
tenantMonthlyFeeChf: number;
tenantSetupFeeChf: number;
threemaMessageChf: number;
vatRateChli: number;
updatedAt: string;
}
/**
* Per-package daily price. Phase 2's admin UI restricts setting
* these to skill-category packages, but the schema accepts any
* package id. A row's existence is what activates billing for that
* package; deleting the row makes it free without affecting the
* append-only event log.
*/
export interface SkillPricing {
skillId: string;
dailyPriceChf: number;
createdAt: string;
updatedAt: string;
}
/**
* Tenant lifecycle bookends mirrored from K8s into Postgres so
* deleted tenants still have a billing record for their final
* invoice. `createdAt` matches PiecedTenant.metadata.creationTimestamp
* at the moment of approval; `deletedAt` is stamped when the admin
* delete endpoint runs.
*/
export interface TenantBillingLifecycle {
tenantName: string;
zitadelOrgId: string;
createdAt: string;
deletedAt: string | null;
}
/**
* Append-only enable/disable event for a package on a tenant.
* Phase 2's billing computation reads the event stream within the
* billing window and collapses it to a set of UTC days during
* which the package was active.
*/
export interface TenantSkillEvent {
id: string;
tenantName: string;
zitadelOrgId: string;
skillId: string;
eventKind: "enabled" | "disabled";
occurredAt: string;
}
/**
* Append-only suspend/resume event. Recorded by the portal at
* command time (when PATCH spec.suspend lands), not at operator
* reconcile time. The few-second delta is irrelevant for monthly
* billing.
*/
export interface TenantSuspensionEvent {
id: string;
tenantName: string;
zitadelOrgId: string;
eventKind: "suspended" | "resumed";
occurredAt: string;
}
/**
* Per-org billing posture and Stripe linkage. Distinct from
* OrgBilling (which is the customer-editable address/VAT block):
* this one is admin-controlled.
*
* `payByInvoice` flips the onboarding gate (Phase 4): when true,
* tenant requests for this org are approvable without a card on
* file. When false, the customer must have a validated Stripe
* payment method before admin approval is allowed.
*
* `stripeCustomerId` is populated by Phase 4's onboarding flow.
* `autoInvoiceEnabled` / `autoRemindersEnabled` give admin per-org
* kill switches for the Phase 6 cron without disabling the cron
* globally.
*/
export interface OrgBillingConfig {
zitadelOrgId: string;
payByInvoice: boolean;
stripeCustomerId: string | null;
autoInvoiceEnabled: boolean;
autoRemindersEnabled: boolean;
createdAt: string;
updatedAt: string;
}