Implementing Webhooks in ASP.NET Core Applications Print

  • 0

What are Webhooks?

Webhooks are HTTP callbacks that notify your application when events occur in external services. They enable real-time integrations without polling.

Creating a Webhook Endpoint

[ApiController]
[Route("api/webhooks")]
public class WebhookController : ControllerBase
{
    private readonly ILogger<WebhookController> _logger;

    public WebhookController(ILogger<WebhookController> logger)
    {
        _logger = logger;
    }

    [HttpPost("payment")]
    public async Task<IActionResult> HandlePaymentWebhook()
    {
        // Read the raw body
        using var reader = new StreamReader(Request.Body);
        var body = await reader.ReadToEndAsync();

        _logger.LogInformation("Received webhook: {Body}", body);

        // Parse and process
        var payload = JsonSerializer.Deserialize<PaymentWebhook>(body);

        // Process the webhook
        await ProcessPaymentAsync(payload);

        // Return 200 OK to acknowledge receipt
        return Ok();
    }
}

Validating Webhook Signatures

Always validate webhook signatures to ensure authenticity:

[HttpPost("stripe")]
public async Task<IActionResult> HandleStripeWebhook()
{
    var json = await new StreamReader(Request.Body).ReadToEndAsync();
    var signature = Request.Headers["Stripe-Signature"];
    var webhookSecret = _configuration["Stripe:WebhookSecret"];

    try
    {
        var stripeEvent = EventUtility.ConstructEvent(
            json, signature, webhookSecret);

        // Handle the event
        switch (stripeEvent.Type)
        {
            case "payment_intent.succeeded":
                var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
                await HandlePaymentSuccessAsync(paymentIntent);
                break;
            // Handle other events...
        }

        return Ok();
    }
    catch (StripeException e)
    {
        _logger.LogError(e, "Webhook signature validation failed");
        return BadRequest();
    }
}

HMAC Signature Validation (Generic)

private bool ValidateSignature(string payload, string signature, string secret)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
    var computedSignature = Convert.ToBase64String(hash);

    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(signature),
        Encoding.UTF8.GetBytes(computedSignature));
}

Webhook Best Practices

  • Return 200 quickly - Process asynchronously if needed
  • Implement idempotency - Handle duplicate deliveries
  • Validate signatures - Never trust unvalidated webhooks
  • Log everything - For debugging failed webhooks
  • Use HTTPS - Always use secure endpoints

Async Processing with Background Service

// Queue webhook for background processing
[HttpPost("order")]
public async Task<IActionResult> HandleOrderWebhook([FromBody] OrderWebhook payload)
{
    // Validate
    if (!ValidateSignature(payload))
        return Unauthorized();

    // Queue for processing
    await _webhookQueue.EnqueueAsync(payload);

    return Ok();
}

// Background service processes the queue
public class WebhookProcessorService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var webhook = await _queue.DequeueAsync(stoppingToken);
            await ProcessWebhookAsync(webhook);
        }
    }
}

Testing Webhooks Locally

Use tools like ngrok to expose your local server:

ngrok http 5000

Then use the ngrok URL as your webhook endpoint during development.


Was this answer helpful?

« Back