-
Notifications
You must be signed in to change notification settings - Fork 136
feat(billing): launch Pro and Team plans with single-seat subscriptions #705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
fcb166c
feat(pricing): add billing guide, FAQ, and clean up features table
softmarshmallow 31a0719
chore(lefthook): include mdx in oxfmt pre-commit glob and fmt billing…
softmarshmallow 72b3354
docs(billing): add Team plan to billing guide and pricing FAQ
softmarshmallow 12c597b
fix(auth): ensure redirect URI is resolved with request origin
softmarshmallow 74c0765
test(billing): add 185 manual test cases for v1 billing system
softmarshmallow de98143
feat(supabase/billing): add grida_billing schema and drop legacy pric…
softmarshmallow 2fc305b
feat(billing): add lib/billing seam, Stripe setup script, and import …
softmarshmallow 4cc6702
feat(billing): add Stripe webhook receiver
softmarshmallow e9dc2bc
feat(billing): add billing settings page and upgrade flow
softmarshmallow 453f06c
refactor(workspace): derive plan from billing source, drop display_plan
softmarshmallow 56018f1
fix(pricing): default to monthly view
softmarshmallow 2d1ff14
test(billing): add webhook E2E suite gated by BILLING_E2E=1
softmarshmallow 28019ce
test(billing): add monthly→annual interval upgrade test cases
softmarshmallow 397fb39
fix(billing/test): keep billing E2E and Playwright excluded from defa…
softmarshmallow 9aeef5a
fix(billing): lazy-init Stripe client so Vercel build doesn't need th…
softmarshmallow e4e716a
refactor(billing): defer multi-seat to v1.x, ship single-seat v1
softmarshmallow 0e13140
review(billing): address review feedback + UX polish
softmarshmallow 6605957
review(billing): second-pass review fixes
softmarshmallow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| # Contributing to Grida | Billing | ||
|
|
||
| Setup guide for contributors working on the billing surface. Once Stripe and Supabase are wired locally, the rest of the codebase works the same against any test account. | ||
|
|
||
| > **We don't share Stripe credentials.** Every contributor uses their own free Stripe test account. | ||
|
|
||
| --- | ||
|
|
||
| ## What you need | ||
|
|
||
| - Local Supabase running (`supabase start`). | ||
| - A free Stripe account in **test mode** (no payment info required at signup). | ||
| - The Stripe CLI: `brew install stripe/stripe-cli/stripe` or [stripe.com/docs/stripe-cli](https://stripe.com/docs/stripe-cli). | ||
| - Node 24 + pnpm (covered by the repo-wide setup). | ||
|
|
||
| --- | ||
|
|
||
| ## Setup | ||
|
|
||
| ### 1. Stripe test account | ||
|
|
||
| Sign up at [dashboard.stripe.com](https://dashboard.stripe.com), switch to **Test mode**, and copy your **Secret key** (starts with `sk_test_…`) from **Developers → API keys**. | ||
|
|
||
| > Don't use a live key. The test fixtures refuse to start unless `STRIPE_SECRET_KEY` begins with `sk_test_`. | ||
|
|
||
| ### 2. Local Supabase | ||
|
|
||
| ```bash | ||
| supabase start | ||
| supabase db reset | ||
| ``` | ||
|
|
||
| ### 3. Secrets in `.env.test.local` | ||
|
|
||
| Committed defaults live in `editor/.env.test`. Secrets go in `editor/.env.test.local` (gitignored): | ||
|
|
||
| ```bash | ||
| supabase status -o env | grep SUPABASE_SECRET_KEY >> editor/.env.test.local | ||
| echo 'STRIPE_SECRET_KEY=sk_test_...' >> editor/.env.test.local | ||
| # STRIPE_WEBHOOK_SECRET=whsec_... ← add after step 5 | ||
| ``` | ||
|
|
||
| ### 4. Provision Stripe products + portal config | ||
|
|
||
| ```bash | ||
| pnpm tsx editor/scripts/billing/setup-stripe-test.ts | ||
| ``` | ||
|
|
||
| Idempotent. Creates products/prices in your sandbox and writes the resulting Stripe IDs into the catalog. Re-run after every `supabase db reset`. | ||
|
|
||
| ### 5. Forward webhooks | ||
|
|
||
| In a dedicated terminal, kept open during development: | ||
|
|
||
| ```bash | ||
| stripe listen --forward-to localhost:3000/private/webhooks/stripe | ||
| ``` | ||
|
|
||
| Copy the printed `whsec_…` into `STRIPE_WEBHOOK_SECRET` in `.env.test.local`. The signing secret is per-`stripe listen` session — restart resets it. | ||
|
|
||
| ### 6. Run + try the flow | ||
|
|
||
| ```bash | ||
| pnpm dev --filter=editor | ||
| ``` | ||
|
|
||
| Sign in as `insider@grida.co` / `password`. Go to org settings → Billing → Upgrade. Use the test card `4242 4242 4242 4242`, any future expiry, any CVC. Watch the `stripe listen` terminal: events flow in, the local DB mirrors, the sidebar plan badge updates within a couple seconds. | ||
|
|
||
| --- | ||
|
|
||
| ## E2E suite | ||
|
|
||
| Three integration tests against your real Stripe sandbox. Refuses to start unless every channel is demonstrably test-mode. | ||
|
|
||
| ```bash | ||
| pnpm --filter editor vitest run lib/billing/__tests__/e2e | ||
| ``` | ||
|
|
||
| See the suite's own README for the contract. | ||
|
|
||
| --- | ||
|
|
||
| ## Stable surface | ||
|
|
||
| A few names that don't change and are useful to know: | ||
|
|
||
| - **DB schema**: `grida_billing.*` (locked down, internal). Public read access through views like `v_billing_subscription` and RPCs prefixed `fn_billing_*`. | ||
| - **Projector**: `public.fn_billing_apply_stripe_event` is the only place subscription state mutates from a webhook. All projection logic is PL/pgSQL. | ||
| - **Webhook path**: `/private/webhooks/stripe` — what Stripe POSTs to, verified by signature. | ||
|
|
||
| Anything else (file layout under `editor/`, server action names, type names) is a moving target — read the code. | ||
|
|
||
| User-facing billing docs: [`docs/platform/billing.mdx`](../platform/billing.mdx). Behaviour test cases live at `test/billing-*.md` in the repo root. | ||
|
|
||
| --- | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| - **`STRIPE_SECRET_KEY is required`** — `.env.test.local` not loaded. Confirm path and contents. | ||
| - **`plan.pro price not wired`** — you haven't run the setup script since your last `supabase db reset`. | ||
| - **Webhook signature verification failing** — your `stripe listen` was restarted and produced a new `whsec_…`. Update `STRIPE_WEBHOOK_SECRET`. | ||
| - **Sidebar plan stale** — the local mirror updates only when the webhook lands. Check that `stripe listen` is running. | ||
| - **Customer Portal flows error with "no Stripe customer"** — the org hasn't upgraded yet. Stripe customers are lazy-created on first paid checkout. | ||
|
|
||
| --- | ||
|
|
||
| ## Required env reference | ||
|
|
||
| | Variable | Where | | ||
| | --------------------------------------------- | ------------------------------ | | ||
| | `NEXT_PUBLIC_SUPABASE_URL` | `editor/.env.test` (committed) | | ||
| | `SUPABASE_SECRET_KEY` | `editor/.env.test.local` | | ||
| | `STRIPE_SECRET_KEY` | `editor/.env.test.local` | | ||
| | `STRIPE_WEBHOOK_SECRET` | `editor/.env.test.local` | | ||
| | `BILLING_E2E`, `BILLING_TEST_MODE`, `APP_URL` | `editor/.env.test` (committed) | | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add required Markdown frontmatter with
format: md.For
.mddocs files, include frontmatter and setformat: mdto opt out of MDX parsing.As per coding guidelines, "For files that don't use JSX/MDX features, add
format: mdto frontmatter to opt out of MDX parsing entirely and prevent angle-bracket issues."🤖 Prompt for AI Agents
Move this page under an actively maintained docs directory.
This file is introduced under
docs/contributing/, but this repository’s docs maintenance scope is limited todocs/wg/**anddocs/reference/**.As per coding guidelines, "When writing documentation, the root ./docs directory is the source of truth, and only actively maintain docs/wg/** and docs/reference/** directories."
🤖 Prompt for AI Agents