Repository navigation
Hosted: checkout, billing confirmation, plan section, and founders - #1407
Conversation
Checkout now refuses to start without agreement to the current terms version and to automatic renewal, and records both, with the plan and Price it offered, before answering the Stripe URL (dormouse_checkout_consents, kept as a billing record). PUT /api/billing/founder joins or leaves the founders row (dormouse_founders): joining needs a current founding subscription (subscribedSql gains its prices argument) and a checked name; leaving always works. GET /api/billing adds the price, founder state, and the recorded consent. dor tool hosted now runs billing on StripeDev. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/checkout signs in, then offers the plans on sale at today's price (founding at the open cohort's step) with what each commits the buyer to beside the purchase action, and starts Stripe Checkout only once the buyer ticks agreement to the current terms version and to automatic renewal. The plan and ref survive a provider's sign-in in sessionStorage for ten minutes. /billing confirms the checkout Stripe returned from, strips the query, and keeps a durable confirmation with the founders opt-in, unticked. The account page gains a Plan section with Manage billing, whose lapsed copy branches on entitled. With billing open, the no-plan offer links to checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rs row hosted.md gains the founder route, the checkout's consent fields, the consent and founders rules, the checkout's sessionStorage, and billing in the dev loop. pricing.md moves sign-in-first checkout, consent at the purchase action, the /billing confirmation, and the founders opt-in above the fold; the paid-launch consent gate narrows to confirming receipts meet each territory's durable-confirmation rule. security-hosted.md adds FAIL IF S286 (consent before the Checkout URL) and S287 (founders insert and name). Privacy covers consent records and the chosen founders name; the README turns on Stripe receipts before opening checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
0d22653
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://3cf65339.mouseterm.pages.dev |
| Branch Preview URL: | https://hosted-billing-app.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
The founding price on the renewal checkbox can disagree with the Price the consent row records. Checkout reads the price for the box ("renews automatically every year at $X") from billing.founding when the page loads. The server picks the founding Price later, when the request arrives: withBilling's founding resolver calls foundingCohort and returns setup.founding[open.cohort]. Suppose the open cohort sells its last seat while a buyer is on /checkout. The buyer ticks a box naming one step, Stripe opens at the next, and dormouse_checkout_consents stores that next Price next to a renewal consent the buyer gave at a different price. Stripe's page does show the real total, but the consent this PR records is meant to be the evidence (S286; pricing.md "Checkout and entitlement" asks for consent to automatic renewal with "the interval and renewal price" disclosed beside it, and says "the server records the consent with the Price offered").
One option that follows the existing pattern: the page sends the cohort (or price) it showed, and checkout answers 409 when the open cohort has moved, the same way TERMS_CHANGED handles an outdated terms version. If Stripe's own page counts as the disclosure, the record should say that instead. Either way the Legal reviewer should weigh in, since the PR already asks them to review this wording.
Separately, two comments still point to the section the consent and confirmation rules lived in before this PR. Suggestions below.
The checkout page now sends the price it showed beside the renewal the buyer agreed to. POST checkout expires a checkout whose Price publishedPrice does not read as that price (a cohort sold out since the page loaded, or a checkout left open at an earlier step) and answers 409 PRICE_CHANGED; the page rereads the price and asks for both agreements again. The consent row records the Price the buyer saw, now the one checkout opens at. S286, hosted.md, and pricing.md say so. Two comments now point at pricing.md -> "Checkout and entitlement", not "Paid-launch requirements". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Fixed the consent-price finding in 1691714. |
|
Thanks, 1691714 resolves it. Both sides read the same constants: |
Entitled without a plan, or entitled on a lapsed plan, can only be the ADMIN_EMAIL comp, so the copy says so and the stories are named for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Billing PR 3 of 4, stacked on #1406 (routes), which is stacked on #1405 (core). Billing stays closed until the
STRIPE_*bindings are set.S286).POST checkoutrequiresterms === TERMS_VERSIONandautoRenew: true(400; 409 for an outdated terms version). Before answering with the Stripe URL, it records a row indormouse_checkout_consents(migration016): the checkout, terms version, user, plan, Price, and time. The table has no foreign key, so the record survives account deletion.S287).PUT /api/billing/founderwritesdormouse_founders(migration015). It inserts only whilesubscribedSql(owner, prices)holds on the founding Prices, and answers 409 otherwise; leaving always works. The name follows the 1–64 character, no-control-or-format rule. The public founders list and the website row come in PR 4.GET /api/billingaddsprice,founderandconsent./checkoutsigns the user in, shows the plan and today's price, the disclosures, and the terms and auto-renew checkboxes, then hands off to Stripe. sessionStorage keeps the plan and the validatedreffor 10 minutes, so a provider sign-in keeps the attribution./billingconfirms the purchase and shows a durable confirmation, plus the unticked founders opt-in for founding buyers.NoPlanOfferlinks to/checkoutwhile billing is open.dev.ts, sodor tool hostedcan walk checkout end to end. Not verified yet: the local smoke run was blocked, so nobody has tried that end-to-end walk.hosted.md,pricing.md(the built items move above the fold),security-hosted.md(S286,S287;nextis 288), the audit prompt, Privacy, and the Hosted README. The README now says to turn on Stripe receipt emails, because the confirmation says Stripe sends them.Legal wording for review:
hosted/src/Checkout.tsx: the disclosure list, both checkbox labels, "Continue to payment", and the Stripe/card note.hosted/src/Plan.tsx: the confirmation rows, the status and lapsed copy, and the founders text.Left for PR 4: the EU/UK withdrawal disclosure, account closure cancelling renewal (there is no deletion route yet), and the dispute customer notice.
🤖 Generated with Claude Code