paymentsintermediate15 min read

Handling and Verifying Paystack Webhooks in ASP.NET Core

How to securely process Paystack webhook events in ASP.NET Core — HMAC-SHA512 signature validation, raw request stream parsing, constant-time byte comparison, and idempotent handling.

By Tofali EditorialPublished Aug 22, 2026
Verified & CurrentLast tested: Aug 22, 2026
Tested with:dotnet-9.0aspnetcore-9.0paystack-webhooks-v1

This guide was verified against live APIs and production environments.

Handling and Verifying Paystack Webhooks in ASP.NET Core

Polling API endpoints for payment status updates is inefficient and unreliable. Paystack pushes transaction status updates to your backend via HTTP webhooks. However, accepting unverified webhooks exposes your application to spoofing and fraud.

This tutorial demonstrates how to build a secure, constant-time HMAC-SHA512 webhook signature verification middleware and minimal API endpoint in ASP.NET Core 9.0.


1. How Paystack Webhook Verification Works

Paystack signs every HTTP POST webhook payload using your account’s secret key:

  1. Header Name: x-paystack-signature.
  2. Algorithm: HMAC-SHA512.
  3. Payload: The exact, unparsed raw JSON request body bytes.
  4. Validation Rule: Compute HMAC-SHA512 over the raw body bytes using your Paystack Secret Key, convert to a hex string, and compare against x-paystack-signature.

[!CAUTION] You MUST compute the HMAC signature using the raw, unparsed request body string. Deserializing and re-serializing JSON will alter whitespace and key ordering, causing signature validation to fail.


2. Signature Verifier Helper Service

Use System.Security.Cryptography.HMACSHA512 and CryptographicOperations.FixedTimeEquals to prevent timing attack vulnerabilities.

namespace Tofali.Payments.Paystack.Security;

using System.Security.Cryptography;
using System.Text;

public interface IPaystackWebhookVerifier
{
    bool VerifySignature(ReadOnlySpan<byte> bodyBytes, string? signatureHeader, string secretKey);
}

public sealed class PaystackWebhookVerifier : IPaystackWebhookVerifier
{
    public bool VerifySignature(ReadOnlySpan<byte> bodyBytes, string? signatureHeader, string secretKey)
    {
        if (string.IsNullOrWhiteSpace(signatureHeader) || string.IsNullOrWhiteSpace(secretKey))
        {
            return false;
        }

        var keyBytes = Encoding.UTF8.GetBytes(secretKey);

        // Compute HMAC-SHA512 directly on raw body bytes without string re-encoding
        Span<byte> hashBytes = stackalloc byte[64];
        if (!HMACSHA512.TryHashData(keyBytes, bodyBytes, hashBytes, out var bytesWritten) || bytesWritten != 64)
        {
            return false;
        }

        var computedHex = Convert.ToHexStringLower(hashBytes);

        var headerBytes = Encoding.UTF8.GetBytes(signatureHeader.Trim().ToLowerInvariant());
        var computedBytes = Encoding.UTF8.GetBytes(computedHex);

        // Constant-time byte comparison prevents timing side-channel attacks
        return CryptographicOperations.FixedTimeEquals(headerBytes, computedBytes);
    }
}

3. Minimal API Webhook Endpoint

Implement an endpoint that reads the raw HTTP request stream without automatic framework JSON binding:

using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using Tofali.Payments.Paystack.Security;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IPaystackWebhookVerifier, PaystackWebhookVerifier>();

var app = builder.Build();

app.MapPost("/api/webhooks/paystack", async (
    HttpContext httpContext,
    [FromServices] IPaystackWebhookVerifier verifier,
    [FromServices] IConfiguration config,
    [FromServices] ILogger<Program> logger) =>
{
    // 1. Extract signature header
    if (!httpContext.Request.Headers.TryGetValue("x-paystack-signature", out var signatureHeader))
    {
        logger.LogWarning("Paystack webhook request missing x-paystack-signature header.");
        return Results.Unauthorized();
    }

    // 2. Read raw request body bytes
    httpContext.Request.EnableBuffering();
    using var ms = new MemoryStream();
    await httpContext.Request.Body.CopyToAsync(ms);
    var bodyBytes = ms.ToArray();
    httpContext.Request.Body.Position = 0;

    // 3. Verify signature using raw bytes
    var secretKey = config["Paystack:SecretKey"] ?? string.Empty;
    if (!verifier.VerifySignature(bodyBytes, signatureHeader, secretKey))
    {
        logger.LogWarning("Invalid Paystack webhook signature header: {Signature}", (string?)signatureHeader);
        return Results.Unauthorized();
    }

    // 4. Parse verified event payload safely
    using var jsonDoc = JsonDocument.Parse(bodyBytes);
    var root = jsonDoc.RootElement;
    var eventType = root.GetProperty("event").GetString();

    logger.LogInformation("Verified Paystack webhook event received: {Event}", eventType);

    if (!string.IsNullOrEmpty(eventType))
    {
        var data = root.GetProperty("data");
        var reference = data.GetProperty("reference").GetString();
        var amountInKobo = data.GetProperty("amount").GetInt64();

        logger.LogInformation("Processing successful payment event ({Event}) for reference {Reference}, Amount: {Kobo} kobo", eventType, reference, amountInKobo);

        // TODO: Dispatch domain event or update database transaction status
    }

    // Always respond with HTTP 200 OK promptly to acknowledge delivery
    return Results.Ok(new { status = "success" });
});

app.Run();

4. Webhook Security Checklist

  • HTTPS Mandatory: Paystack will only deliver production webhooks over TLS/HTTPS.
  • Fast HTTP 200 Response: Process webhooks asynchronously (or queue them) if your processing takes longer than 3 seconds. Paystack considers non-200 responses as delivery failures and retries delivery.
  • Idempotency: Webhook events may be delivered more than once by Paystack’s retry mechanism. Always ensure your database update logic handles duplicate event delivery safely using the transaction reference.

5. Official Provenance

Tofali Editorial

Verified Author

The Tofali editorial team researches, verifies, and documents African developer infrastructure — payment gateways, identity services, messaging APIs, and cloud platforms.

Expertise:paymentsdeveloper-infrastructureapi-integration

Type a search query to explore African developer infrastructure.

Popular: