One payment was credited twice
In this controlled demo, the same payment notification arrives twice and the app adds the credits twice. Payment providers resend these notifications, and the handler never checks whether it already counted this one.
Demo built and tested 20 Sept 2026 · Published 26 Sept 2026 · Last reviewed 26 Sept 2026 · By Ctrl+Z Therapy (how we test)
Why did one payment get credited twice?
Because payment providers can deliver the same webhook more than once, and the handler adds credits every time it runs. In our controlled demo (fake payments), the same event delivered twice gave 20 credits for a 10-credit payment. The fix: record each event's unique ID and add the credits in the same database transaction, so a repeat changes nothing.
How to tell if your app has this
No code needed.
- Ask your AI tool: "In my payment webhook handler, what happens if the same event arrives twice? Show me where we check the event ID."
- In your payment provider's test mode, resend an event your app already received (Stripe's dashboard has a Resend button on each event). The balance or order should not change the second time.
- Check for a table that stores processed event IDs with a unique rule. If there isn't one, a repeat is counted again.
Before and after
Same payment delivered twice. Credited twice.
The repeat is skipped. A new payment still counts.
Why AI-built apps end up with this
In normal testing every event arrives once, so a handler that simply adds the credits looks correct. Repeats only happen under real conditions, like a slow response or a timeout, which rarely show up while you build.
How do I fix it? Paste this into your AI tool
My payment webhook adds credits every time it receives an event, so a repeated delivery credits the account twice. Change the handler so it first verifies the webhook signature, then records the event ID in a table where the ID is unique and adds the credits in the same database transaction. If the event ID was already recorded, return success without changing anything. Also make sure the app never treats a message or redirect in the browser as proof of payment.Works with Lovable, Bolt, Cursor, Replit and similar tools. Then run the check-up below on your own app.
How we checked the fix
| Same request | Before | After |
|---|---|---|
| Same payment event, delivered twice | Credited twice (20) | Credited once (10) |
| A new payment event after that | Credited | Credited |
Results from our demo test, run against the same demo before and after the fix.
Common mistakes when fixing this
- Duplicate deliveries are normal, not a bug in your payment provider. Plan for them.
- A success page or message in the browser is not proof of payment. Only the verified webhook is.
Questions people ask
Isn't a duplicate webhook the provider's bug?
No. Stripe's documentation says webhook endpoints might occasionally receive the same event more than once, and recommends logging the event IDs you've processed and skipping ones you've already handled.
Why does the check need to be in the same transaction?
If two copies arrive at the same moment, both can pass a separate "have I seen this?" check. Saving the event ID under a unique rule and adding the credits in one transaction means only one copy can win.
Can I just trust the success page after checkout?
No. A redirect or message in the browser can be faked, or shown without a completed payment. Give credits or access only after the verified webhook, or after asking the provider's API.
For developers: the cause and the fix in code
− balance += event.amount + if save_once(event.id): # unique, same transaction + balance += event.amount
Simplified. Your stack will look different; the principle is the same.