To verify a Paystack payment in Laravel, use Http::withToken() to GET the verify endpoint. Check data.data.status is "success", the amount matches your Order model, and the currency is correct. Use Order::where("reference", $ref)->where("paid", false)->update() for idempotent fulfillment.
// app/Services/PaystackService.php
namespace App\Services;
use App\Models\Order;
use Illuminate\Support\Facades\Http;
class PaystackService
{
private string $secretKey;
public function __construct()
{
$this->secretKey = config('services.paystack.secret_key');
}
public function verify(string $reference): array
{
$response = Http::withToken($this->secretKey)
->get('https://api.paystack.co/transaction/verify/' . $reference);
return $response->json();
}
public function verifyAndFulfill(string $reference): array
{
$order = Order::where('reference', $reference)->first();
if (!$order) {
return ['success' => false, 'error' => 'Order not found'];
}
if ($order->paid) {
return ['success' => true, 'already_fulfilled' => true];
}
$result = $this->verify($reference);
if (!($result['status'] ?? false) || $result['data']['status'] !== 'success') {
return ['success' => false, 'error' => 'Payment not successful'];
}
if ($result['data']['amount'] !== $order->amount) {
return ['success' => false, 'error' => 'Amount mismatch'];
}
if ($result['data']['currency'] !== $order->currency) {
return ['success' => false, 'error' => 'Currency mismatch'];
}
$updated = Order::where('reference', $reference)
->where('paid', false)
->update(['paid' => true, 'paid_at' => now()]);
return [
'success' => true,
'already_fulfilled' => $updated === 0,
'amount' => $order->amount / 100,
'currency' => $order->currency,
];
}
}
// In PaymentController
public function callback(Request $request, PaystackService $paystack)
{
$reference = $request->query('reference') ?? $request->query('trxref');
if (!$reference) {
return redirect('/')->with('error', 'Missing reference');
}
$result = $paystack->verifyAndFulfill($reference);
if ($result['success']) {
return view('payment.success', ['reference' => $reference]);
}
return view('payment.failed', ['error' => $result['error']]);
}
public function verify(Request $request, PaystackService $paystack)
{
$reference = $request->query('reference');
if (!$reference) {
return response()->json(['error' => 'Missing reference'], 400);
}
$result = $paystack->verifyAndFulfill($reference);
return response()->json($result, $result['success'] ? 200 : 400);
}
Laravel automatically injects the PaystackService through constructor or method injection.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
public function verifyWithRetry(string $reference, int $maxRetries = 3): ?array
{
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
try {
$response = Http::withToken($this->secretKey)
->timeout(10)
->get('https://api.paystack.co/transaction/verify/' . $reference);
if ($response->successful()) {
return $response->json();
}
} catch (\Exception $e) {
// Log and retry
}
if ($attempt < $maxRetries - 1) {
sleep(pow(2, $attempt));
}
}
return null;
}
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