// Tenant self-service billing helpers — wraps /api/v1/billing/* on arcadia-core. // // The provider is resolved server-side (mock in dev, a real PSP in prod), so // these helpers are provider-agnostic: `createCheckoutSession` returns a hosted // URL to redirect the browser to, whatever the backend provider is. // Shapes mirror `ArcadiaWeb.API.BillingJSON`. import type { ArcadiaClient } from "@crema/arcadia-client" // ============================================================================ // Wire types // ============================================================================ export interface BillingOverview { billing_model: string | null plan: string subscription_status: string current_period_start: string | null current_period_end: string | null trial_ends_at: string | null billing_email: string | null plan_limits: Record tenant_limits: Record } export interface PlanPricing { period: string price_cents: number currency: string discount_label: string | null } export interface PlanMeter { meter_key: string included_units: number | null overage_price_cents: number | null } export interface CatalogPlan { slug: string name: string description: string | null billing_track: string trial_days: number metadata: Record pricing: PlanPricing[] meters: PlanMeter[] } export interface Invoice { id: string | null amount: number | null currency: string | null status: string | null due_date: string | null paid_at: string | null invoice_url: string | null pdf_url: string | null } export interface CheckoutSession { url: string } export interface PortalSession { url: string } // ============================================================================ // Reads // ============================================================================ export async function getBillingOverview( arcadia: ArcadiaClient, ): Promise { const res = await arcadia.GET<{ data: BillingOverview }>( "/api/v1/billing/overview", ) return res.data } export async function listBillingPlans( arcadia: ArcadiaClient, ): Promise { const res = await arcadia.GET<{ data: CatalogPlan[] }>( "/api/v1/billing/plans", ) return res.data } export async function listInvoices( arcadia: ArcadiaClient, opts: { limit?: number } = {}, ): Promise { const qs = opts.limit ? `?limit=${opts.limit}` : "" const res = await arcadia.GET<{ data: Invoice[] }>( `/api/v1/billing/invoices${qs}`, ) return res.data } // ============================================================================ // Actions // ============================================================================ /** * Create a hosted checkout session for `plan` and return the URL to redirect * the browser to. `successUrl`/`cancelUrl` are where the provider returns the * user (default: back to this page). */ export async function createCheckoutSession( arcadia: ArcadiaClient, input: { plan: string; successUrl?: string; cancelUrl?: string }, ): Promise { const res = await arcadia.POST<{ data: CheckoutSession }>( "/api/v1/billing/checkout", { body: { plan: input.plan, success_url: input.successUrl, cancel_url: input.cancelUrl, }, }, ) return res.data } /** Create a billing-portal session for self-service payment management. */ export async function createPortalSession( arcadia: ArcadiaClient, input: { returnUrl?: string } = {}, ): Promise { const res = await arcadia.POST<{ data: PortalSession }>( "/api/v1/billing/portal", { body: { return_url: input.returnUrl } }, ) return res.data } /** Cancel the active subscription (at period end). */ export async function cancelSubscription( arcadia: ArcadiaClient, ): Promise { await arcadia.POST("/api/v1/billing/cancel", { body: {} }) }