stripe: per-cadence cooling-off + manage-subscription button

Bundles three related pieces that came out of the operator's first
end-to-end test of the paid flow:

1. Manage subscription button on /settings (paid users with a real
   Stripe sub — i.e. not credit-granted access). POSTs to the existing
   /api/stripe/portal endpoint; Stripe-hosted customer portal handles
   card updates, cancellation, monthly↔annual switch, invoice history.
   Replaces the stale "Paid features unlock with Paddle (D.3) or
   invite credits" hint for free users with a live link to /pricing.

2. Per-cadence cooling-off treatment:

   - **Annual £70**: 14-day free trial via
     subscription_data.trial_period_days=14. No money moves during
     the trial, so the CCR 2013 14-day refund question doesn't arise
     (nothing paid = nothing to refund). Card is still required at
     checkout so Stripe can charge on day 15.

   - **Monthly £7**: bills immediately. A 14-day trial there would
     give away ~50% of cycle one. Instead, /pricing now carries a
     required tick-box above the Subscribe buttons (subscribe stays
     disabled until checked) — by ticking, the user expressly
     consents to begin performance immediately and acknowledges that
     this extinguishes their statutory 14-day right under Reg 36
     CCR 2013. Consent collected on our own page (not via Stripe's
     account-wide consent_collection.terms_of_service) so each
     product can keep its own Terms URL as we add more.

3. T&C §6 clause 1 split into 1a (annual / trial substitute) +
   1b (monthly / Reg 36 waiver via on-page tick-box). Clause 2
   (post-cooling-off cancellation) unchanged.

Settings page shows "Free trial — N days remaining" while the
sub is in `trialing` status, falling back to "Paid subscription
active." once it transitions to active. Countdown is computed
server-side from User.stripe_trial_end_at (new column, migration
0020) populated by the subscription.created/updated webhook from
the Stripe trial_end timestamp; cleared on the trialing→active
transition and on revoke.

Drive-by: fixed a structlog kwarg-name collision on
`log.warning(..., event=event_type, ...)` in both polar_webhook.py
and stripe_billing.py — `event` is structlog's positional event
name and "got multiple values" crashed the user-not-found log
path. Renamed to `event_type=` everywhere it appeared. Caught by
the new trialing-stores-trial-end test.

Tests
- 4 new in test_stripe_billing.py covering monthly (no trial, no
  consent_collection), annual (trial, no consent), trialing stores
  trial_end, trialing→active clears trial_end.
- 1 existing test renamed + reworked for the consent split.
- Full suite: 224 passed, 5 skipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-26 20:06:19 +02:00
parent 62960d5bea
commit a07fd144ea
10 changed files with 390 additions and 31 deletions

View file

@ -98,13 +98,32 @@
<section class="public-section">
<h2 class="public-section__head">6. Refunds</h2>
<p>
<strong>14-day cooling-off (UK / EU consumers).</strong> If you buy a
paid subscription as a consumer, you have 14 days from the day of
purchase to cancel and receive a full refund of that purchase, under
the Consumer Contracts (Information, Cancellation and Additional
Charges) Regulations 2013. As noted in clause 5, if you start using
a paid feature inside the cancellation window you lose the right to
cancel in respect of digital content already delivered.
<strong>14-day cooling-off period (UK / EU consumers).</strong>
The Consumer Contracts (Information, Cancellation and Additional
Charges) Regulations 2013 give consumers a 14-day right to cancel
digital service contracts. We honour this right in different ways
depending on your billing cadence.
</p>
<p>
<strong>1a. Annual subscriptions.</strong> Every new annual
subscription begins with a 14-day free trial. No payment is taken
during the trial; cancel any time before the trial ends and you
are not charged. This is offered as a substitute for, and is at
least as generous as, the statutory 14-day refund right. After
the trial ends and the first payment is taken, the rules in
paragraph 2 below apply.
</p>
<p>
<strong>1b. Monthly subscriptions.</strong> Monthly subscribers
receive immediate access to paid features at checkout and are
billed on day one. Before you can start a monthly subscription
you must tick a required consent box on the pricing page in
which you (i) agree to these Terms, (ii) give express consent to
begin performance immediately, and (iii) acknowledge that, under
Regulation 36 of the Consumer Contracts Regulations 2013, doing
so extinguishes your statutory 14-day right to cancel in respect
of digital content already delivered. The rules in paragraph 2
below apply from the first day.
</p>
<p>
<strong>Cancellation after the cooling-off window.</strong> You can