Implementing USDC Payment Status Webhooks for Onchain Billing
Learn how to set up onchain webhooks for USDC payment status notifications in crypto billing. Covers endpoint design, event mapping, and settlement edge cases.

When you collect payments in USDC onchain, knowing exactly when a transaction settles matters. You will want to update your records, provision access, or trigger fulfillment as soon as a payment is final. Webhooks are the standard way to notify your backend in real time, but onchain settlement brings several differences from card or bank payments. This guide covers how to implement payment status webhooks for onchain USDC settlements, so your system always knows what actually happened.
Why Webhooks for Onchain USDC Payments?
Traditional payment rails rely on webhooks to notify your backend of new payments, failed attempts, or refunds. Onchain, there is no card processor in the middle, but your billing stack still needs a way to react when a blockchain payment settles. Webhooks bridge this gap. When a USDC payment arrives at your settlement address with the required amount and confirmation depth, the webhook event tells your system it’s time to act.
Key Events in Onchain Payment Status
Onchain payments have a few concrete states. You will want to map these to webhook events:
- Payment Initiated: The payer submitted a transaction. This is usually an intent, not a final payment signal.
- Payment Confirmed: The transaction is mined, meets the required confirmation count, and the USDC has settled to your address. This is the canonical “paid” event.
- Payment Failed: The expected payment was not received within a timeout, or the transaction was dropped or replaced.
- Partial Payment / Wrong Amount: Sometimes a payer sends less than required, or splits a payment. This is detectable and should trigger a specific event.
Your backend should listen for at least the confirmed and failed events. The others depend on your billing logic and customer experience.
Designing the Webhook Endpoint
Your webhook endpoint must accept POST requests with a defined JSON schema. It should authenticate requests and respond quickly, since delays may lead to retries or failures.
A typical payload for a confirmed payment event might look like this:
{
"event": "payment_confirmed",
"invoice_id": "inv_abc123",
"chain": "ethereum",
"transaction_hash": "0x6b6...a9f",
"payer_address": "0x123...789",
"amount_usdc": "150.00",
"settlement_address": "0xfeed...beef",
"confirmed_at": "2024-05-01T16:20:00Z",
"block_number": 19876543
}
Design your endpoint to: - Verify the event signature or HMAC. - Parse and validate the payload. - Idempotently update the invoice or subscription status. - Respond with a 2xx status on success.
Mapping Onchain Settlements to Webhook Triggers
Onchain USDC payments are final once included in a block and confirmed past any likely reorg risk. Your webhook provider should only send a “confirmed” event after this point. Do not trigger access or fulfillment on the “initiated” event, since a transaction can fail, be dropped, or never confirm.
A typical flow looks like this:
- Customer pays a USDC invoice by sending 150 USDC to your settlement address.
- Your monitoring service sees the transaction in the mempool, but takes no action yet.
- After 12 block confirmations (on Ethereum), the monitoring service sends a POST to your webhook endpoint with the payment details.
- Your backend matches the invoice ID, marks it as paid, and provisions service.
Edge cases to handle: - Underpayment: If only 100 USDC arrives, send a partial payment event. Do not mark as paid. - Overpayment: If more than required arrives, notify and decide if you refund or credit the difference. - Late payment: If the payment arrives after invoice expiry, mark as late or invalid per your policy.
Worked Example: Subscription Renewal with Onchain Webhook
Suppose you run a SaaS that charges $49/month in USDC. Alice has an active capped approval for your contract with a $100 limit. On June 1:
- Your billing engine detects her subscription is due.
- It triggers a transaction to transfer 49 USDC from Alice’s wallet to your settlement address on Base.
- The transaction hash is
0xabc...123. Your monitoring system watches Base for this hash. - After 6 confirmations, the transfer is final. Your webhook provider sends:
{
"event": "payment_confirmed",
"subscription_id": "sub_789xyz",
"chain": "base",
"transaction_hash": "0xabc...123",
"payer_address": "0xalice...wxyz",
"amount_usdc": "49.00",
"settlement_address": "0xmerchant...feed",
"confirmed_at": "2024-06-01T03:00:00Z",
"block_number": 3200550
}
- Your backend receives the webhook, verifies the signature, and updates Alice’s subscription as renewed. Access is extended another month.
If the transaction fails (e.g., Alice’s wallet has 0 USDC), no confirmed event is sent. Instead, after a timeout, your webhook provider might send:
{
"event": "payment_failed",
"subscription_id": "sub_789xyz",
"chain": "base",
"reason": "insufficient_funds",
"attempted_at": "2024-06-01T02:59:00Z"
}
Handling Retries, Idempotency, and Security
- Retries: Your webhook provider should retry failed POSTs with exponential backoff. Your endpoint must be idempotent, so repeated events do not double-provision or double-update records.
- Idempotency: Use a unique transaction hash or invoice ID as the idempotency key. Store processed events and skip duplicates.
- Security: Authenticate each webhook with an HMAC or secret header. Never trust unauthenticated calls.
- Logging: Log every webhook event and your handler’s response. This is valuable for troubleshooting missing payments or accidental double-handling.
Real-Time Notifications vs Batch Settlement
Onchain webhooks can be real-time, sending events as soon as the confirmation threshold is met, or batch-based, sending updates at fixed intervals. Real-time is preferred for SaaS and access control, but batch can work for lower-frequency invoicing. Design your system to handle both. Always confirm that a payment is final on the blockchain before updating customer access or marking invoices paid.
Common Edge Cases to Test
- Multiple payments for one invoice: Only mark as paid when the full amount is reached.
- Chain reorgs: Webhook should only fire after enough confirmations to prevent accidental rollbacks.
- Manual payments: Some customers pay directly, not via the hosted flow. Match by settlement address and invoice reference.
How We Handle Onchain Payment Status Webhooks at Reclack
At Reclack, we operate non-custodial billing infrastructure for onchain USDC payments. Our webhook events only trigger after a payment is final on Base, Ethereum, or Polygon, and never before the confirmation threshold is met. Each event includes all relevant details: chain, transaction hash, payer, and settlement address. We require HMAC authentication on every webhook call. Idempotency is handled via transaction hash and invoice or subscription ID. There is no reversal after confirmation, and we never touch your funds or private keys. To learn more or try it yourself, visit reclack.com.

