What events does a crypto checkout emit?
InfraIO Pay fires one webhook per state transition. The state machine is small on purpose, so you can key fulfilment to exactly one event and ignore the rest.
- payment.pending, the buyer broadcast the transaction; confirmations are still accruing.
- payment.confirmed, the transaction reached the confirmation threshold for its network.
- payment.settled, funds were swept to your settlement wallet. Fulfil here.
- session.expired, the checkout window closed without payment. Never billed.
Which event should you fulfil on?
Fulfil on payment.settled. A pending transaction can still fail to confirm, and confirmation thresholds vary by network, so treating anything earlier as final risks shipping against a payment that never lands. Settlement is the point at which the money is irreversibly yours.
If you need to show the buyer progress, you can surface pending and confirmed in your UI, but keep the irreversible action (ship, grant access, mint) bound to settled.
How do you verify a webhook is genuine?
Every webhook is signed with your endpoint secret. Recompute the HMAC over the raw request body and compare it in constant time against the signature header; reject anything that does not match. If you are new to HMAC, MDN's SubtleCrypto.sign reference is a good primer, and Stripe's webhook best-practices guide covers the operational side well.
Verify before you parse or act. An unsigned or mis-signed request should never reach your business logic.
How do you make delivery safe to retry?
Webhooks are delivered at least once, so the same event can arrive more than once after a network blip. Each event carries an idempotency key; record the keys you have processed and no-op on repeats. Respond 2xx quickly and do slow work asynchronously so the sender does not time out and retry unnecessarily. InfraIO keeps a delivery log with one-click replay for the times you need to reprocess deliberately.