Skip to main content

Billing, AI Usage Limits, and Stripe Setup

Summary​

Implemented Quantago billing scaffolding and AI spend guardrails to stop runaway strategy generation costs.

The change set adds:

  • three plan definitions with bot, daily AI spend, and strategy codegen limits
  • an account-level $2/day AI spend ceiling
  • billing account, usage event, and Stripe webhook event tables
  • Stripe Checkout, Billing Portal, webhook, and usage reporting endpoints
  • a Billing page in the app with plan cards, usage meters, checkout, and portal actions
  • strategy codegen cost controls: cheaper default model, no timeout retries, lower output caps, tighter tool loop limits, and per-tool usage checks
  • Stripe test-mode products/prices using Billing Meters for usage-based pricing

Plans​

PlanBaseDaily AI limitBotsStrategy codegens/day
Starter$19/mo$0.5035
Pro$49/mo$1.251520
Scale$99/mo$2.005050

All plans are still bounded by the global account cap:

export const GLOBAL_DAILY_AI_LIMIT_USD = 2;

Stripe setup​

Created test-mode Stripe products/prices with lookup prefix quantago_dev:

{
"starter": "quantago_dev_starter_base_monthly",
"pro": "quantago_dev_pro_base_monthly",
"scale": "quantago_dev_scale_base_monthly",
"usage": "quantago_dev_ai_usage_cent",
"meterEventName": "quantago_dev_ai_usage_cents"
}

Stripe's current API requires Billing Meters for metered prices, so usage reporting uses /v1/billing/meter_events instead of legacy subscription item usage records.

await stripeRequest(env, '/billing/meter_events', {
method: 'POST',
body: toFormBody({
event_name: getAiUsageMeterEventName(env),
identifier: input.idempotencyKey,
'payload[stripe_customer_id]': input.stripeCustomerId,
'payload[value]': quantity,
}),
});

The production worker is temporarily pointed at STRIPE_PRICE_LOOKUP_PREFIX=quantago_dev because the requested Stripe keys are dev/test keys. STRIPE_SECRET_KEY and STRIPE_PUBLISHABLE_KEY were set on both production and dev workers from the project root Stripe dev env values without printing secrets.

Created test-mode webhook endpoints and set STRIPE_WEBHOOK_SECRET on both workers:

  • production API: we_1TW28ZRwQbtek35WmbqpfVew
  • dev API: we_1TW28dRwQbtek35W2IrLUx6i

Cost-control changes​

Strategy generation was the main cost spike. The default strategy model now avoids Codex:

const DEFAULT_STRATEGY_MODEL = 'gpt-5.4-mini';

Codex remains available but is no longer retried on timeout, defaults to lower reasoning, and has stricter request/stream timeouts plus a smaller output cap. Strategy generation and repair calls also run behind a wall-clock timeout so a stalled provider call stops instead of looping.

const TOOL_ITERATION_CAP = 12;
const STRATEGY_CODE_GENERATION_MAX_ATTEMPTS = 1;
const STRATEGY_CODEGEN_TIMEOUT_MS = 240_000;

Every chat turn now checks account usage before starting, after usage receipts, before expensive strategy codegen tools, and again after tool usage is recorded. Completed inference receipts are written to billing_usage_events and reported to Stripe meter events when the account has a Stripe customer/subscription.

Billing page​

Added /billing to the app and main navigation. The page shows:

  • current plan and subscription status
  • today's AI spend vs. daily cap
  • strategy code generations today
  • bot allowance
  • month-to-date usage
  • plan cards with Checkout actions
  • Billing Portal button for existing Stripe customers

Database​

Added and applied:

services/backend/src/db/migrations/0040_billing_accounts_usage.sql

Created tables:

  • billing_accounts
  • billing_usage_events
  • billing_stripe_events

The full legacy migration runner still stops on old non-idempotent migration 0007_exchange_credentials_and_orders.sql, so 0040 was applied directly with psql and verified in Neon.

Validation and deploys​

Passed:

  • backend pnpm typecheck
  • frontend pnpm typecheck
  • backend pnpm lint with only pre-existing unrelated warnings
  • frontend pnpm lint with only pre-existing unrelated warnings
  • Stripe setup script created/reused all test products, prices, and meter
  • Stripe test webhooks created and signing secrets set on both workers
  • deployed backend: quantago-api version 4592a4de-d51c-491f-a23f-704d400704a5
  • deployed frontend: https://d58399f6.quantago-app.pages.dev
  • smoke checked https://api.quantago.co/api/billing/summary returns auth-protected 401
  • smoke checked https://app.quantago.co/billing returns the SPA shell

Follow-ups​

  • Switch production from quantago_dev lookup keys to real production Stripe lookup keys before accepting live payments.
  • Add usage checks to any non-bot AI routes that generate material OpenAI spend.