AUTH4D-33 billing contracts

These schemas live in packages/contracts/src/billing.ts and are re-exported from @auth4/contracts. They describe wire shapes only; this fragment enables no routes. The owning billing tickets choose route paths and implement them.

Money, plans and entitlements

  • Money is { amountMinor, currency }: a non-negative safe integer in minor units (cents) and an ISO 4217 code from the closed CurrencyCode enum (currently ZAR). Floats are rejected.
  • PlanId is a closed enum: free, indie, studio. PaidPlanId excludes free.
  • Plan carries price, interval (month or year), trialDays and PlanEntitlements (maxMonthlyActiveUsers, maxApplications, auditLogRetentionDays). Only free may cost zero. Catalog values live in packages/billing, not in contracts.

Management DTOs

  • BillingPlanCatalogResponse lists unique plans.
  • ManagementBillingSubscriptionResponse returns the effective planId, its entitlements and the Subscription or null. Tenants without a subscription are on free.
  • Subscription.status is a closed enum: trialing, active, past_due, canceled or incomplete. trialing requires trialEndsAt. canceledAt is present exactly when the status is canceled.
  • Invoice keeps line items, total and invoice currency consistent. paidAt is present exactly when the status is paid. Lists use ManagementInvoiceListResponse with an optional nextCursor.
  • Payment.status is a closed enum: pending, completed, failed, canceled or expired. completedAt is present exactly when the status is completed, and only failed payments carry a failureReason. Lists use ManagementPaymentListResponse.
  • CreateCheckoutRequest accepts only { planId } with a paid plan. Price and entitlement are always decided server-side. CheckoutSessionResponse returns paymentId, redirectUrl and expiresAt. The redirect must use HTTPS, except for loopback HTTP.
  • CancelSubscriptionRequest accepts { atPeriodEnd }, which defaults to true.

Normalized webhook events

BillingWebhookEvent is the provider-agnostic, signature-verified event that provider adapters emit and the billing state machine consumes. It is discriminated on type; the only current type is payment.status_changed. Every event carries an internal eventId, a provider slug, a providerEventId, the tenantId, and occurredAt/receivedAt. Processing is idempotent on billingWebhookIdempotencyKey(event), which is provider:providerEventId. Raw provider payloads and provider-specific field names are rejected.

Billing mutations emit tenant audit events named from BILLING_AUDIT_EVENTS.

Source: docs/api-spec/auth4d-33.md