feat(billing): let custom-plan orgs pick a catalog plan

Merged
PE
11d ago
+44-60

What is this contribution about?

Orgs on the contract (custom) plan saw only "Your plan: Custom. Contact us to change it." in Settings → Plans, with no way to move to a catalog plan themselves.

  • Catalog for custom orgs: they now get the same plan cards as everyone else and can subscribe through Stripe checkout. Whether they already have a subscription is read from Stripe, since their gateway plan id says nothing about it. The Custom card is marked as their current plan.
  • Invoice option: orgs flagged invoice_upgrade (and without a live Stripe subscription) also get "Add to my invoice" on Starter/Business. AI_PLAN_INVOICE_UPGRADE no longer requires the AI usage to be exhausted; the flag is the gate. This relaxes the rule added in #7761 and applies to any flagged org.
  • Staff-managed is now only ai_service: deco's own plan keeps the contact card. Paywalls now quote the next plan up for custom orgs instead of a bare lock.
  • The unused settings.plans.limitReached string is removed.

No ai-gateway change needed: Stripe checkout and the webhook move the org through the gateway's admin plan route, which already accepts custom → starter/business and drops the contract's allowance override on the way out.

How did you verify your code works?

  • bun test apps/api/src/tools/ai-providers/plan-change.test.ts: 7 pass (the test asserting the old usage-exhausted refusal was removed).
  • Web typecheck, oxlint, and knip pass.
  • API typecheck fails at src/mcp-clients/lazy-client.ts:205; the same error reproduces on origin/main without this change.
  • Not checked in a browser yet.

Screenshots/Demonstration

Not captured yet.

How to Test

  1. Put an org on the custom plan through the gateway admin API.
  2. Open Settings → AI providers: the plan catalog shows, with Custom marked as the current plan.
  3. Choose Starter: it opens Stripe checkout.
  4. Add the invoice_upgrade feature override to that org: "Add to my invoice" appears on Starter/Business, and confirming moves the org to that plan.

Review Checklist

  • PR title is clear and descriptive
  • Changes are tested and working
  • Documentation is updated (if needed)
  • No breaking changes (invoice upgrades no longer require exhausted usage)

Summary by cubic

Orgs on the contract (custom) plan used to see only "Your plan: Custom. Contact us to change it." in Settings → Plans. They now see the full plan catalog and can move to a catalog plan themselves: subscribing through Stripe checkout, or — when flagged invoice_upgrade — adding the plan to their deco invoice. Only deco's own ai_service plan keeps the contact card.

  • The Custom card is marked as the current plan, and subscription state is read from Stripe since the gateway plan id says nothing about it.
  • Invoice upgrades are now gated solely by the invoice_upgrade flag; AI_PLAN_INVOICE_UPGRADE no longer requires exhausted AI usage.
  • Paywalls now quote the next plan up for custom orgs instead of a bare lock, and the unused settings.plans.limitReached string is removed.