Skip to content

Billing: read a customer portal cancellation as not renewing - #87

Merged
nedtwigg merged 1 commit into
mainfrom
cancel-at-renewal
Nov 4, 2026
Merged

nedtwigg merged 1 commit into
mainfrom
cancel-at-renewal

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Nov 3, 2026

Copy link
Copy Markdown
Member

A real Stripe sandbox shows the customer portal's "Cancel subscription" (mode at_period_end) sets cancel_at to the period's end and leaves cancel_at_period_end false, at API version 2026-08-26.dahlia. @pgstencil/stripe copied only the boolean, so a subscriber who cancelled in the portal stayed cancel_at_period_end = false, and an application reading that column treats them as renewing. Dormouse Hosted's billing page said "Renews automatically" after a cancel, and its renewal notices would have kept going out.

  • The fix: the sync stores cancel_at_period_end as true when the boolean is set, or when cancel_at falls on or before the current period's end. A cancel_at further out still renews at this period's end, so it stays false.
  • StripeDev: a new portal-cancel transition makes the portal's form, since the existing cancel-at-period-end transition can't reproduce this.
  • No schema change: the column keeps its name and now means "does not renew past this period".

The new test goes red with the fixed line reverted. tests/integration/billing.test.ts, pnpm typecheck and pnpm format:check pass locally; I haven't run the full pnpm check.

🤖 Generated with Claude Code

The Stripe customer portal schedules a period-end cancellation through
`cancel_at` and leaves `cancel_at_period_end` false, so a subscriber who
cancelled there still read as renewing. Store either form in the
`cancel_at_period_end` column, and let StripeDev make the portal's form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nedtwigg
nedtwigg merged commit 4ecf8fa into main Nov 4, 2026
1 check passed
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