Skip to main content

Billing Plans and Roles Spec

This spec defines the canonical behavior for AIQLick plans, subscriptions, credits, and billing roles.

Canonical Plan Data

Production RDS is the canonical source for plan business fields:

PlanActiveBilling ModePriceTrialmaxCompaniesmaxUsersPerCompanyCredits
BasicYesPREPAIDEUR 49 monthly14 days110
ProYesPREPAIDEUR 99 monthly0 days130
EnterpriseYesPOSTPAIDEUR 0 monthly0 daysUnlimitedUnlimited0
Test PlanNoPREPAIDSEK 10 monthly30 days3Unlimited30

Development RDS must match production for plan names, active flags, prices, currencies, billing modes, trials, features, and all plan-limit fields. Stripe price and product IDs are environment-specific and must not be copied from production to development.

Dev-only legacy plans may remain in development only when inactive and retained for historical references. They must not be shown for checkout or public pricing.

Plan Limits

Plan-limit values use the same convention everywhere:

ValueMeaning
-1Unlimited
0Disabled
>0Specific cap

Inputs must reject limits below -1.

Company-scoped limits are enforced by backend PlanLimitGuard or background BillingGate. maxCompanies is user-scoped and counts companies where the user is Company.billingOwnerId. Users with no active subscription are on the free tier and can own exactly one company.

Billing Ownership

Company billing resolution is:

All billing checks must use the same resolution order:

  1. Company.billingOwnerId
  2. Earliest user with the built-in Company Admin role

This fallback exists only for legacy or admin-created companies missing billingOwnerId. New company creation must set billingOwnerId to the creator.

Role Rules

Company subscription and credit-pack management is allowed only for:

ActorAllowed
Super adminYes
Company billing ownerYes
Built-in Company AdminYes
Custom role with tier = "admin"Yes
Member or viewer rolesNo

Economy, External Sales, and Auditor/Viewer are viewer-tier roles. They may read subscription state when the UI allows it, but they cannot mutate company billing.

Subscription Creation

Self-service paid subscription creation must go through Stripe Checkout. The public/authenticated createSubscription mutation is not a self-service billing path and is restricted to super admins for controlled admin assignments and tests.

Direct local subscription creation must not allocate credits unless the subscription is active or trialing.

Inactive plans cannot be selected for checkout, enterprise requests, admin assignment, or approval.

Credits

PREPAID subscriptions allocate creditsPerMonth only when:

  1. The subscription is ACTIVE or TRIALING
  2. The subscription is isActive = true
  3. The plan is PREPAID
  4. creditsPerMonth > 0

Allocation order:

  1. If the billing owner has company billing configs, distribute by percentages and put the remainder in the user's personal balance.
  2. Else if the subscription has companyId, allocate all credits to that company balance.
  3. Else allocate all credits to the user's personal balance.

POSTPAID plans do not allocate credits. They log usage and are invoiced monthly.

Zero-cost gated operations, such as ADD_CANDIDATE, still run subscription and enterprise-block checks, but they do not require a positive credit balance.

Stripe Webhooks

Stripe webhook idempotency must distinguish processed events from retryable events:

Local StripeEvent stateBehavior
No rowCreate processed=false, process handler, then mark processed=true
processed=trueReturn 200 without reprocessing
processed=falseReprocess handler and mark processed=true on success
Concurrent create conflictReturn 500 so Stripe retries

Failed handlers must not become permanent no-ops on Stripe retry.

Dev Sync Procedure

When development plan, system role, or permission assignment data drifts, sync directly against RDS with the guarded backend utility. This is not a seed path and must not run Prisma migrations.

  1. Open network access to prod and dev RDS, usually through AWS SSM port-forwarding.
  2. Set PROD_DATABASE_URL and DEV_DATABASE_URL.
  3. Review the diff:
    npm run rds:sync-system-billing
  4. Apply the diff:
    npm run rds:sync-system-billing -- --apply
  5. Re-run the review command and confirm no drift remains.

Never copy production Stripe IDs into development.

The utility performs these direct RDS changes:

  • Removes Economy -> Subscriptions_WRITE from prod and dev.
  • Mirrors prod global system role permission assignments into dev.
  • Syncs dev plan business fields from prod without copying Stripe price or product IDs.
  • Deactivates active dev-only plans instead of deleting them.