Skip to content

Hosted: renewal, annual, price-change, and dispute notices - #1414

Merged
nedtwigg merged 4 commits into
mainfrom
hosted-billing-notices
Oct 29, 2026
Merged

nedtwigg merged 4 commits into
mainfrom
hosted-billing-notices

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Billing PR 4c, stacked on #1413. Billing stays closed.

  • Subscription notices, each a dormouse_billing_notices row (migration 018) recorded before it is sent and stamped once Postmark accepts it, at most once per renewal or dispute, from the 10-minute cron:
    • yearly and founding renewal notice 30 days before renewal (California: 15 to 45), which also serves as the annual reminder;
    • monthly annual reminder 30 days before each renewal that ends a year;
    • price-change notice 21 days before the first renewal it applies at (California: 7 to 30).
  • Billing email routing. Every notice goes to the Stripe customer's email, so provider-only accounts get them too.
  • Price changes (FAIL IF S291). The operator adds a dormouse_price_changes row (procedure in hosted/README.md → "Change a price"). From then on both Prices grant the plan. The cron moves a subscriber to the new Price, without proration, only once their notice went out at least 7 days before that renewal. A schema CHECK repeats that rule. A row naming a founding Price, or one Stripe's Prices contradict, gets no notices and fails the cron run.
  • Dispute notices. Suspension and restoration notices are recorded in the webhook transaction and sent after the commit. The cron retries a failed send. A restoration notice goes out only when no other dispute is open.
  • Terms, Privacy, specs. hosted.md gains "Billing notices". pricing.md moves the notice gates above the fold. TERMS_VERSION and the dates are unchanged, since they are already today's.

Decisions for review: the schedule above; the 30-day renewal notice states the current price even if a price-change notice follows; no notice when a lost dispute ends a subscription; "other notices" to the account email or shown at sign-in remain a launch gate.

Legal wording for review: the five emails in noticeMessage in hosted/server/notices.ts; the price-change and dispute paragraphs of website/src/pages/Terms.tsx; the notice records, Postmark, and retention in website/src/pages/Privacy.tsx.

Not verified: StripeDev models neither Price retrieval nor subscription updates, so price changes run end to end only against the test fake.

Rule IDs: takes S291.

🤖 Generated with Claude Code

nedtwigg and others added 3 commits October 28, 2026 06:32
The account cron records and sends each subscription notice once, to the
Stripe billing email: a yearly renewal notice 30 days ahead, a monthly
plan's annual reminder before each renewal that ends a year, and a price
change's notice 21 days before the renewal it applies at. An operator's
dormouse_price_changes row moves a subscriber to the new Price only after
their notice was sent at least 7 days before that renewal, never from a
founding Price, and only once Stripe's Prices match the recorded amounts.
Opening a dispute records its suspension notice, and closing it in the
account's favor its restoration notice, in the webhook's transaction; the
send follows the commit and the cron retries one that failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Terms state the notice schedule, the billing-email routing and the
records kept, and the dispute suspension and restoration emails. Privacy
lists the notice records, Postmark sending them, and their retention.
hosted.md gains "Billing notices", pricing.md promotes the built notice
gates above the fold, security-hosted.md adds FAIL IF S290, and the Hosted
README documents how an operator schedules a price change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 050a5e2
Status: ✅  Deploy successful!
Preview URL: https://1101f1ae.mouseterm.pages.dev
Branch Preview URL: https://hosted-billing-notices.mouseterm.pages.dev

View logs

…ed-billing-notices

# Conflicts:
#	docs/specs/hosted.md
#	docs/specs/security-hosted.md
#	hosted/server/tests/billing-routes.test.ts
#	scripts/spec-word-budgets.json
#	website/src/pages/Privacy.tsx
Base automatically changed from hosted-billing-rights to main October 29, 2026 00:24
@nedtwigg
nedtwigg merged commit 49bdfd5 into main Oct 29, 2026
13 checks passed
@nedtwigg
nedtwigg deleted the hosted-billing-notices branch October 29, 2026 00:25
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.

2 participants