Webhook handlers receive the same event more than once. This is not a bug in the payment processor — it is in the contract. If your handler is not idempotent, you will eventually double-charge a customer or duplicate an order.
The pattern
Every PayMongo event has a stable event ID. On receipt, I check a Postgres table of processed event IDs (with a unique constraint). If the ID exists, return 200 OK and exit. Otherwise insert the ID, process the event, return 200.
That is the whole pattern. Three lines of SQL and a try/catch. It is the difference between sleeping well and reading angry Messenger threads at midnight.
