paylode / paylode-php
Official PHP SDK for Paylode Services Limited — CBN Licensed PSSP
Requires
- php: >=7.4
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.0
README
Official PHP SDK for Paylode Services Limited — CBN Licensed Payment Solution Service Provider (PSSP).
Requirements
- PHP 7.4+
ext-curlandext-json(standard in most PHP installs)- A Paylode secret key (
sk_live_...orsk_test_...) — obtain from your merchant dashboard
Installation
composer require paylode/paylode-php
Zero external dependencies — uses PHP cURL extension only.
Quick start
use Paylode\Paylode; $client = new Paylode('sk_live_xxxxxxxxxxxxxxxxxxxx'); // 1. Initialize a payment — redirect your customer to authorization_url $txn = $client->transaction->initialize([ 'email' => 'customer@example.com', 'amount' => 500000, // ₦5,000 in kobo (1 kobo = 0.01 naira) 'callback_url' => 'https://yoursite.com/payment/complete', 'channels' => ['card', 'bank_transfer'], 'metadata' => ['order_id' => 'ORD-9812', 'customer_name' => 'Ada Obi'], ]); header('Location: ' . $txn['data']['authorization_url']); exit; // 2. Verify server-side when the customer returns — ALWAYS do this $result = $client->transaction->verify($_GET['reference']); if ($result['data']['status'] === 'success') { fulfillOrder($result['data']['metadata']['order_id']); }
API reference
new Paylode($secretKey, $sandbox = null)
| Parameter | Type | Description |
|---|---|---|
$secretKey |
string | Your sk_live_... or sk_test_... key (required) |
$sandbox |
bool|null | Force sandbox mode (auto-detected from key prefix) |
$client = new Paylode('sk_live_xxxxxxxxxxxxxxxxxxxx'); var_dump($client->sandbox); // bool(false) for live keys echo $client->getVersion(); // '1.0.0'
$client->transaction
->initialize(array $params) → array
Initialize a new payment. Redirect your customer to $result['data']['authorization_url'].
| Key | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Customer email address |
amount |
int | Yes | Amount in kobo (minimum ₦100 = 10000 kobo) |
reference |
string | No | Unique transaction reference (auto-generated if omitted) |
currency |
string | No | 'NGN' (default) |
callback_url |
string | No | URL to redirect customer after payment |
channels |
string[] | No | ['card', 'bank_transfer', 'ussd', 'direct_debit'] |
metadata |
array | No | Arbitrary key-value pairs — returned in webhooks |
$txn = $client->transaction->initialize([ 'email' => 'customer@example.com', 'amount' => 250000, // ₦2,500 'reference' => 'ORD-2026-00142', // optional, must be unique 'callback_url' => 'https://myshop.com/confirm', 'channels' => ['card', 'bank_transfer'], 'metadata' => ['order_id' => 'ORD-9812', 'plan' => 'premium'], ]); $redirectUrl = $txn['data']['authorization_url']; // redirect customer here $reference = $txn['data']['reference']; // store this
->verify(string $reference) → array
Verify a transaction. Always call this server-side before fulfilling any order.
$result = $client->transaction->verify('ORD-2026-00142'); if ($result['data']['status'] === 'success') { $amountPaid = $result['data']['amount']; // in kobo $fees = $result['data']['fees']; // platform fees $orderId = $result['data']['metadata']['order_id']; fulfillOrder($orderId); }
->list(...) → array
$transactions = $client->transaction->list( page: 1, perPage: 50, status: 'success', // 'success' | 'failed' | 'pending' fromDate: '2026-01-01', toDate: '2026-01-31' ); foreach ($transactions['data'] as $txn) { echo $txn['reference'] . ' — ₦' . ($txn['amount'] / 100) . PHP_EOL; }
->fetch(string $transactionId) → array
$txn = $client->transaction->fetch('txn_01JXYZ123456');
->refund(string $reference, ?int $amount = null, string $reason = '') → array
// Full refund $client->transaction->refund('ORD-2026-00142'); // Partial refund — ₦2,000 $client->transaction->refund('ORD-2026-00142', 200000, 'Item out of stock');
$client->customer
// Create $customer = $client->customer->create([ 'email' => 'ada@example.com', 'first_name' => 'Ada', 'last_name' => 'Obi', 'phone' => '+2348012345678', 'metadata' => ['plan' => 'gold'], ]); // Fetch by email or customer code $c = $client->customer->fetch('ada@example.com'); $c = $client->customer->fetch('CUS_abc123'); // List $customers = $client->customer->list(page: 1, perPage: 50); // Update $client->customer->update('CUS_abc123', ['phone' => '+2349011112222']);
$client->subaccount — for aggregators
Subaccounts represent merchants under an aggregator. Paylode uses them to automatically split settlement at the point of transaction.
// Register a merchant under your aggregator account $sub = $client->subaccount->create([ 'business_name' => 'Shoprite Nigeria', 'settlement_bank' => 'GTB', 'account_number' => '0123456789', 'percentage_charge' => 70, // merchant receives 70% of each transaction 'description' => 'Shoprite Lagos Island branch', ]); $subaccountCode = $sub['data']['subaccount_code']; // SUB_xxxx // Use the subaccount code in a split transaction $txn = $client->transaction->initialize([ 'email' => 'buyer@example.com', 'amount' => 100000, 'subaccount' => $subaccountCode, 'metadata' => ['order_id' => 'ORD-9812'], ]);
$client->subaccount->fetch('SUB_abc123'); $client->subaccount->list(page: 1, perPage: 50); $client->subaccount->update('SUB_abc123', ['percentage_charge' => 75]);
$client->settlement
// List settlements $settlements = $client->settlement->list( page: 1, perPage: 50, fromDate: '2026-01-01', toDate: '2026-01-31' ); // Fetch one by ID $s = $client->settlement->fetch('STL_01JX123456');
Paylode::verifyWebhook() — static
Verify a webhook signature from Paylode. Call at the top of every webhook handler before processing the event.
use Paylode\Paylode; // In your webhook handler $raw = file_get_contents('php://input'); $sig = $_SERVER['HTTP_X_PAYLODE_SIGNATURE'] ?? ''; if (!Paylode::verifyWebhook($raw, $sig, getenv('PAYLODE_WEBHOOK_SECRET'))) { http_response_code(401); exit; } $event = json_decode($raw, true); $type = $event['event']; // e.g. 'payment.success', 'refund.processed' switch ($type) { case 'payment.success': fulfillOrder($event['data']['metadata']['order_id']); break; case 'payment.failed': notifyCustomer($event['data']['customer']['email']); break; case 'refund.processed': updateOrderStatus($event['data']['reference'], 'refunded'); break; } http_response_code(200);
Static helpers
Paylode::generateRef('ORD'); // 'ORD-60A3F2-9C4E1A2B' Paylode::koboToNaira(500_000); // 5000.0 Paylode::nairaToKobo(5000); // 500000 $limits = Paylode::KYC_LIMITS; // array of tier limits
KYC tier limits
$limits = Paylode::KYC_LIMITS; $limits['tier_1']['single_txn']; // 5_000_000 kobo (₦50,000) $limits['tier_1']['daily']; // 30_000_000 kobo (₦300,000) $limits['tier_2']['single_txn']; // 100_000_000 kobo (₦1,000,000) $limits['tier_3']['single_txn']; // 500_000_000 kobo (₦5,000,000)
Error handling
use Paylode\Exceptions\PaylodeValidationException; use Paylode\Exceptions\PaylodeApiException; use Paylode\Exceptions\PaylodeAuthException; use Paylode\Exceptions\PaylodeException; try { $txn = $client->transaction->initialize([ 'email' => 'customer@example.com', 'amount' => 250000, ]); } catch (PaylodeValidationException $e) { // Invalid params — client-side, don't retry echo $e->getMessage(); // e.g. 'amount must be an integer in kobo...' echo $e->getField(); // e.g. 'amount' } catch (PaylodeAuthException $e) { // Invalid or expired API key echo 'Check your PAYLODE_SECRET_KEY'; } catch (PaylodeApiException $e) { // API returned an error echo $e->getMessage(); // Human-readable error echo $e->getErrorCode(); // e.g. 'DUPLICATE_REFERENCE' echo $e->getStatusCode(); // HTTP status: 400, 422, 500... print_r($e->getRaw()); // Full API response array } catch (PaylodeException $e) { // Network error or parse error echo 'Could not reach Paylode: ' . $e->getMessage(); }
Sandbox / test mode
// sk_test_ key auto-sets $client->sandbox = true $client = new Paylode('sk_test_xxxxxxxxxxxxxxxxxxxx'); var_dump($client->sandbox); // bool(true)
Test cards for the checkout page:
| Card number | Result |
|---|---|
4084 0841 1111 1111 |
Success |
4084 0841 1111 1112 |
Insufficient funds |
4084 0841 1111 1113 |
Declined |
Complete integration example (Laravel)
// routes/web.php Route::post('/checkout', [PaymentController::class, 'checkout']); Route::get('/confirm', [PaymentController::class, 'confirm']); Route::post('/webhook/paylode', [PaymentController::class, 'webhook'])->withoutMiddleware(VerifyCsrfToken::class); // app/Http/Controllers/PaymentController.php namespace App\Http\Controllers; use Illuminate\Http\Request; use Paylode\Paylode; use Paylode\Exceptions\PaylodeException; class PaymentController extends Controller { private Paylode $client; public function __construct() { $this->client = new Paylode(config('services.paylode.secret_key')); } public function checkout(Request $request) { try { $txn = $this->client->transaction->initialize([ 'email' => $request->email, 'amount' => $request->amount, 'callback_url' => route('payment.confirm'), 'metadata' => ['order_id' => $request->order_id], ]); return redirect($txn['data']['authorization_url']); } catch (PaylodeException $e) { return back()->withErrors(['payment' => $e->getMessage()]); } } public function confirm(Request $request) { $result = $this->client->transaction->verify($request->reference); if ($result['data']['status'] === 'success') { $this->fulfillOrder($result['data']['metadata']['order_id']); return redirect()->route('order.complete'); } return redirect()->route('order.failed'); } public function webhook(Request $request) { $raw = $request->getContent(); $sig = $request->header('X-Paylode-Signature', ''); if (!Paylode::verifyWebhook($raw, $sig, config('services.paylode.webhook_secret'))) { abort(401); } $event = json_decode($raw, true); match ($event['event']) { 'payment.success' => $this->fulfillOrder($event['data']['metadata']['order_id']), 'refund.processed' => $this->markRefunded($event['data']['reference']), default => null, }; return response('OK', 200); } }
Running tests
# Built-in test runner php tests/PaylodeTest.php # With PHPUnit composer require --dev phpunit/phpunit vendor/bin/phpunit tests/
Paylode Services Limited · CBN/PAY/2024/001847 · docs.paylodeservices.com