chore(billing): remove the Stripe rail, which was never configured anywhere - #508
Merged
Conversation
…ywhere
STRIPE_SECRET_KEY and the seven other STRIPE_* vars appear zero times outside
src/ and zero times in .env.example, so isStripeReady() could only ever return
false. Three API routes, a checkout route, a tier grid and a "Manage
subscription" button were all gated on it and permanently unreachable.
Confirmed empty in production before touching the schema:
select count(*) filter (where stripe_customer_id is not null),
count(*) filter (where stripe_subscription_id is not null),
count(*) from users;
→ 0 | 0 | 7
So this removes no data and, more to the point, changes nothing a user sees:
every code path already fell through to the branch below it.
WHAT STAYS. Plans are real and untouched. They are granted through the
OrangeCat/Bitcoin rail (/api/orangecat/entitlement -> recordOcBillingGrant),
with the expiry cron reverting a lapsed pass to free. The pricing CTA cascade
loses only its unreachable middle step: free -> TBA -> [stripe] -> BTC ->
start-free.
WHAT WAS ACTIVELY WRONG. Because isStripeReady() was false, the one part of
Billing that DID render was a notice telling the reader to "add
STRIPE_SECRET_KEY and price IDs" — an environment variable shown to a person
who does not deploy the app and could not act on it. That is the same mistake
NotificationsPill's comment already calls out. Billing now states the plan and
links to /pricing.
Removed: lib/stripe.ts, api/stripe/{checkout,portal,webhook}, api/checkout/
[plan], getUserByStripeCustomerId, two user columns (migration 0063), two
entries from the user-client-view redaction lists, the api/stripe/webhook entry
in proxy.ts's public matcher, the stripe npm dependency, and the smoke/auth
probes for routes that no longer exist.
BillingSettings drops "use client" — with the handlers gone it holds no state,
no effect and no search-param read.
Two mentions deliberately KEPT because they are not the rail: the placeholder
prose describing what a user might build ("Simple dashboard, automated
reminders, Stripe payments…") and the demo project's stack string.
pnpm run verify green. 28 files, 519 deletions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P
github-actions Bot
pushed a commit
that referenced
this pull request
Sep 7, 2026
…rday (#514) #508 removed the Stripe rail. Four living docs kept describing it, and one of them is named in CLAUDE.md as an SSOT — so the wrong answer was the documented answer. - `development/cloud-local-workflows.md` listed `Stripe billing | STRIPE_*` in the env-gated feature table. Replaced with the rail that exists: `ORANGECAT_PAY_URL_{PERSONAL,PRO,TEAM}`, names checked against lib/oc-pay.ts. - `sustainability-gates.md` Gate 3 argued from a `quantity: 1` hardcode in `src/app/api/stripe/checkout/route.ts`. The gate did not go stale, it HARDENED: that route is gone and the BTC rail is a static per-plan URL with no quantity concept, so there is no seat number left in the purchase path to correct. Per-seat Team is now a build, not a config change. - `oc-rail-monetization-scope.md` claimed the Stripe webhook "already calls" updateUserBilling and that /pricing has an isStripeReady() seam to reuse. The write survives (via /api/orangecat/entitlement, plus scripts/grant-plan.ts); the seam does not — the BTC CTA is the default case now, not one to add. - `development/user-flow-audit.md` asserted `/api/stripe/portal` returns 503. It returns 404. CH01-CH04 and the CH04 runbook tested four deleted endpoints; retired with a pointer to what replaced them. `scripts/grant-plan.ts` said it performs "the SAME write the Stripe webhook does" — updated to name the entitlement route instead. Two findings that were not about Stripe: `docs/self-improvement-plan.md` enumerated three `insertPromptHistory` call sites. There are four — `api/control/tab-inject` was missing — and all three line numbers had drifted (270→397, 452→514, 80→138). Following that list would have shipped the runId linking with one dispatch path still unlinked. Line numbers dropped in favour of "grep the symbol"; same for `src/auth.ts:234` in the OC scope doc, which now points at line 268's unrelated code. `src/config/demo.ts` still denied `/api/checkout` under its "money" heading long after #508 deleted that route — a deny rule for a route on no disk, which made the billing section of the sandbox policy look staffed. demo-sandbox.ts already had the reverse check for DEMO_SAFE_FAMILIES ("a family listed as safe that no longer exists is dead reassurance"); the denied lists had no equivalent, which is why that entry survived. Added the mirror check — it caught `/api/checkout` on its first run — and mutation-proved it with a fake prefix. 262 assertions. Also measured and did NOT gate: doc path references (314) and `pnpm run` commands (77) both came back clean once false positives were removed, and the dominant pattern is prose naming a file precisely to record its REMOVAL, which grep cannot tell from rot. A gate there would fire on the correct docs and stay silent on the wrong ones. CLAUDE.md's own four flagged paths were all correct, and all 22 routes in its Views table exist. verify green (13 steps, exit 0). Claude-Session: https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
STRIPE_SECRET_KEYand the seven otherSTRIPE_*vars appear zero times outsidesrc/and zero times in.env.example, soisStripeReady()could only ever return false. Three API routes, a checkout route, a tier grid and a "Manage subscription" button were all gated on it and permanently unreachable.Confirmed empty in production before touching the schema
So this removes no data — and, more to the point, changes nothing a user sees: every code path already fell through to the branch below it.
What stays
Plans are real and untouched. They're granted through the OrangeCat/Bitcoin rail (
/api/orangecat/entitlement→recordOcBillingGrant), with the expiry cron reverting a lapsed pass to free. The pricing CTA cascade loses only its unreachable middle step:What was actively wrong
Because
isStripeReady()was false, the one part of Billing that did render was a notice telling the reader to "addSTRIPE_SECRET_KEYand price IDs" — an environment variable shown to a person who does not deploy the app and cannot act on it. That's the same mistakeNotificationsPill's own comment calls out. Billing now states the plan and links to/pricing.Removed
lib/stripe.ts·api/stripe/{checkout,portal,webhook}·api/checkout/[plan]·getUserByStripeCustomerId· twouserscolumns (migration0063) · two entries from theuser-client-viewredaction lists · theapi/stripe/webhookentry inproxy.ts's public matcher · thestripenpm dependency · the smoke and authenticated-smoke probes for routes that no longer exist.BillingSettingsalso drops"use client"— with the handlers gone it holds no state, no effect and no search-param read.Deliberately kept
Two mentions that are not the rail: the placeholder prose describing what a user might build ("Simple dashboard, automated reminders, Stripe payments…") and the demo project's stack string.
pnpm run verifygreen. 28 files, 519 deletions, 59 insertions.🤖 Generated with Claude Code
https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P