Skip to content

feat(quota): admin-granted paid plans via wallet PlanOverride (no Stripe)#2735

Merged
lukemarsden merged 1 commit into
mainfrom
feat/wallet-plan-override
Jun 25, 2026
Merged

feat(quota): admin-granted paid plans via wallet PlanOverride (no Stripe)#2735
lukemarsden merged 1 commit into
mainfrom
feat/wallet-plan-override

Conversation

@lukemarsden

Copy link
Copy Markdown
Collaborator

Admins could only Activate Trial (a Stripe trial sub), which is wrong for a customer who paid out-of-band (bank transfer, no card): it's time-limited, Stripe-managed (so a manual fix gets reverted by Stripe sync — exactly what happened when I bumped find-ai to active and a webhook put it back to trialing), and ties the account to a trial lifecycle. find-ai/Leah hit all of this.

Adds a Stripe-independent plan grant:

  • Wallet.PlanOverride ("pro"|"free"|"") — quota honors it over the Stripe-derived tier in both the org and user paths. Stripe sync only ever writes subscription_*, never PlanOverride, so an admin grant can't be reverted by a webhook.
  • POST /admin/orgs/{id}/plan — set an existing org's plan.
  • Activate-with-plan — grant a paid Pro plan (override, no Stripe) as an alternative to the trial; if the user has no org yet it stashes PlanOnFirstOrg, consumed onto their first org's wallet (consumeUserPlanOnFirstOrg, next to the existing trial/credit consumers in org creation).
  • Frontend: AdminOrgsTable gains a Plan column + set-plan menu (Pro / Free / clear); the Activate dialog gains a plan selector (Trial vs Paid Pro). Regenerated API client + swagger.

Testing

  • Unit (quota_test.go): PlanOverride=pro → Pro with no subscription; =free forces Free even over an active sub. go test ./pkg/quota/ green.
  • go build ./pkg/server/ clean; yarn build clean.
  • Not yet live-exercised end-to-end (ships via release); meta E2E after merge: set a test org to Pro in the admin Orgs UI → GetQuotas returns Pro; confirm a Stripe sync does not change the override.

Once this ships, find-ai gets a proper paid plan (PlanOverride=pro) instead of the spurious trial, and the same admin control covers every out-of-band-paid customer.

🤖 Generated with Claude Code

…ipe)

Admins could only "Activate Trial" (a Stripe trial sub), which is wrong for a
customer who paid out-of-band (bank transfer, no card): it's time-limited,
Stripe-managed — so a manual fix gets reverted by Stripe sync — and ties the
account to a trial lifecycle. find-ai/Leah hit exactly this.

Add a Stripe-independent plan grant:
- Wallet.PlanOverride ("pro"|"free"|""); quota honors it over the Stripe-derived
  tier in both the org and user paths. Stripe sync only writes subscription_*,
  never PlanOverride, so an admin grant can't be reverted by a webhook.
- POST /admin/orgs/{id}/plan to set an existing org's plan.
- "Activate" can grant a paid Pro plan (PlanOverride) as an alternative to the
  Stripe trial; for a user with no org yet it stashes PlanOnFirstOrg, consumed
  onto their first org's wallet (consumeUserPlanOnFirstOrg, alongside the
  existing trial/credit consumers in org creation).
- Frontend: AdminOrgsTable gains a Plan column + set-plan menu (Pro/Free/clear);
  ActivateTrialDialog gains a plan selector (Trial vs Paid Pro). Regenerated
  API client + swagger.

Tests: quota — PlanOverride=pro grants Pro with no subscription; =free forces
Free over an active sub. go build ./pkg/server/ + go test ./pkg/quota/ green;
yarn build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lukemarsden lukemarsden merged commit ee8026a into main Jun 25, 2026
5 checks passed
@lukemarsden lukemarsden deleted the feat/wallet-plan-override branch June 25, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant