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. Verify Paystack Payments in ASP.NET Core
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Verify Paystack Payments in ASP.NET Core

Call GET https://api.paystack.co/transaction/verify/:reference with your secret key in the Authorization header. In ASP.NET Core, use the registered "Paystack" named HttpClient. Check that data.status equals "success" and that data.amount divided by 100 matches the expected amount in your database. Never trust the amount from the frontend.

In this article

  1. 1. Build a PaystackService
  2. 2. Use in Controller
  3. 3. Amount Validation
  4. 4. Learn More

Build a PaystackService

Extract verification into a service class to keep controllers clean:

// Services/PaystackService.cs
using System.Text.Json;

public class PaystackVerifyResult
{
    public bool Success { get; init; }
    public string? Status { get; init; }
    public long AmountInMinorUnit { get; init; }
    public string? Currency { get; init; }
    public string? Reference { get; init; }
    public string? GatewayResponse { get; init; }
    public string? CustomerEmail { get; init; }
    public string? ErrorMessage { get; init; }
}

public class PaystackService
{
    private readonly HttpClient _client;

    public PaystackService(IHttpClientFactory factory)
    {
        _client = factory.CreateClient("Paystack");
    }

    public async Task<PaystackVerifyResult> VerifyAsync(string reference)
    {
        try
        {
            var response = await _client.GetAsync(
                $"transaction/verify/{Uri.EscapeDataString(reference)}");
            var body = await response.Content.ReadAsStringAsync();
            var json = JsonSerializer.Deserialize<JsonElement>(body);

            if (!json.GetProperty("status").GetBoolean())
            {
                return new PaystackVerifyResult
                {
                    Success = false,
                    ErrorMessage = json.GetProperty("message").GetString()
                };
            }

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

            return new PaystackVerifyResult
            {
                Success = txStatus == "success",
                Status = txStatus,
                AmountInMinorUnit = data.GetProperty("amount").GetInt64(),
                Currency = data.GetProperty("currency").GetString(),
                Reference = data.GetProperty("reference").GetString(),
                GatewayResponse = data.GetProperty("gateway_response").GetString(),
                CustomerEmail = customer.GetProperty("email").GetString()
            };
        }
        catch (Exception ex)
        {
            return new PaystackVerifyResult { Success = false, ErrorMessage = ex.Message };
        }
    }
}

Register the service in Program.cs:

builder.Services.AddScoped<PaystackService>();

Use in Controller

// Controllers/PaymentController.cs
[HttpGet("callback")]
public async Task<IActionResult> Callback(
    [FromQuery] string reference,
    [FromQuery] string trxref,
    [FromServices] PaystackService paystack)
{
    var ref_ = reference ?? trxref;
    if (string.IsNullOrWhiteSpace(ref_))
        return BadRequest("Reference missing");

    var result = await paystack.VerifyAsync(ref_);

    if (!result.Success)
    {
        return BadRequest(new
        {
            error = result.ErrorMessage ?? "Payment not verified",
            gateway_response = result.GatewayResponse
        });
    }

    // IMPORTANT: validate the amount against your database
    // var order = await _db.Orders.FirstOrDefaultAsync(o => o.Reference == ref_);
    // if (order == null) return NotFound();
    // if (order.AmountInKobo != result.AmountInMinorUnit) return BadRequest("Amount mismatch");

    return Ok(new
    {
        verified = true,
        amount = result.AmountInMinorUnit / 100m,
        currency = result.Currency,
        reference = result.Reference,
        customer = result.CustomerEmail
    });
}

Get weekly developer tips

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

No spam. Unsubscribe anytime.

Amount Validation

Always validate the amount returned by Paystack against what you stored in your database before the checkout:

// Example with EF Core
var order = await _dbContext.Orders
    .FirstOrDefaultAsync(o => o.Reference == ref_);

if (order == null)
    return NotFound(new { error = "Order not found" });

if (order.IsVerified)
    return Ok(new { verified = true, message = "Already verified" }); // idempotent

var result = await paystack.VerifyAsync(ref_);

if (!result.Success)
    return BadRequest(new { error = "Payment failed", status = result.Status });

// Validate amount: order stored amount in NGN, Paystack returns in kobo
var expectedKobo = (long)(order.Amount * 100);
if (result.AmountInMinorUnit != expectedKobo)
{
    // Log this as a potential attack
    return BadRequest(new { error = "Amount mismatch" });
}

order.IsVerified = true;
order.PaidAt = DateTime.UtcNow;
await _dbContext.SaveChangesAsync();

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

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

  • ✓Always verify server-side. The redirect callback and popup success event are not proof of payment.
  • ✓Check both status ("success") and amount (must match your database record) in the verify response.
  • ✓Extract the verification logic into a PaystackService class so controllers stay thin.
  • ✓Handle failed, abandoned, and pending statuses explicitly. Do not treat anything other than "success" as paid.
  • ✓The gateway_response field in the verify response tells you exactly what the bank said about the transaction.
  • ✓Use a database flag (e.g., IsVerified) to prevent double-fulfillment if the callback fires twice.

Frequently Asked Questions

What does "Transaction not found" mean from Paystack verify?
It means the reference you sent does not exist on Paystack, or you are using a test reference with live keys (or vice versa). Double-check that the reference matches what was returned during initialization and that you are using the same key environment.
Can I verify a transaction more than once?
Yes, the verify endpoint is idempotent and can be called multiple times for the same reference. Track verification in your database with an IsVerified flag to avoid processing the payment twice on your side.
What is the difference between status and gateway_response in the verify response?
status is the Paystack-level outcome: "success", "failed", "abandoned", or "pending". gateway_response is the human-readable message from the card network or bank, like "Approved" or "Insufficient Funds". Use status for logic and gateway_response for user-facing messages.
How do I handle the case where a customer pays but the amount is wrong?
Paystack initializes transactions with a fixed amount. A mismatch should not occur in normal usage. If it does, it likely indicates a bug in your initialization code or a replay attack. Log it, do not fulfill, and investigate. Never accept an amount less than expected.

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

Accept Payments with Paystack in ASP.NET Core

Wire Paystack payments into an ASP.NET Core web application. This guide covers configuring HttpClient for the Paystack API, creating payment controllers, initializing transactions, handling redirect callbacks, and verifying payments server-side.

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

Transaction Not Found on Paystack Verify

Paystack returns "Transaction not found" when the reference you pass to the verify endpoint does not match any transaction in the current environment. The causes range from wrong reference strings to test/live key confusion to URL encoding problems. This guide covers every cause with specific fixes.

Read more

Paystack Gateway Responses: Reading What the Bank Told You

When a payment fails on Paystack, the gateway_response field tells you exactly what the bank said. Approved. Declined. Insufficient Funds. Timeout. This guide decodes the most common gateway responses, explains what they mean for your integration, and shows you how to turn cryptic bank codes into messages your customers can act on.

Read more

Idempotency in Paystack Payment Flows

Duplicate charges are the nightmare scenario for any payment integration. A customer clicks pay twice, a network timeout triggers a retry, or a webhook fires before the redirect callback. Idempotency is the engineering discipline that makes sure the same payment operation produces the same result no matter how many times it runs.

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.