DEV Community

Rama Pratheeba
Rama Pratheeba

Posted on

How to Handle Duplicate Webhook Events in ASP.NET Core (Idempotency Guide)

Duplicate webhook events are not rare.

They are expected behavior.

Most payment providers retry webhook calls when:

Your endpoint times out

They don’t receive a 200 OK

Network issues occur

If your system isn’t idempotent, duplicate events can:

Overwrite valid payment states

Trigger duplicate business logic

Cause inconsistent data

Create financial reconciliation issues

In payment systems, this is dangerous.

Why Duplicate Webhooks Happen

Payment gateways (including Worldpay, Stripe, etc.) retry webhooks intentionally.

From their perspective:
Delivery is not guaranteed.

From your perspective:
You might process the same event twice.

If you don’t protect against it, your system may:

  • Mark a payment as captured twice
  • Send duplicate confirmation emails
  • Create duplicate order records

What Is Idempotency?

Idempotency means:

Processing the same event multiple times results in the same final state.

In other words:
The second attempt does nothing.

A Simple Strategy in ASP.NET Core

One reliable approach:

  1. Store each webhook event ID in the database.
  2. Before processing, check if it already exists.
  3. If it exists → ignore.
  4. If not → process and save.

Example:

public async Task HandleWebhook(WebhookEvent webhook)
{
    var exists = await _context.PaymentEvents
        .AnyAsync(e => e.EventId == webhook.EventId);

    if (exists)
        return;

    var paymentEvent = new PaymentEvent
    {
        EventId = webhook.EventId,
        PaymentId = webhook.PaymentId,
        Status = webhook.Status,
        CreatedAt = DateTime.UtcNow
    };

    _context.PaymentEvents.Add(paymentEvent);
    await _context.SaveChangesAsync();

    // Continue business logic safely
}
Enter fullscreen mode Exit fullscreen mode

This ensures duplicate retries do not corrupt state.

Going Beyond Basic Logging

In real production systems, you also want:

  • Full status transition tracking
  • Previous vs new state comparison
  • Raw payload storage
  • Source tracking (Webhook / Manual / API)

That’s where structured audit logging becomes critical.

Handling Payment Webhook Retries Safely in ASP.NET Core

If you're integrating with payment providers like Worldpay or any webhook-based gateway, you must assume that webhook retries will happen.

Common search scenarios developers face include:

  • How to prevent duplicate webhook processing in ASP.NET Core
  • How to implement idempotent webhook handling in .NET
  • Handling payment webhook retries safely
  • Avoiding duplicate payment status updates
  • Preventing double order creation from webhook calls

The key principle is simple:

Every webhook event must be processed in an idempotent way.

In ASP.NET Core, this typically means:

  • Persisting a unique event ID
  • Checking for existing records before executing business logic
  • Storing structured audit logs for traceability
  • Returning proper HTTP 200 responses only after successful persistence

Without this protection, payment systems become fragile — especially under network instability or gateway retry policies.

Idempotent webhook processing is not an optimization.
It is a requirement for reliable payment architecture.

Final Thoughts

Duplicate webhooks are not a bug.

They are a guarantee.

If your ASP.NET Core payment integration does not implement idempotency, it is fragile by design.

I recently extracted a lightweight audit logging component from a real production payment integration that helps handle scenarios like this.

🎁 I’m offering it free for early developers while gathering feedback.

👉 If you're implementing payment gateways like Worldpay in production and want a complete working integration guide (with logging, validation, and real-world architecture), you can access the full implementation here: https://ramapratheeba.gumroad.com/l/gdzkpw

Top comments (0)