Thank you! And honestly — re-fetch alone WAS enough for state correctness, and that's
the part I'd emphasize to anyone pre-payments: if every handler re-fetches live state
by subscription ID instead of trusting the payload, processing becomes convergent.
A duplicate event just re-fetches the same truth and writes the same row. Duplicates
become harmless no-ops against the DB.
Where it still bites is side effects. The DB converges, but nothing stops a duplicate
from re-sending the "Welcome to Pro" email, double-logging an upgrade analytics event,
or burning a redundant Stripe API call. So no — I didn't originally store event IDs,
and I learned the hard way that state idempotency ≠ side-effect idempotency.
What's on the backlog now: a small processed_events table (event_id PK, type,
created_at, processed_at). Handler checks it first and returns 200 early on
duplicates, plus an ordering guard — only apply if event.created is newer than the
last applied timestamp for that subscription, so a stale retry can't overwrite
fresher state under concurrent delivery.
Since you're pre-payments on Supabase, the combo I'd wire from day one:
(1) verify signatures, (2) re-fetch live state, (3) event-ID store for side effects.
It's maybe 30 lines of Edge Function and it deletes an entire class of 2 AM bugs.
Happy to share my handler skeleton if it's useful — good luck with the wiring,
it's about to get interesting!