paymentsintermediate14 min read

Integrating Paystack with ASP.NET Core

A production-grade guide to payment initialization and verification in ASP.NET Core — HttpClientFactory configuration, strongly-typed DTOs, security, and error handling.

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

This guide was verified against live APIs and production environments.

Integrating Paystack with ASP.NET Core

Integrating Paystack into a modern ASP.NET Core backend requires more than wrapping HTTP requests. Production payment integrations require resilient HTTP client configuration, strongly-typed data contracts, secure secret management, and robust error handling.

This tutorial demonstrates how to build a production-ready Paystack API client using ASP.NET Core 9.0, IHttpClientFactory, and C# 13 features.


1. Prerequisites and Environment Setup

Before implementing the API integration, ensure you have:

  • .NET 9.0 SDK or .NET 8.0 SDK installed.
  • A Paystack merchant account with access to test secret keys on the Paystack Dashboard.
  • Verified base API URL: https://api.paystack.co.
  • Secret Key format: sk_test_... for development or sk_live_... for production.

[!IMPORTANT] Never embed your Paystack secret key (sk_test_... or sk_live_...) in C# source code or commit it to version control. Store it in environment variables or Azure Key Vault / User Secrets.


2. Strongly-Typed Configuration and DTOs

Define configuration options and request/response models representing Paystack’s official API contracts.

Configuration Model (PaystackOptions.cs)

namespace Tofali.Payments.Paystack;

public sealed class PaystackOptions
{
    public const string SectionName = "Paystack";

    public string SecretKey { get; set; } = string.Empty;
    public string BaseUrl { get; set; } = "https://api.paystack.co/";
    public int TimeoutSeconds { get; set; } = 30;
}

Transaction Initialize Models

Paystack expects amount in kobo (integer minor units). For example, ₦5,000.00 is sent as 500000.

using System.Text.Json.Serialization;

namespace Tofali.Payments.Paystack.Models;

public sealed record InitializeTransactionRequest(
    [property: JsonPropertyName("email")] string Email,
    [property: JsonPropertyName("amount")] long AmountInKobo,
    [property: JsonPropertyName("reference")] string Reference,
    [property: JsonPropertyName("callback_url")] string? CallbackUrl = null
);

public sealed record InitializeTransactionResponseData(
    [property: JsonPropertyName("authorization_url")] string AuthorizationUrl,
    [property: JsonPropertyName("access_code")] string AccessCode,
    [property: JsonPropertyName("reference")] string Reference
);

public sealed record PaystackApiResponse<T>(
    [property: JsonPropertyName("status")] bool Status,
    [property: JsonPropertyName("message")] string Message,
    [property: JsonPropertyName("data")] T Data
);

Transaction Verification Models

namespace Tofali.Payments.Paystack.Models;

public sealed record VerifyTransactionResponseData(
    [property: JsonPropertyName("id")] long Id,
    [property: JsonPropertyName("domain")] string Domain,
    [property: JsonPropertyName("status")] string Status,
    [property: JsonPropertyName("reference")] string Reference,
    [property: JsonPropertyName("amount")] long AmountInKobo,
    [property: JsonPropertyName("gateway_response")] string GatewayResponse,
    [property: JsonPropertyName("paid_at")] DateTimeOffset? PaidAt,
    [property: JsonPropertyName("channel")] string Channel,
    [property: JsonPropertyName("currency")] string Currency
);

3. Typed HTTP Client Service

Create an interface and implementation using ASP.NET Core’s HttpClient.

namespace Tofali.Payments.Paystack;

using System.Net.Http.Json;
using Microsoft.Extensions.Logging;
using Tofali.Payments.Paystack.Models;

public interface IPaystackClient
{
    Task<PaystackApiResponse<InitializeTransactionResponseData>> InitializeTransactionAsync(
        InitializeTransactionRequest request,
        CancellationToken cancellationToken = default);

    Task<PaystackApiResponse<VerifyTransactionResponseData>> VerifyTransactionAsync(
        string reference,
        CancellationToken cancellationToken = default);
}

public sealed class PaystackClient(HttpClient httpClient, ILogger<PaystackClient> logger) : IPaystackClient
{
    public async Task<PaystackApiResponse<InitializeTransactionResponseData>> InitializeTransactionAsync(
        InitializeTransactionRequest request,
        CancellationToken cancellationToken = default)
    {
        logger.LogInformation("Initializing Paystack transaction reference {Reference}", request.Reference);

        var response = await httpClient.PostAsJsonAsync("transaction/initialize", request, cancellationToken);
        var result = await response.Content.ReadFromJsonAsync<PaystackApiResponse<InitializeTransactionResponseData>>(
            cancellationToken: cancellationToken);

        if (!response.IsSuccessStatusCode || result == null || !result.Status)
        {
            logger.LogError("Paystack initialization failed for reference {Reference}: {Message} (HTTP {StatusCode})", 
                request.Reference, result?.Message ?? "Unknown error", (int)response.StatusCode);
            throw new InvalidOperationException($"Paystack API error: {result?.Message ?? "No response"}");
        }

        return result;
    }

    public async Task<PaystackApiResponse<VerifyTransactionResponseData>> VerifyTransactionAsync(
        string reference,
        CancellationToken cancellationToken = default)
    {
        logger.LogInformation("Verifying Paystack transaction reference {Reference}", reference);

        var response = await httpClient.GetAsync($"transaction/verify/{Uri.EscapeDataString(reference)}", cancellationToken);
        var result = await response.Content.ReadFromJsonAsync<PaystackApiResponse<VerifyTransactionResponseData>>(
            cancellationToken: cancellationToken);

        if (!response.IsSuccessStatusCode || result == null || !result.Status)
        {
            logger.LogError("Paystack verification failed for reference {Reference}: {Message} (HTTP {StatusCode})", 
                reference, result?.Message ?? "Unknown error", (int)response.StatusCode);
            throw new InvalidOperationException($"Paystack API error: {result?.Message ?? "No response"}");
        }

        return result;
    }
}

4. Dependency Injection & Service Registration

Register PaystackClient in Program.cs with AddHttpClient and configure bearer authentication:

using Microsoft.Extensions.Options;
using Tofali.Payments.Paystack;

var builder = WebApplication.CreateBuilder(args);

// Bind Paystack configuration options
builder.Services.Configure<PaystackOptions>(
    builder.Configuration.GetSection(PaystackOptions.SectionName));

// Register Typed HttpClient with BaseAddress and Authorization header
builder.Services.AddHttpClient<IPaystackClient, PaystackClient>((serviceProvider, client) =>
{
    var options = serviceProvider.GetRequiredService<IOptions<PaystackOptions>>().Value;
    
    client.BaseAddress = new Uri(options.BaseUrl);
    client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {options.SecretKey}");
});

var app = builder.Build();

5. Security & Production Considerations

  1. Amount Representation: Always convert decimal values to integer kobo before constructing requests to avoid floating-point errors (5000.00m * 100 = 500000).
  2. Server-Side Verification: Never rely solely on front-end checkout success callbacks. Always verify transactions server-to-server or via webhooks.
  3. HTTP Resilience: Use Polly policies (e.g. AddStandardResilienceHandler()) in production to handle temporary network glitches gracefully.

6. 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: