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.
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>();
// 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.
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_ });
This guide is part of the Paystack integration guides by language and framework series.
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