Skip to content

Cancel Stripe billing when deleting an account - #1093

Merged
kody-bot merged 2 commits into
mainfrom
cursor/w3-stripe-cancel-c46b
Jul 31, 2026
Merged

kody-bot merged 2 commits into
mainfrom
cursor/w3-stripe-cancel-c46b

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 31, 2026 •

Copy link
Copy Markdown
Owner

Part of #1069

Summary

  • immediately cancel active and trialing Stripe subscriptions before account deletion
  • delete the Stripe customer so no future invoices can be generated
  • keep deletion non-blocking on Stripe failures while logging and returning a reconciliation warning
  • skip Stripe calls cleanly for users without a customer

Verification

  • npm run validate green locally (1,712 unit tests and 19 Playwright tests passed)
  • final post-rebase CI validation green across static, Node, Workers, MCP, and Playwright jobs
  • targeted Stripe/deletion tests cover successful cleanup, free users, non-blocking API failures, and resource-ID redaction
  • production deploy healthcheck and smoke check passed
System recap — extends existing primitives (medium risk)

Mode: recap · Base: main @ ffc26e41 · Head: 81ed1c4b

Classification: extends — account deletion now coordinates immediate Stripe subscription and customer cleanup while preserving deletion availability.

Primitives touched

Primitive Group Impact
app-ui surfaces extends — deletion snapshots billing identity and performs best-effort cleanup before removing the user row
billing auth extends — adds immediate subscription cancellation and customer deletion REST operations

System map

Account deletion flows from the authenticated app deletion orchestrator through Stripe billing cleanup before the user row is removed.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	appUi["app-ui<br/>Browser app (Remix 3)"]:::extended
	billing["billing<br/>Stripe billing"]:::extended
	appUi -->|"cancel active subscriptions and delete customer before D1 user deletion"| billing
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Invariants

  • Stripe cleanup uses the customer ID scoped to the deleting database user.
  • Stripe failures are logged and surfaced as warnings but cannot block user-initiated deletion.
  • Free users without a Stripe customer do not make Stripe API requests.

Conductor report

  • Status: merged as bfe039aa and successfully deployed to production.
  • What I verified: direct Stripe DELETE request contracts; mocked account deletion success, free-user no-op, non-blocking failure behavior, and log redaction; authoritative local validation; final post-rebase CI; production healthcheck and smoke check.
  • Scope spill: none; only the two owned implementation files and their existing test files changed.
  • Kent decisions: immediate cancellation is used because deleted users lose portal access; Stripe failures favor completing deletion and emit a reconciliation signal.
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Account deletion now cancels active or trialing billing subscriptions and removes associated billing customer records.
    • Already-canceled subscriptions and accounts without billing records are handled automatically.
  • Bug Fixes
    • Billing cleanup failures are logged as warnings and no longer prevent the rest of the account deletion process from completing.
    • Billing requests now validate deletion responses and protect sensitive identifiers in logs.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Account deletion now inventories Stripe customer IDs, cancels active or trialing subscriptions, deletes Stripe customers, and then removes database data. Stripe failures generate warnings and structured logs without blocking database deletion. Stripe APIs validate DELETE responses and redact resource IDs in logs.

Changes

Stripe account deletion

Layer / File(s) Summary
Stripe deletion APIs
packages/worker/src/billing/stripe-client.ts, packages/worker/src/billing/stripe-client.node.test.ts
Stripe requests now support DELETE. New APIs cancel subscriptions and delete customers after validating request inputs and response shapes. Tests verify successful requests and redacted failure logging.
Account-deletion Stripe cleanup
packages/worker/src/app/account-deletion.ts
The deletion inventory now includes nullable Stripe customer IDs. Cleanup cancels only active or trialing subscriptions, deletes the customer, aggregates failures, and continues D1 deletion after logging Stripe failures.
Deletion behavior validation
packages/worker/src/app/account-deletion.node.test.ts
Tests cover subscription filtering, customer deletion, missing Stripe customers, lookup failures, and non-blocking cleanup failures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AccountDeletion
  participant StripeClient
  participant StripeAPI
  participant D1
  AccountDeletion->>StripeClient: List customer subscriptions
  StripeClient->>StripeAPI: GET subscriptions
  StripeAPI-->>StripeClient: Return subscriptions
  AccountDeletion->>StripeClient: Cancel active or trialing subscriptions
  StripeClient->>StripeAPI: DELETE subscriptions
  AccountDeletion->>StripeClient: Delete Stripe customer
  StripeClient->>StripeAPI: DELETE customer
  AccountDeletion->>D1: Delete account data
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: canceling Stripe billing during account deletion.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/w3-stripe-cancel-c46b

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kody-bot
kody-bot marked this pull request as ready for review July 31, 2026 04:58
@github-actions

github-actions Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-1093.kody-a99.workers.dev

Worker: kody-pr-1093
D1: kody-pr-1093-db
KV: kody-pr-1093-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/worker/src/billing/stripe-client.ts (1)

141-146: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add error-path test coverage for subscription/customer id redaction.

The redaction regex now also covers customers/... and subscriptions/... paths, not just checkout/sessions/.... The added test (stripe-client.node.test.ts lines 208-234) only covers the success path. Add a test that forces a non-2xx or invalid-JSON response for cancelSubscription or deleteCustomer and asserts that console.error receives the redacted path, not the raw subscription or customer id.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/billing/stripe-client.ts` around lines 141 - 146, Add
error-path coverage in stripe-client.node.test.ts for cancelSubscription or
deleteCustomer by forcing a non-2xx or invalid-JSON response, spy on
console.error, and assert the logged endpoint contains the redacted
customers/subscriptions path while excluding the raw identifier.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/worker/src/billing/stripe-client.ts`:
- Around line 141-146: Add error-path coverage in stripe-client.node.test.ts for
cancelSubscription or deleteCustomer by forcing a non-2xx or invalid-JSON
response, spy on console.error, and assert the logged endpoint contains the
redacted customers/subscriptions path while excluding the raw identifier.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cb53e22e-c6d1-4c7d-982c-e1401496fdfc

📥 Commits

Reviewing files that changed from the base of the PR and between 30b51c3 and fa5b17b.

📒 Files selected for processing (4)
  • packages/worker/src/app/account-deletion.node.test.ts
  • packages/worker/src/app/account-deletion.ts
  • packages/worker/src/billing/stripe-client.node.test.ts
  • packages/worker/src/billing/stripe-client.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a604b6c. Configure here.

})
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stripe runs before D1 commit

High Severity

cancelSubscriptionsAndDeleteStripeCustomer runs before deleteUserScopedRowsAndUser. If Stripe customer deletion succeeds but the atomic D1 batch fails, the Kody user row (and stripe_customer_id) remain while the Stripe customer is already gone—billing is torn down but the account is not, often still marked deleting.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a604b6c. Configure here.

cursoragent and others added 2 commits July 31, 2026 05:12
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@cursor
cursor Bot force-pushed the cursor/w3-stripe-cancel-c46b branch from a604b6c to 81ed1c4 Compare July 31, 2026 05:12
@kody-bot
kody-bot merged commit bfe039a into main Jul 31, 2026
10 checks passed
@kody-bot
kody-bot deleted the cursor/w3-stripe-cancel-c46b branch July 31, 2026 05:17
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.

3 participants