feat(billing): TD-13 webhook idempotency + Stripe Customer Portal + doc cleanup
- Table stripe_webhook_events + helpers isEventProcessed/markEventProcessed - POST /stripe/customer-portal (auth + stripe_customer_id check) - ARCHITECTURE-backend.md: suppression POST /plans/upgrade (duplication doc) - TD-13 fermé dans TECH_DEBT-backend.md - Tests: 261 → 278 verts (+17)
This commit is contained in:
parent
ec0598d122
commit
6671bac347
10 changed files with 891 additions and 324 deletions
|
|
@ -97,15 +97,14 @@
|
|||
### TD-13 — Webhook Stripe non idempotent
|
||||
|
||||
**Priorité :** 🔴 Critique
|
||||
**Statut :** Ouvert — à faire avant mise en production
|
||||
**Description :** Stripe peut livrer un même event webhook deux fois (retries réseau, rejeu manuel depuis le dashboard). La route `POST /stripe/webhook` traite chaque réception sans dédoublonnage. En pratique, les opérations `updateUserPlan` et `updateUserStripeInfo` sont idempotentes par nature (même résultat en cas de double appel), mais si de la logique non idempotente est ajoutée plus tard (ex: compteur, envoi d'email, crédit utilisateur), un double traitement causerait un bug.
|
||||
**À faire :**
|
||||
**Statut :** Résolu — Sprint 5a (2026-04-26)
|
||||
**Description :** Stripe peut livrer un même event webhook deux fois (retries réseau, rejeu manuel depuis le dashboard). La route `POST /stripe/webhook` traite désormais chaque réception via une déduplication explicite : check `stripe_webhook_events(id)` avant traitement, INSERT après succès.
|
||||
**Résolution Sprint 5a :**
|
||||
|
||||
- Créer une table `stripe_webhook_events(id TEXT PRIMARY KEY, processed_at TIMESTAMPTZ)`
|
||||
- Avant traitement, vérifier si `event.id` est déjà en base → si oui, retourner 200 sans rien faire
|
||||
- Après traitement, insérer l'`event.id` dans la table
|
||||
**Session concernée :** Stripe (POST /stripe/webhook)
|
||||
**Condition de résolution :** Avant la mise en production publique.
|
||||
- Migration `supabase/migrations/007_sprint_5a_stripe_webhook_events.sql` — table `stripe_webhook_events(id TEXT PRIMARY KEY, processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW())` + index sur `processed_at`.
|
||||
- Helper `src/lib/stripeWebhookEvents.ts` — `isEventProcessed` / `markEventProcessed` (insert idempotent, conflit unique avalé silencieusement).
|
||||
- `src/routes/stripe.ts` — early return `200 { received: true, replayed: true }` si l'event est déjà journalisé ; `markEventProcessed(event.id)` après traitement réussi (pas si exception, pour permettre rejeu Stripe).
|
||||
- 8 tests unitaires + 2 tests d'intégration (`isEventProcessed`/`markEventProcessed` + comportement route).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -258,3 +257,4 @@ Gate de qualité actuel : npm run test.
|
|||
| TD-16 | Bucket Storage abandonné | 2026-04-25 | Sprint 4b — Deepgram direct |
|
||||
| TD-17 | Limite audio in-memory caduque | 2026-04-25 | Sprint 4b |
|
||||
| TD-18 | RLS Storage caduque | 2026-04-25 | Sprint 4b |
|
||||
| TD-13 | Webhook Stripe idempotent | 2026-04-26 | Sprint 5a |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue