expria-backend/supabase/migrations/007_sprint_5a_stripe_webhook_events.sql
Hermann_Kitio 6671bac347 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)
2026-04-26 04:15:46 +03:00

30 lines
1.3 KiB
SQL

-- Sprint 5a — Idempotency des webhooks Stripe (TD-13)
--
-- Stripe peut livrer le même `event.id` plusieurs fois (retries réseau,
-- rejeu manuel depuis le dashboard). Cette table sert de journal de
-- déduplication : la route `POST /stripe/webhook` consulte la table
-- avant traitement et y insère l'event après succès.
--
-- Stratégie (cf. TD-13) :
-- 1. Avant traitement, `SELECT 1 FROM stripe_webhook_events WHERE id = $1`.
-- Présent → retour 200 immédiat sans rien faire.
-- 2. Après traitement, `INSERT ... ON CONFLICT DO NOTHING`.
--
-- Race window résiduelle (deux deliveries concurrentes passent toutes deux
-- le SELECT initial) couverte par l'idempotence native des opérations
-- métier (`updateUserPlan`, `updateUserStripeInfo`).
--
-- À exécuter manuellement : `supabase db push` (Hermann — cf. Règle F).
-- Idempotent : sûre à rejouer en dev comme en prod.
CREATE TABLE IF NOT EXISTS stripe_webhook_events (
id TEXT PRIMARY KEY,
processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Index pour les futures purges (rétention ~90 jours envisagée).
CREATE INDEX IF NOT EXISTS stripe_webhook_events_processed_at_idx
ON stripe_webhook_events (processed_at);
COMMENT ON TABLE stripe_webhook_events IS
'Journal de déduplication des webhooks Stripe (Sprint 5a — TD-13).';