dotnet-9.0aspnetcore-9.0paystack-api-v1Integrating 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 orsk_live_...for production.
[!IMPORTANT] Never embed your Paystack secret key (
sk_test_...orsk_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
- Amount Representation: Always convert decimal values to integer kobo before constructing requests to avoid floating-point errors (
5000.00m * 100=500000). - Server-Side Verification: Never rely solely on front-end checkout success callbacks. Always verify transactions server-to-server or via webhooks.
- HTTP Resilience: Use Polly policies (e.g.
AddStandardResilienceHandler()) in production to handle temporary network glitches gracefully.
6. Official Provenance
- Paystack API Reference: https://paystack.com/docs/api
- Paystack Fee Schedule: https://paystack.com/pricing
- Last Tested: August 22, 2026 UTC