2 Commits

Author SHA1 Message Date
ordinarthur
b0e9425ed5 feat(stripe): on-demand sync fallback when webhooks are missed
Problem: if stripe listen is not running (dev) or the webhook secret is
misconfigured, a successful checkout leaves the user stuck on the free
plan in the DB even though Stripe knows they're subscribed.

Solution: 3 recovery mechanisms.

1. Backend: POST /stripe/sync (auth required)
   Fetches the current user's subscriptions from Stripe by customer ID,
   picks the most recent active/trialing/past_due one, and applies it to
   the User row via the same applySubscriptionToUser helper used by the
   webhook. If no active sub exists, downgrades to free. Returns the
   current plan state.

2. Frontend: CheckoutSuccess now calls /stripe/sync first (instant,
   reliable) before falling back to polling /stripe/subscription. This
   fixes the 'just paid but still free' bug even with no webhook setup.

3. Frontend: 'Rafraîchir' button on the Profile free-plan upgrade banner
   (ghost style with RefreshCw spinning icon). Tooltip hints at its
   purpose. Users who paid but see the free state can click it to
   self-heal in one click.

4. Backend script: scripts/sync-subscription.ts
   - npm run stripe:sync -- user@example.com  (sync one user by email)
   - npm run stripe:sync -- --all             (sync every user with a
                                                stripeId, useful after
                                                a prod webhook outage)
   Colored output with ✓ / ✗ / ↷ status per user.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:07:02 +02:00
ordinarthur
64d2bf4506 feat(stripe): idempotent setup script that provisions products + prices
New script backend/scripts/setup-stripe.ts that:
- Reads STRIPE_SECRET_KEY from .env
- Detects test vs live mode and warns + 5s delay for live
- For each plan (Essentiel 3EUR/mo, Premium 5EUR/mo):
  - Looks up existing price by lookup_key (freedge_essential_monthly,
    freedge_premium_monthly) — idempotent, safe to re-run
  - If missing, creates the product then the recurring price with the
    lookup_key and nickname for clarity
- Prints the resulting price IDs with their env var names
- With --write-env flag, automatically upserts the values into
  backend/.env preserving other lines
- Points to Customer Portal settings and stripe listen command as
  next steps

npm scripts added:
- npm run stripe:setup        # dry run, just print IDs
- npm run stripe:setup:write  # update .env automatically
- npm run stripe:listen       # shortcut for stripe CLI webhook forward

Updated README to show the script as the recommended path for step 1,
keeping the manual dashboard instructions as a fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:58:26 +02:00