McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Accept Payments with Paystack in ASP.NET Core
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Accept Payments with Paystack in ASP.NET Core

To accept Paystack payments in ASP.NET Core, inject IHttpClientFactory into a PaymentController. Create a POST action that calls the Paystack transaction/initialize endpoint with your secret key from IConfiguration. Return the authorization URL to the frontend. In the callback GET action, verify the transaction with a second API call before granting access.

In this article

  1. 1. Setup: Configuration and HttpClient
  2. 2. Payment Controller
  3. 3. Callback Handler
  4. 4. Minimal API Alternative
  5. 5. Learn More

Setup: Configuration and HttpClient

Add your Paystack credentials to appsettings.json:

{
  "Paystack": {
    "SecretKey": "sk_test_xxxxxxxxxxxx",
    "PublicKey": "pk_test_xxxxxxxxxxxx",
    "BaseUrl": "https://api.paystack.co"
  }
}

Register a named HttpClient in Program.cs:

// Program.cs
builder.Services.AddHttpClient("Paystack", (provider, client) =>
{
    var config = provider.GetRequiredService<IConfiguration>();
    var secretKey = config["Paystack:SecretKey"];
    client.BaseAddress = new Uri("https://api.paystack.co/");
    client.DefaultRequestHeaders.Authorization =
        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", secretKey);
    client.DefaultRequestHeaders.Accept.Add(
        new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
});

Payment Controller

// Controllers/PaymentController.cs
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class PaymentController : ControllerBase
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IConfiguration _config;

    public PaymentController(IHttpClientFactory httpClientFactory, IConfiguration config)
    {
        _httpClientFactory = httpClientFactory;
        _config = config;
    }

    [HttpPost("initialize")]
    public async Task<IActionResult> Initialize([FromBody] InitializeRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.Email))
            return BadRequest(new { error = "Email is required" });

        var reference = "dotnet_" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() +
                        "_" + Guid.NewGuid().ToString("N")[..6];

        var payload = new
        {
            email = req.Email,
            amount = (long)(req.Amount * 100),  // Convert to kobo
            reference = reference,
            callback_url = _config["Paystack:CallbackUrl"]
        };

        var client = _httpClientFactory.CreateClient("Paystack");
        var json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await client.PostAsync("transaction/initialize", content);
        var responseBody = await response.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<JsonElement>(responseBody);

        if (!result.GetProperty("status").GetBoolean())
        {
            return BadRequest(new { error = result.GetProperty("message").GetString() });
        }

        var data = result.GetProperty("data");
        return Ok(new
        {
            authorization_url = data.GetProperty("authorization_url").GetString(),
            access_code = data.GetProperty("access_code").GetString(),
            reference = data.GetProperty("reference").GetString()
        });
    }
}

public record InitializeRequest(string Email, decimal Amount);

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Callback Handler

// Controllers/PaymentController.cs (continued)

[HttpGet("callback")]
public async Task<IActionResult> Callback([FromQuery] string reference, [FromQuery] string trxref)
{
    var ref_ = reference ?? trxref;

    if (string.IsNullOrWhiteSpace(ref_))
        return BadRequest("Reference missing");

    var client = _httpClientFactory.CreateClient("Paystack");
    var response = await client.GetAsync($"transaction/verify/{Uri.EscapeDataString(ref_)}");
    var responseBody = await response.Content.ReadAsStringAsync();
    var result = JsonSerializer.Deserialize<JsonElement>(responseBody);

    if (!result.GetProperty("status").GetBoolean())
        return BadRequest("Verification failed");

    var data = result.GetProperty("data");
    var txStatus = data.GetProperty("status").GetString();

    if (txStatus != "success")
        return BadRequest(new { status = txStatus });

    var amount = data.GetProperty("amount").GetInt64();
    var currency = data.GetProperty("currency").GetString();

    // Grant access, fulfill order, update database
    return Ok(new { verified = true, amount = amount / 100m, currency, reference = ref_ });
}

Minimal API Alternative

If you prefer Minimal APIs in .NET 6+:

// Program.cs additions
app.MapPost("/api/pay", async (InitializeRequest req, IHttpClientFactory factory, IConfiguration config) =>
{
    var reference = "dotnet_" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
    var payload = new { email = req.Email, amount = (long)(req.Amount * 100), reference };

    var client = factory.CreateClient("Paystack");
    var response = await client.PostAsJsonAsync("transaction/initialize", payload);
    var result = await response.Content.ReadFromJsonAsync<JsonElement>();

    if (!result.GetProperty("status").GetBoolean())
        return Results.BadRequest(result.GetProperty("message").GetString());

    var data = result.GetProperty("data");
    return Results.Ok(new
    {
        authorization_url = data.GetProperty("authorization_url").GetString(),
        reference = data.GetProperty("reference").GetString()
    });
});

app.MapGet("/api/pay/callback", async (string reference, IHttpClientFactory factory) =>
{
    var client = factory.CreateClient("Paystack");
    var result = await client.GetFromJsonAsync<JsonElement>($"transaction/verify/{reference}");

    if (result.GetProperty("data").GetProperty("status").GetString() != "success")
        return Results.BadRequest("Payment not verified");

    return Results.Ok(new { verified = true, reference });
});

Learn More

This guide is part of the Paystack integration guides by language and framework series.

Next: Handle Paystack Webhooks in ASP.NET Core

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Store your Paystack secret key in appsettings.json under a Paystack section, and load it via IConfiguration.
  • ✓Register a named HttpClient for Paystack in Program.cs with the base address and Authorization header set once.
  • ✓Amounts are in kobo (NGN), pesewas (GHS), or cents (ZAR/KES/USD). Multiply display price by 100.
  • ✓Always verify the transaction server-side in the callback action. The redirect alone is not proof of payment.
  • ✓Use IHttpClientFactory rather than new HttpClient() to avoid socket exhaustion in production.
  • ✓Anti-forgery tokens apply to form POST actions but not to the Paystack callback GET redirect.

Frequently Asked Questions

Should I use IHttpClientFactory or new HttpClient() for Paystack in ASP.NET Core?
Always use IHttpClientFactory. Creating HttpClient with new HttpClient() in controllers can cause socket exhaustion because sockets are not released immediately on disposal. IHttpClientFactory manages a pool of handlers and recycles connections correctly.
Where should I store the Paystack secret key in ASP.NET Core?
In development, use appsettings.Development.json or user secrets (dotnet user-secrets). In production, use environment variables, Azure Key Vault, or your hosting platform secret management. Never commit the key to source control.
How do I convert Nigerian Naira to kobo in C#?
Multiply the amount in Naira by 100. Use decimal for the conversion to avoid floating point issues: (long)(amount * 100). Paystack expects an integer in the smallest currency unit (kobo for NGN, pesewas for GHS, cents for others).
Can I use Razor Pages instead of Controllers for Paystack?
Yes. Inject IHttpClientFactory in your PageModel constructor and call the Paystack API in OnPostAsync. The verification logic is the same. The main difference is you handle the callback URL in a separate GET handler or use a dedicated page.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Integration Guides by Language and Framework

A complete directory of Paystack integration guides organized by programming language and framework. Each guide covers accepting payments, verifying transactions, handling webhooks, and building subscriptions in your specific stack.

Read more

Verify Paystack Payments in ASP.NET Core

Verify Paystack payments in ASP.NET Core by calling the transaction verify endpoint with your secret key. This guide covers the verify action, parsing the response, validating the amount, and building a reusable PaystackService.

Read more

Handle Paystack Webhooks in ASP.NET Core

A complete guide to receiving and verifying Paystack webhooks in an ASP.NET Core application. Covers reading the raw body before JSON parsing, computing the HMAC SHA512 signature in C#, routing events, and preventing double-processing.

Read more

Build Paystack Subscriptions in ASP.NET Core

Implement Paystack recurring billing in ASP.NET Core. This guide covers creating plans, subscribing customers through transaction initialization, handling renewal webhooks with C#, and cancelling subscriptions.

Read more

Why You Must Never Call the Paystack API From Your Frontend

Putting your Paystack secret key in frontend JavaScript is the most dangerous mistake in payment integration. This guide shows exactly what goes wrong, why CORS will also fight you, and the correct pattern: frontend talks to your server, your server talks to Paystack.

Read more

Paystack Transaction References: Design Patterns That Prevent Chaos

Your transaction reference is the single string that ties a Paystack payment to your internal records. A sloppy reference scheme leads to duplicate charges, mystery transactions, and 2 AM debugging sessions. This guide covers UUID, sequential, and composite patterns with code for each.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.