Skip to content

chore(billing): remove the Stripe rail, which was never configured anywhere - #508

Merged
github-actions[bot] merged 1 commit into
mainfrom
chore/drop-stripe
Sep 5, 2026
Merged

chore(billing): remove the Stripe rail, which was never configured anywhere#508
github-actions[bot] merged 1 commit into
mainfrom
chore/drop-stripe

Conversation

@catomean

@catomean catomean commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

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're granted through the OrangeCat/Bitcoin rail (/api/orangecat/entitlementrecordOcBillingGrant), with the expiry cron reverting a lapsed pass to free. The pricing CTA cascade loses only its unreachable middle step:

free → price-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 cannot act on it. That's the same mistake NotificationsPill'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 · two users 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 · the smoke and authenticated-smoke probes for routes that no longer exist.

BillingSettings also 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 verify green. 28 files, 519 deletions, 59 insertions.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UvjGNAS9CMfEGNW26tUR4P

…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
github-actions Bot merged commit 81bf204 into main Sep 5, 2026
3 checks passed
@github-actions
github-actions Bot deleted the chore/drop-stripe branch September 5, 2026 17:04
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>
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