Skip to content

Billing Module

The billing module manages subscription tiers, credit-based usage accounting, and Stripe integration. It lives in src/mods/billing/ and is server-only (gated behind #[cfg(not(target_family = "wasm"))]) except for shared types and UI components.

Billing follows a four-pool credits-only waterfall:

  1. Op-type credit pool — per-operation credits included in the tier (e.g. 9000 voice credits/month), stored in org_budget.*_included
  2. Included credits — general credit pool bundled with the tier, replenished each billing cycle
  3. Purchased credits — top-up credit packs that persist across cycles
  4. Overdraft — drives included_credits negative up to the org’s overdraft limit

All pools store and deduct credits. Native units (minutes, segments, tokens) are converted to credits once via billing_config.*_credits rates before the waterfall starts. If all pools are exhausted and the overdraft limit is reached, the operation is rejected.

src/mods/billing/
├── api/ # Dioxus server functions
│ ├── list_subscription_tiers # GET /api/billing/tiers (public)
│ ├── get_org_subscription # GET /api/billing/subscription
│ ├── create_checkout_session # POST /api/billing/checkout (owner-only)
│ └── create_billing_portal # POST /api/billing/portal (owner-only)
├── components/
│ └── billing_section # Settings page billing UI
├── conf/
│ └── stripe_core_conf # Stripe keys from core_conf table
├── routes/
│ └── stripe_webhook_route # POST /api/billing/stripe/webhook
├── services/
│ ├── stripe_client # Shared Stripe API client (OnceCell)
│ ├── create_stripe_customer # Creates Stripe Customer on signup
│ ├── create_checkout_session # Builds Stripe Checkout Session
│ ├── create_billing_portal # Builds Stripe Billing Portal Session
│ ├── dispatch_stripe_event # Webhook event router
│ ├── record_operation # Waterfall deduction engine
│ ├── check_operation_allowed # Lightweight pre-flight check
│ └── reset_cycle_budget # Replenishes credits on invoice.paid
└── types/ # Shared domain types (WASM-safe)
TablePurpose
billing_configSingle-row global credit pricing for each operation type
subscription_tierPlan catalog (slug, name, price, included allowances, feature flags)
organization_subscriptionOne-per-org active subscription (tier, status, seats, period)
org_budgetPer-org credit balances (included + purchased pools, free allowance counters)
operationAppend-only audit log of every billable event
credit_packAvailable top-up packs
credit_pack_purchasePurchase history linking orgs to packs

The organization table gains a stripe_customer_id column. The core_conf table gains stripe_secret_key and stripe_webhook_secret.

Every new organization gets a Stripe Customer at signup. This happens inline — before the org row is committed — so a Stripe failure rolls back the entire signup.

Both signup paths create the customer:

  • Email/passwordsignup_api.rs calls create_stripe_customer() after creating the user
  • OAuthcreate_organization_with_twilio_service receives the OAuth email and creates the customer inline

The Stripe Customer’s metadata.organization_id links it back to the Loquent org.

VariableRequiredDescription
STRIPE_SECRET_KEYFor billingStripe API secret key, seeded into core_conf
STRIPE_WEBHOOK_SECRETFor billingWebhook signing secret for signature verification

Both are stored in core_conf and loaded via StripeCoreConf. Missing keys don’t crash the app — billing services return clear errors when invoked without configuration.