Crypto Webhooks: How to Receive Payment Events Safely

How to verify, deduplicate and process crypto payment webhooks so a fake, late or repeated event never credits a customer by mistake.

A webhook is a message our platform sends to your server when something happens: a deposit is credited, a payout is confirmed, a card is topped up. It saves you from asking "did anything change?" every few seconds.

But a webhook is also a door into your system. Anyone on the internet can send a request to that URL. The same event can arrive twice. An event can arrive late, or not at all while your server is down. If your handler is careless, any of these can turn into money credited to the wrong person.

This guide shows how to receive crypto payment events safely, step by step, with the mistakes we see most often.

#What a webhook is (and is not)

Think of a webhook as a doorbell. It tells you that someone may be at the door. It does not tell you who it is for certain, and it does not open the door for you.

On our platform every delivery is an HTTPS POST to the endpoint you registered. It carries two headers:

Delivery is at-least-once. That means we promise to try until you answer with a success code, and as a result you may get the same event more than once. Plan for that from day one.

#The events you can expect

The main money events for a TRON wallet business are:

EventFires when
deposit.creditedA deposit has enough confirmations and the user's balance went up
withdrawal.confirmedAn on-chain payout is confirmed on TRON
withdrawal.failedA payout could not complete
card.topupA card load finished
card.statusA card changed status
card.transactionA card was used to pay

The full list, with payload examples for each, is in the webhooks docs. One thing to know early: swaps emit no webhook. If you offer USDT and TRX swaps, poll the swap order until it reaches a final status.

#Step 1: register a clean endpoint

Create the endpoint from the dashboard or the developer API. A few rules make life easier later:

Treat that secret like a password. Keep it in your secrets store, not in your code or your git history. If it ever leaks, regenerate it. The old secret stops working at once, so do it at a time you can watch.

#Step 2: verify the signature before anything else

This is the most important step. Without it, anyone who learns your URL can send a fake deposit.credited and get credited for money they never sent.

To verify:

  1. Read the raw bytes of the request body, before any JSON parser touches it.
  2. Compute HMAC-SHA256 of those bytes with your endpoint secret.
  3. Compare your result with the x-cryptoinfra-signature header using a constant-time comparison.
  4. If they do not match, or the header is missing, reject the request and stop.

The most common bug is step 1. Many web frameworks parse JSON automatically. If you sign the parsed object turned back into text, the bytes change — key order, spaces, number format — and the signature never matches. Developers then "fix" this by turning verification off. Do not do that. Configure your route to give you the raw body instead. The docs have ready examples for Node.js and Python.

#Step 3: answer fast, work later

Each delivery waits a short time for your answer. If your handler is slow — because it calls another service, sends an email or writes to many tables — the delivery is marked failed and retried. Now you process the same event twice.

The safe pattern is:

  1. Verify the signature.
  2. Put the event on a queue or into a jobs table.
  3. Reply with a 2xx code right away.
  4. Let a background worker do the real work.

Your answer only means "I received it". It does not mean "I finished handling it".

#Step 4: deduplicate every event

Because delivery is at-least-once, you will see duplicates. A timeout that actually succeeded, a brief network problem, or an error on your side all cause a retry.

There is no separate delivery id. Deduplicate on the pair of event type and entity id — the deposit id, withdrawal id, card id or order id in the payload. Keep a small table with that pair as its primary key. When an event arrives, try to insert it. If the row already exists, you have handled it before: reply with success and stop.

One exception: status events such as card.status can fire many times for the same card, once per change. Key those on event type, entity id and the new status.

Then make the work itself safe to repeat. For example, crediting an order should check "is this order already paid?" before it adds anything. Two layers of protection beat one.

#Step 5: treat the webhook as a hint, not the truth

Even a valid, signed webhook only says that something may have changed. Before you credit a customer, refund someone or tell support that money moved, read the record from the API:

This matters most when your server was down for a long time. Retries do stop after a while. A webhook that never arrived must not leave money stuck in your system. Run a regular sweep that looks at every record that is not yet final and checks it against the API, whether a webhook came or not. Our guide on how crypto deposits are confirmed on TRON explains why a deposit is only final after it is credited.

#Step 6: test the real path before going live

Do not wait for real money to test your handler. Use the sandbox and the test button for your endpoint. It sends a signed test event through the real delivery system, so it checks your actual signature code, not a simulation. You can pick the event type you want to rehearse.

Test at least these cases:

#A safe webhook checklist

#Common mistakes

MistakeWhat goes wrongFix
No signature checkAnyone can fake a paymentVerify every request, reject failures
Signing parsed JSONValid events fail, so checks get turned offVerify the raw bytes
Normal string compareTiming leaks can help an attackerUse constant-time compare
Heavy work before replyingTimeouts cause repeatsReply first, work in the background
No deduplicationA deposit is credited twiceUnique key on event type and entity id
Trusting the payload aloneStale or missing events strand moneyConfirm with the API, run a sweep
Waiting for a swap webhookThe flow waits foreverPoll the swap order

#In summary

A safe webhook handler does four things: it checks the signature on the raw body, it answers fast, it ignores duplicates, and it confirms money decisions against the API. Do these well and webhooks become a reliable, real-time feed for your wallets and payouts, instead of a risk.

Want signed webhooks, per-user TRON addresses and on-chain payouts under your own brand? Sign up free and send your first test event from the sandbox today.

Launch your own crypto brand.

Wallets, payouts, cards and swaps under your name. Start free and test in the sandbox.