Skip to content

24 Sept 2026 · 1 min read

Making Stripe webhooks safe to retry in ASP.NET Core

Verify the signature, remember every event ID, and never enrol a student twice.

  • Stripe
  • Payments
  • ASP.NET Core

Stripe retries webhooks whenever your server does not answer quickly, so the same event can arrive more than once. If the handler grants course access each time, the result is duplicate enrollments or double processing.

Step 1: verify the signature

The payment gateway rebuilds the event from the raw body and the Stripe-Signature header using the webhook secret. A forged request fails before any business logic runs:

var stripeEvent = EventUtility.ConstructEvent(json, signatureHeader, _o.WebhookSecret, throwOnApiVersionMismatch: false);

Step 2: remember every event

A WebhookEvent row stores the gateway's event ID, its type and when it was processed. The service checks for the ID first and returns early if it has been handled:

if (await db.WebhookEvents.AnyAsync(w => w.GatewayEventId == evt.EventId, ct)) return;

Step 3: make the state change idempotent too

Even for a new event, a payment that is already marked Successful returns immediately, and enrolling in a course the student already owns raises a conflict instead of creating a second record.

The platform runs against Stripe in test mode. The pattern is what matters: verify, deduplicate, then change state.

Read the full case study: HRC TECH

Next note

Building the Shop-Zone REST API with Express, JWT and MongoDB