Skip to content

Overview

# ๐Ÿ“š API Reference **alphabalex/payment-made-easy** *Complete reference for every interface, method, and service across all 16 supported payment gateways.* [![Laravel](https://img.shields.io/badge/Laravel-8.x--13.x-red?style=flat-square&logo=laravel)](https://laravel.com) [![PHP](https://img.shields.io/badge/PHP-8.1+-purple?style=flat-square&logo=php)](https://php.net) [![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)](LICENSE) ---

๐Ÿ“– Table of Contents

๐Ÿ—๏ธ Shared Foundations - [Core Interface](#-core-interface) - [Idempotency & Retry](#-idempotency--retry) - [Gateway Capabilities](#-gateway-capabilities) - [Exception Classes](#-exception-classes)
๐ŸŒ Payment Providers - [๐Ÿ’ฐ Paystack](#-paystack) โ€” Full API coverage (11 interfaces) - [๐ŸŒŠ Flutterwave](#-flutterwave) โ€” Full API coverage (9 interfaces) - [๐Ÿ”’ Seerbit](#-seerbit) โ€” Full API coverage (5 interfaces) - [โšก Stripe](#-stripe) โ€” Core + Subscriptions + Payment Links - [๐Ÿฆ Other Gateways](#-other-gateways) โ€” Monnify, Squad, OPay, Remita, Budpay, Mono, Interswitch, PayPal, M-Pesa, MTN MoMo, Razorpay, Paddle
๐Ÿ› ๏ธ Services & Utilities - [๐Ÿ“Š Balance Aggregator](#-balance-aggregation-service) - [๐Ÿ“ Payment Recorder](#-payment-recorder)

๐Ÿ—๏ธ Shared Foundations

These interfaces and features are available across multiple gateways. Each driver implements only the interfaces it supports.

๐ŸŽฏ Core Interface

Namespace: Alphabalex\PaymentMadeEasy\Contracts\PaymentDriverInterface Available on: All 16 gateways

Every driver implements this interface. These methods provide a consistent API regardless of the underlying gateway.

initializePayment(array $data): array

Initialize a payment and get a checkout URL or authorization details.

Parameter Type Required Description
email string โœ… Customer email
amount float โœ… Amount in major currency units (e.g. 5000.00 for โ‚ฆ5,000)
reference string โŒ Unique reference โ€” auto-generated if omitted
callback_url string โŒ Redirect URL after payment
currency string โŒ ISO 4217 currency code โ€” defaults to config
metadata array โŒ Custom key/value pairs
channels array โŒ Payment channels to show (gateway-specific)
split_code string โŒ Transaction split code (Paystack)
subaccount string โŒ Subaccount code (Paystack)
use Alphabalex\PaymentMadeEasy\Facades\Payment;

$response = Payment::initializePayment([
    'email'        => 'customer@example.com',
    'amount'       => 5000.00,
    'reference'    => 'ORDER_123',
    'callback_url' => 'https://yoursite.com/callback',
    'metadata'     => ['order_id' => 123],
]);

// $response['data']['authorization_url'] โ†’ redirect customer here

verifyPayment(string $reference): array

$response = Payment::driver('paystack')->verifyPayment('ORDER_123');
// $response['data']['status'] === 'success'

getPayment(string $reference): array

Get payment details by reference.

refundPayment(string $reference, ?float $amount = null): array

Refund a payment โ€” null for full refund, amount for partial.

Payment::driver('paystack')->refundPayment('ORDER_123', 2500.00); // partial
Payment::driver('paystack')->refundPayment('ORDER_123');           // full

getTransactions(array $params = []): array

$transactions = Payment::driver('paystack')->getTransactions([
    'per_page' => 50,
    'page'     => 1,
    'status'   => 'success',
    'from'     => '2025-01-01',
    'to'       => '2025-12-31',
]);

pay(array $data, ?callable $onPaid = null): array

Convenience method available on all 15 drivers. Handles multi-step payment flows behind a single call.

Parameter Type Description
data array Payment data (same as initializePayment plus driver-specific extras)
onPaid callable? Optional callback โ€” receives raw gateway response on success, can return OTP string (Interswitch)

Return structure:

Field Type Description
response array Raw gateway response
success bool Payment confirmed successful
status string completed ยท redirect ยท sdk ยท processing ยท otp_required ยท 3ds_required ยท failed
reference string Transaction reference
auth_url / link / checkout_url string? Checkout URL (redirect-type drivers)
client_secret / order_id string? Client secret for JS SDK (Stripe, Razorpay)
checkout_request_id / reference_id string? Async polling ID (Interswitch, MPesa, MTN MoMo)
// Hosted checkout
$result = Payment::driver('paystack')->pay([
    'email'  => 'customer@example.com',
    'amount' => 5000.00,
]);
// Redirect to $result['auth_url']

// Card-on-file charge
$result = Payment::driver('paystack')->pay([
    'email'              => 'customer@example.com',
    'amount'             => 5000.00,
    'authorization_code' => 'AUTH_xxxxx',
], function (array $response) {
    // called on success
});

// Auto-verify after redirect callback
$result = Payment::driver('paystack')->pay([
    'email'         => 'customer@example.com',
    'amount'        => 5000.00,
    '_auto_verify'  => true,
    'reference'     => 'ORDER_123',
]);

// Interswitch OTP flow
$result = Payment::driver('interswitch')->pay(
    ['email' => 'customer@example.com', 'amount' => 5000.00],
    fn (array $initResponse) => $otpFromUser
);
// $result['status'] === 'otp_required' | 'completed' | 'failed' | '3ds_required'

Status values by driver:

Status Drivers
completed All (after successful verify or direct charge)
redirect Paystack, Flutterwave, Seerbit, Squad, OPay, Budpay, Remita, PayPal, Paddle
sdk Stripe, Razorpay
processing MPesa, MTN MoMo
otp_required Interswitch (when responseCode is T0)
3ds_required Interswitch, Paystack, Flutterwave (Visa 3D Secure)
failed All (on error or declined)

๐Ÿ” Idempotency & Retry

Built into AbstractPaymentDriver::makeRequest() โ€” applies to all gateways automatically.

Automatic Idempotency Keys

Every mutating request (POST, PUT, PATCH) automatically includes an Idempotency-Key header to prevent duplicate charges.

Priority resolution:

Priority Source Example
1 Explicit idempotency_key option 'idempotency_key' => 'payout_001'
2 Header already set headers: ['Idempotency-Key' => 'custom']
3 Auto-generated UUID idem_a1b2c3d4-e5f6-...
// Automatic โ€” gets a generated key
Payment::driver('paystack')->initializePayment([...]);

// Custom key โ€” for retry-safe operations
Payment::driver('paystack')->transfer([...], 'idempotency_key' => 'payout_001');

Exponential Backoff Retry

What's Retried What's NOT Retried
5xx server errors (500, 502, 503, 504) 4xx client errors (400, 401, 403, 404)
429 Too Many Requests POST/PUT/PATCH without an idempotency key
Connection errors (timeouts, DNS failures)

Backoff formula: delay = min(base ร— 2^(attempt-1), max) ร— random(0, 1)

// config/payment-gateways.php
'gateways' => [
    'paystack' => [
        // ...
        'retry_attempts'   => 3,    // default: 3 (0 = disabled)
        'retry_base_delay' => 500,  // default: 500ms
        'retry_max_delay'  => 5000, // default: 5000ms (5s)
    ],
],
// Per-call opt-out
Payment::driver('paystack')->transfer([...], 'retry' => false);

๐Ÿ”ง Gateway Capabilities

Check what a gateway supports at runtime:

use Alphabalex\PaymentMadeEasy\GatewayCapabilities;
use Alphabalex\PaymentMadeEasy\Contracts\BalanceDriverInterface;

// Check if a specific gateway supports an interface
$supported = GatewayCapabilities::driverImplements('paystack', BalanceDriverInterface::class); // true

// List all gateways that support an interface
$gateways = GatewayCapabilities::gatewaysImplementing(BalanceDriverInterface::class);
// ['paystack', 'flutterwave']

// Get all capabilities for a gateway
$capabilities = GatewayCapabilities::optionalCapabilities('paystack');
Interface Description
PaymentDriverInterface Core payment methods (all gateways)
SubscriptionDriverInterface Plans and subscriptions
DisbursementDriverInterface Transfers and payouts
VirtualAccountDriverInterface Dedicated virtual accounts
PaymentLinkDriverInterface Shareable payment links
CustomerDriverInterface Customer CRUD and management
DisputeDriverInterface Disputes and chargebacks
BalanceDriverInterface Account balance queries
SettlementDriverInterface Settlement history
SubaccountDriverInterface Subaccount management (Paystack)
SplitDriverInterface Transaction splits (Paystack)
ChargeAuthorizationDriverInterface Charge stored authorizations

โš ๏ธ Exception Classes

Exception Used For
PaymentException Base exception โ€” all payment errors extend this
SubscriptionException Subscription/plan operation failures
DisbursementException Transfer/disbursement failures
CustomerException Customer management failures
DisputeException Dispute/chargeback failures
BalanceException Balance query failures
SettlementException Settlement query failures
WebhookException Webhook signature/payload validation failures

All exceptions extend PaymentException โ†’ \RuntimeException.


# ๐ŸŒ Payment Providers

๐Ÿ’ฐ Paystack

Nigeria ยท Ghana ยท Kenya ยท South Africa ยท Cรดte d'Ivoire ยท US

Base URL: https://api.paystack.co Interfaces: 12 total โ€” every available interface

โœ… Feature Matrix

Feature Status Key Methods
๐Ÿ’ณ One-Time Payments โœ… initializePayment, verifyPayment, getTransactions
๐Ÿ”„ Subscriptions โœ… createPlan, createSubscription, pauseSubscription, cancelSubscription
๐Ÿ‘ค Customers โœ… createCustomer, validateCustomer, setCustomerRiskAction
๐Ÿ”‘ Charge Authorization โœ… chargeAuthorization, listAuthorizations
๐Ÿ“Š Transaction Details โœ… getTransactionTimeline, getTransactionTotals, exportTransactions
๐Ÿฆ Disbursements โœ… transfer, bulkTransfer, finalizeTransfer, OTP management
๐Ÿง Virtual Accounts โœ… createVirtualAccount, listVirtualAccounts
๐Ÿ”— Payment Links โœ… createPaymentLink, sendPaymentRequest
โš–๏ธ Disputes โœ… listDisputes, addDisputeEvidence, getDisputeUploadUrl
๐Ÿ’ฐ Balance โœ… getBalance, getLedger
๐Ÿ“… Settlements โœ… listSettlements, getSettlementTransactions
๐Ÿข Subaccounts โœ… createSubaccount, updateSubaccount
โœ‚๏ธ Transaction Splits โœ… createSplit, addSubaccountToSplit
๐Ÿ’ธ Refunds โœ… refundPayment, listRefunds, retryRefund

๐Ÿ’ณ Core Payments

$driver = Payment::driver('paystack');

$response = $driver->initializePayment([
    'email'        => 'customer@example.com',
    'amount'       => 5000.00,
    'reference'    => 'ORDER_123',
    'callback_url' => 'https://yoursite.com/callback',
    'metadata'     => ['order_id' => '123'],
    'channels'     => ['card', 'bank', 'ussd'],
]);
// Redirect to $response['data']['authorization_url']

$verification = $driver->verifyPayment('ORDER_123');
$payment = $driver->getPayment('ORDER_123');

$txns = $driver->getTransactions([
    'per_page' => 50,
    'status'   => 'success',
]);

๐Ÿ“Š Transaction Details

$timeline = $driver->getTransactionTimeline('12345');

$totals = $driver->getTransactionTotals([
    'from' => '2025-01-01',
    'to'   => '2025-12-31',
]);

$export = $driver->exportTransactions([
    'from' => '2025-01-01',
    'to'   => '2025-12-31',
]);

๐Ÿ”„ Subscriptions & Plans

// Plans
$plan = $driver->createPlan([
    'name'        => 'Pro Monthly',
    'amount'      => 5000.00,
    'interval'    => 'monthly',
    'currency'    => 'NGN',
    'description' => 'Full access',
    'trial_days'  => 7,
]);
$planCode = $plan['data']['plan_code'];

$driver->getPlan($planCode);
$driver->updatePlan($planCode, ['name' => 'Pro Monthly (Updated)']);
$driver->listPlans(['per_page' => 20]);
$driver->deletePlan($planCode);

// Subscriptions
$sub = $driver->createSubscription([
    'customer'   => 'customer@example.com',
    'plan'       => $planCode,
    'start_date' => '2025-06-01T00:00:00.000Z',
]);
$subCode = $sub['data']['subscription_code'];

$driver->getSubscription($subCode);
$driver->listSubscriptions(['plan' => $planCode]);
$driver->listCustomerSubscriptions('customer@example.com');
$driver->pauseSubscription($subCode);
$driver->resumeSubscription($subCode);
$driver->cancelSubscription($subCode);

๐Ÿ‘ค Customer Management

$customer = $driver->createCustomer([
    'email'      => 'customer@example.com',
    'first_name' => 'John',
    'last_name'  => 'Doe',
    'phone'      => '+2348012345678',
]);
$code = $customer['data']['customer_code'];

$customer = $driver->getCustomer('CUS_xxxxx');
$customers = $driver->listCustomers(['per_page' => 50]);
$driver->updateCustomer($code, ['first_name' => 'Jane']);

// Validate identity (bank account)
$driver->validateCustomer($code, [
    'country'        => 'NG',
    'type'           => 'bank_account',
    'account_number' => '0123456789',
    'bvn'            => '20012345677',
    'bank_code'      => '058',
    'first_name'     => 'John',
    'last_name'      => 'Doe',
]);

// Risk management
$driver->setCustomerRiskAction($code, 'allow');   // whitelist
$driver->setCustomerRiskAction($code, 'deny');    // blacklist
$driver->setCustomerRiskAction($code, 'default'); // reset

๐Ÿ”‘ Charge Authorization (Recurring Payments)

$response = $driver->chargeAuthorization([
    'email'              => 'customer@example.com',
    'amount'             => 5000.00,
    'authorization_code' => 'AUTH_xxxxx',
    'reference'          => 'RECURRING_001',
    'currency'           => 'NGN',
]);

$auth = $driver->getAuthorization('AUTH_xxxxx');
$auths = $driver->listAuthorizations(['per_page' => 50]);
$driver->deactivateAuthorization('AUTH_xxxxx');

๐Ÿฆ Disbursements & Transfers

$account = $driver->resolveAccountNumber([
    'account_number' => '0123456789',
    'bank_code'      => '044',
]);

$recipient = $driver->createTransferRecipient([
    'name'           => 'Jane Doe',
    'account_number' => '0123456789',
    'bank_code'      => '044',
    'currency'       => 'NGN',
]);
$rCode = $recipient['data']['recipient_code'];

$transfer = $driver->transfer([
    'amount'    => 10000.00,
    'recipient' => $rCode,
    'reason'    => 'Salary payout',
    'reference' => 'PAYOUT_001',
]);

$driver->bulkTransfer([
    ['amount' => 5000.00, 'recipient' => 'RCP_abc', 'reference' => 'B1'],
    ['amount' => 3000.00, 'recipient' => 'RCP_def', 'reference' => 'B2'],
]);

$driver->verifyTransfer('PAYOUT_001');
$driver->getTransfer('TRF_xxxxx');
$driver->listTransfers(['per_page' => 50]);

// OTP management
$driver->finalizeTransfer('TRF_xxxxx', '123456');
$driver->disableTransferOtp(['code' => '123456']);
$driver->resendTransferOtp('TRF_xxxxx');

// Banks
$banks = $driver->listBanks(['country' => 'nigeria']);

๐Ÿง Virtual Accounts

$va = $driver->createVirtualAccount([
    'email'          => 'customer@example.com',
    'name'           => 'Jane Doe',
    'bvn'            => '12345678901',
    'preferred_bank' => 'wema-bank',
]);
$accountNumber = $va['data']['account_number'];

$driver->getVirtualAccount($va['data']['id']);
$driver->listVirtualAccounts(['active' => true]);
$driver->deactivateVirtualAccount($va['data']['id']);

$link = $driver->createPaymentLink([
    'name'        => 'Product Launch',
    'amount'      => 2500.00,
    'description' => 'Early-bird ticket',
    'currency'    => 'NGN',
]);
$url = $link['data']['link'];

$driver->getPaymentLink($link['data']['id']);
$driver->listPaymentLinks();
$driver->updatePaymentLink($link['data']['id'], ['amount' => 3000.00]);
$driver->disablePaymentLink($link['data']['id']);

// Payment request extensions
$driver->sendPaymentRequest('PRQ_xxxxx', 'email');
$pdf = $driver->getPaymentRequestPdf('PRQ_xxxxx');

โš–๏ธ Disputes

$disputes = $driver->listDisputes(['status' => 'pending']);
$dispute = $driver->getDispute('2867');
$driver->updateDispute('2867', ['refund_amount' => 1002]);

$driver->addDisputeEvidence('2867', [
    'customer_email'  => 'customer@example.com',
    'customer_name'   => 'John Doe',
    'customer_phone'  => '+2348012345678',
    'service_details' => 'Service delivered on 2025-01-15',
]);

$upload = $driver->getDisputeUploadUrl('2867');
$txDisputes = $driver->getTransactionDisputes('12345');
$driver->respondToDisputeMessage('2867', ['message' => 'Delivered']);

๐Ÿ’ฐ Balance & Settlements

$balance = $driver->getBalance();      // all currencies
$ngn = $driver->getBalance('NGN');     // specific currency
$ledger = $driver->getLedger();        // includes pending

$settlements = $driver->listSettlements(['status' => 'success']);
$txns = $driver->getSettlementTransactions('3090024', ['per_page' => 50]);

๐Ÿข Subaccounts

$subaccount = $driver->createSubaccount([
    'business_name'     => 'Vendor Store',
    'settlement_bank'   => '058',
    'account_number'    => '0123456047',
    'percentage_charge' => 30.0,
    'description'       => 'Vendor marketplace account',
]);
$code = $subaccount['data']['subaccount_code'];

$driver->listSubaccounts();
$driver->getSubaccount($code);
$driver->updateSubaccount($code, ['business_name' => 'Updated Name']);

โœ‚๏ธ Transaction Splits

$split = $driver->createSplit([
    'name'        => 'Marketplace Split',
    'type'        => 'percentage',
    'currency'    => 'NGN',
    'subaccounts' => [
        ['subaccount' => 'ACCT_vendor1', 'share' => 40],
        ['subaccount' => 'ACCT_vendor2', 'share' => 30],
    ],
    'bearer_type' => 'all-proportional',
]);
$splitCode = $split['data']['split_code'];

$driver->listSplits();
$driver->getSplit($splitCode);
$driver->updateSplit($splitCode, ['bearer_type' => 'all']);
$driver->addSubaccountToSplit($splitCode, 'ACCT_vendor3', 20);
$driver->removeSubaccountFromSplit($splitCode, 'ACCT_vendor2');

// Use with payments
$driver->initializePayment([
    'email'      => 'customer@example.com',
    'amount'     => 10000.00,
    'split_code' => $splitCode,
]);

๐Ÿ’ธ Refunds

$refund = $driver->refundPayment('ORDER_123', 2500.00); // partial
$driver->refundPayment('ORDER_123');                     // full

$driver->getRefund('REF_xxxxx');
$driver->listRefunds(['per_page' => 50]);
$driver->retryRefund('REF_xxxxx');

๐Ÿ”” Webhooks

POST https://yoursite.com/webhooks/payment-gateways/paystack
Event Description
charge.success Payment completed
charge.failed Payment failed
charge.pending Payment pending
refund.created / refund.processed Refund events
subscription.create / subscription.disable Subscription lifecycle
transfer.success / transfer.failed / transfer.reversed Transfer events
dispute.created / dispute.resolved Dispute events

๐Ÿ” Configuration

PAYSTACK_PUBLIC_KEY=pk_live_xxxxx
PAYSTACK_SECRET_KEY=sk_live_xxxxx
PAYSTACK_CALLBACK_URL=https://yoursite.com/payment/callback
PAYSTACK_WEBHOOK_SECRET=your_paystack_webhook_secret

---

๐ŸŒŠ Flutterwave

Nigeria ยท Ghana ยท Kenya ยท South Africa ยท US ยท UK ยท 30+ countries

Base URL: https://api.flutterwave.com/v3 Interfaces: 9 total

โœ… Feature Matrix

Feature Status Key Methods
๐Ÿ’ณ One-Time Payments โœ… initializePayment, verifyPayment, getTransactions
๐Ÿ”„ Subscriptions โœ… createPlan, createSubscription, pauseSubscription, cancelSubscription
๐Ÿ‘ค Customers โœ… createCustomer, getCustomer, listCustomers, updateCustomer
๐Ÿ”‘ Charge Authorization โœ… chargeAuthorization, listAuthorizations, deactivateAuthorization
๐Ÿ“Š Transaction Details โœ… getTransactionFees, getTransactionRefs, resendTransactionHook
๐Ÿฆ Disbursements โœ… transfer, bulkTransfer, verifyTransfer, retryTransfer, getTransferById
๐Ÿง Virtual Accounts โœ… createVirtualAccount, listVirtualAccounts
๐Ÿ”— Payment Links & Pages โœ… createPaymentLink, createPaymentPage, listPaymentPages
โš–๏ธ Chargebacks โœ… listChargebacks, acceptChargeback, rejectChargeback
๐Ÿ’ฐ Balance โœ… getBalance, getBalanceByCurrency
๐Ÿ“… Settlements โœ… listSettlements, getSettlement
๐Ÿค Beneficiaries โœ… createBeneficiary, getBeneficiary, listBeneficiaries, deleteBeneficiary
๐Ÿ’ธ Refunds โœ… refundPayment, listRefunds, getRefund

๐Ÿ’ณ Core Payments

$driver = Payment::driver('flutterwave');

$response = $driver->initializePayment([
    'email'    => 'customer@example.com',
    'amount'   => 5000.00,
    'name'     => 'John Doe',
    'phone'    => '+2348123456789',
    'currency' => 'NGN',
]);
// Redirect to $response['data']['link']

$verification = $driver->verifyPayment('ORDER_123');
$payment = $driver->getPayment('ORDER_123');

$txns = $driver->getTransactions(['per_page' => 50]);

๐Ÿ“Š Transaction Details

$fees = $driver->getTransactionFees([
    'amount'   => 5000,
    'currency' => 'NGN',
]);

$refs = $driver->getTransactionRefs('ORDER_123');

$driver->resendTransactionHook('ORDER_123');

๐Ÿ”„ Subscriptions & Plans

// Plans
$plan = $driver->createPlan([
    'name'        => 'Pro Monthly',
    'amount'      => 5000.00,
    'interval'    => 'monthly',
    'currency'    => 'NGN',
]);
$planCode = $plan['data']['id'];

$driver->getPlan($planCode);
$driver->updatePlan($planCode, ['name' => 'Pro Monthly (Updated)']);
$driver->listPlans();
$driver->deletePlan($planCode);

// Subscriptions
$sub = $driver->createSubscription([
    'customer' => 'customer@example.com',
    'plan'     => $planCode,
]);
$subCode = $sub['data']['id'];

$driver->getSubscription($subCode);
$driver->listSubscriptions(['plan' => $planCode]);
$driver->listCustomerSubscriptions('customer@example.com');
$driver->pauseSubscription($subCode);
$driver->resumeSubscription($subCode);
$driver->cancelSubscription($subCode);

๐Ÿ‘ค Customer Management

$customer = $driver->createCustomer([
    'email'      => 'customer@example.com',
    'first_name' => 'John',
    'last_name'  => 'Doe',
    'phone'      => '+2348012345678',
]);
$cId = $customer['data']['id'];

$customer = $driver->getCustomer($cId);
$customers = $driver->listCustomers(['per_page' => 50]);
$driver->updateCustomer($cId, ['first_name' => 'Jane']);

๐Ÿ”‘ Charge Authorization (Recurring Payments)

$response = $driver->chargeAuthorization([
    'email'              => 'customer@example.com',
    'amount'             => 5000.00,
    'authorization_code' => 'AUTH_xxxxx',
    'currency'           => 'NGN',
]);

$auths = $driver->listAuthorizations(['per_page' => 50]);
$driver->deactivateAuthorization('AUTH_xxxxx');

๐Ÿฆ Disbursements & Transfers

$account = $driver->resolveAccountNumber([
    'account_number' => '0123456789',
    'bank_code'      => '044',
]);

$recipient = $driver->createTransferRecipient([
    'name'           => 'Jane Doe',
    'account_number' => '0123456789',
    'bank_code'      => '044',
    'currency'       => 'NGN',
]);
$rCode = $recipient['data']['recipient_code'];

$transfer = $driver->transfer([
    'amount'    => 10000.00,
    'recipient' => $rCode,
    'reference' => 'PAYOUT_001',
]);

$driver->bulkTransfer([
    ['amount' => 5000.00, 'recipient' => 'RCP_abc', 'reference' => 'B1'],
    ['amount' => 3000.00, 'recipient' => 'RCP_def', 'reference' => 'B2'],
]);

$driver->verifyTransfer('PAYOUT_001');
$driver->getTransfer('1');
$driver->retryTransfer('1');
$driver->listTransfers(['per_page' => 50]);

// Fees & rates
$fees = $driver->getTransferFees(['amount' => 5000, 'currency' => 'NGN']);
$rates = $driver->getTransferRates(['amount' => 100, 'currency' => 'USD']);

// Banks
$banks = $driver->listBanks();

๐Ÿง Virtual Accounts

$va = $driver->createVirtualAccount([
    'email' => 'customer@example.com',
    'bvn'   => '12345678901',
]);
$accountNumber = $va['data']['account_number'];

$driver->getVirtualAccount($va['data']['id']);
$driver->listVirtualAccounts();
$driver->deactivateVirtualAccount($va['data']['id']);

// Payment Links
$link = $driver->createPaymentLink([
    'title'   => 'Product Launch',
    'amount'  => 2500.00,
    'currency' => 'NGN',
]);
$url = $link['data']['link'];

$driver->getPaymentLink($link['data']['id']);
$driver->listPaymentLinks();
$driver->updatePaymentLink($link['data']['id'], ['amount' => 3000.00]);
$driver->disablePaymentLink($link['data']['id']);

// Payment Pages
$page = $driver->createPaymentPage([
    'title'    => 'Donation Page',
    'amount'   => 0,
    'currency' => 'NGN',
]);
$driver->listPaymentPages();
$driver->getPaymentPage('1');

โš–๏ธ Chargebacks (Disputes)

$chargebacks = $driver->listChargebacks();
$chargeback = $driver->getChargeback('1');

$driver->acceptChargeback('1');
$driver->rejectChargeback('1');
$driver->updateChargebackEvidence('1', ['evidence' => 'Proof of delivery']);

// DisputeDriverInterface methods
$disputes = $driver->listDisputes();
$driver->getDispute('1');
$driver->updateDispute('1', ['refund_amount' => 500]);

๐Ÿ’ฐ Balance & Settlements

// Balance
$balance = $driver->getBalance();        // default currency
$usd = $driver->getBalanceByCurrency('USD');
$ledger = $driver->getLedger();

// Settlements
$settlements = $driver->listSettlements(['per_page' => 50]);
$settlement = $driver->getSettlement('1');

๐Ÿค Beneficiaries

$beneficiary = $driver->createBeneficiary([
    'bank_code'      => '044',
    'account_number' => '0123456789',
    'name'           => 'Jane Doe',
    'currency'       => 'NGN',
]);

$driver->getBeneficiary('1');
$driver->listBeneficiaries();
$driver->deleteBeneficiary('1');

๐Ÿ’ธ Refunds

$refund = $driver->refundPayment('ORDER_123', 2500.00); // partial
$driver->refundPayment('ORDER_123');                     // full

$driver->listRefunds();
$driver->getRefund('REF_xxxxx');

๐Ÿ”” Webhooks

POST https://yoursite.com/webhooks/payment-gateways/flutterwave
Event Description
charge.completed Payment completed
charge.failed Payment failed
charge.created Payment initiated
transfer.completed Transfer completed
transfer.failed Transfer failed
transfer.pending Transfer pending
refund.completed Refund processed
subscription.enable Subscription activated
subscription.disable Subscription cancelled
subscription.not_renew Subscription expired
dedicated_account.assign.image VA assigned
chargeback.created / chargeback.resolved Chargeback events
settlements Settlement events

๐Ÿ” Configuration

FLUTTERWAVE_PUBLIC_KEY=FLWPUBK-xxxxx
FLUTTERWAVE_SECRET_KEY=FLWSECK-xxxxx
FLUTTERWAVE_ENCRYPTION_KEY=your_flutterwave_encryption_key
FLUTTERWAVE_CALLBACK_URL=https://yoursite.com/payment/callback
FLUTTERWAVE_WEBHOOK_SECRET=your_flutterwave_webhook_secret

---

๐Ÿ”’ Seerbit

Nigeria ยท Ghana ยท Kenya ยท Other African markets

Base URL: https://seerbitapi.com/api/v2 Interfaces: 5 total

โœ… Feature Matrix

Feature Status Key Methods
๐Ÿ’ณ One-Time Payments โœ… initializePayment, verifyPayment, getTransactions
๐Ÿ”„ Subscriptions โœ… createPlan, createSubscription, chargeSubscription
๐Ÿฆ Disbursements โœ… transfer, bulkTransfer, verifyTransfer, listTransfers
๐Ÿง Virtual Accounts โœ… createVirtualAccount, listVirtualAccounts
๐Ÿ”— Payment Links โœ… createPaymentLink, listPaymentLinks
๐Ÿƒ Card Operations โœ… tokenizeCard, chargeCard, authorizeCard, validateOtp
๐Ÿ“ฑ Mobile Money โœ… initializeMobilePayment, getMobileNetworks
๐Ÿ“ฆ Order Payments โœ… createOrder
โš–๏ธ Disputes โœ… listDisputes, getDispute, resolveDispute
๐Ÿ’ฐ Balance โœ… getBalance, getLedger
๐Ÿ’ธ Refunds โœ… refundPayment, listRefunds, getRefund, createRefund
๐Ÿ” Encryption โœ… encryptKey

๐Ÿ’ณ Core Payments

$driver = Payment::driver('seerbit');

$response = $driver->initializePayment([
    'email'    => 'customer@example.com',
    'amount'   => 5000.00,
    'currency' => 'NGN',
    'name'     => 'John Doe',
    'phone'    => '+2348123456789',
]);
// Redirect to $response['data']['payments']['link']

$verification = $driver->verifyPayment('ORDER_123');
$payment = $driver->getPayment('ORDER_123');
$txns = $driver->getTransactions();

๐Ÿƒ Card Operations

Direct card payment APIs beyond the standard checkout flow:

// Tokenize a card for recurring payments
$response = $driver->tokenizeCard([
    'card_number'  => '4187427415564246',
    'expiry_month' => '12',
    'expiry_year'  => '2030',
    'cvv'          => '123',
    'email'        => 'customer@example.com',
]);
$token = $response['data']['token'];

// Charge a card directly (non-3D secure)
$driver->chargeCard([
    'card_number'  => '4187427415564246',
    'expiry_month' => '12',
    'expiry_year'  => '2030',
    'cvv'          => '123',
    'amount'       => 5000.00,
    'email'        => 'customer@example.com',
    'pin'          => '1234', // optional
]);

// Charge using a token
$driver->chargeCard([
    'token'  => $token,
    'amount' => 5000.00,
    'email'  => 'customer@example.com',
]);

// Authorize a card (3D secure)
$response = $driver->authorizeCard([
    'amount' => 5000.00,
    'email'  => 'customer@example.com',
    'token'  => $token,
]);
// Redirect to $response['data']['payments']['redirectLink']

// Validate OTP
$driver->validateOtp([
    'otp'                   => '123456',
    'transaction_reference' => 'TRF_xxxxx',
    'amount'                => 5000.00,
]);

// Capture / Cancel pre-authorized payment
$driver->capturePayment([
    'transaction_reference' => 'TRF_xxxxx',
    'amount'                => 5000.00,
]);

$driver->cancelPayment([
    'transaction_reference' => 'TRF_xxxxx',
]);

๐Ÿ“ฑ Mobile Money

$response = $driver->initializeMobilePayment([
    'email'        => 'customer@example.com',
    'amount'       => 5000.00,
    'phone'        => '256771234567',
    'network_code' => 'MTN', // optional
]);

$networks = $driver->getMobileNetworks();

๐Ÿ“ฆ Order Payments

$response = $driver->createOrder([
    'email'       => 'customer@example.com',
    'amount'      => 5000.00,
    'description' => 'Order #123',
    'items'       => [
        ['name' => 'Product A', 'quantity' => 2, 'amount' => 2500.00],
    ],
]);

๐Ÿ”„ Subscriptions & Plans

$plan = $driver->createPlan([
    'name'     => 'Pro Monthly',
    'amount'   => 5000.00,
    'interval' => 'monthly',
]);
$planCode = $plan['data']['planCode'];

$driver->getPlan($planCode);
$driver->listPlans();
$driver->updatePlan($planCode, ['planName' => 'Pro Monthly (Updated)']);
$driver->deletePlan($planCode);

$sub = $driver->createSubscription([
    'email' => 'customer@example.com',
    'plan'  => $planCode,
]);

// Seerbit-specific subscription extensions
$driver->chargeSubscription([
    'subscription_id' => 'SUB_xxxxx',
    'amount'          => 5000.00,
]);
$driver->getCustomerSubscriptions('CUST_xxxxx');
$driver->getMerchantSubscriptions();

๐Ÿฆ Disbursements & Payouts

// Single payout
$transfer = $driver->transfer([
    'amount'         => 10000.00,
    'bank_code'      => '044',
    'recipient_code' => '0123456789', // account number
    'reason'         => 'Salary payout',
    'currency'       => 'NGN',
]);

// Bulk payouts
$driver->bulkTransfer([
    ['amount' => 5000.00, 'bank_code' => '044', 'recipient_code' => '0123456789'],
    ['amount' => 3000.00, 'bank_code' => '058', 'recipient_code' => '0987654321'],
]);

$driver->verifyTransfer('SEERBIT_REF_001');
$driver->listTransfers(['page' => 0, 'perPage' => 50]);

// Banks
$banks = $driver->listBanks();
$account = $driver->resolveAccountNumber([
    'account_number' => '0123456789',
    'bank_code'      => '044',
]);

๐Ÿง Virtual Accounts

$va = $driver->createVirtualAccount([
    'email'          => 'customer@example.com',
    'name'           => 'Jane Doe',
    'bvn'            => '12345678901',
    'preferred_bank' => '058',
    'currency'       => 'NGN',
]);

$driver->getVirtualAccount($va['data']['reference']);
$driver->listVirtualAccounts();
$driver->deactivateVirtualAccount($va['data']['reference']);

$link = $driver->createPaymentLink([
    'name'   => 'Product Launch',
    'amount' => 2500.00,
]);

$driver->getPaymentLink($link['data']['linkId']);
$driver->listPaymentLinks();
$driver->updatePaymentLink($link['data']['linkId'], ['amount' => 3000.00]);
$driver->disablePaymentLink($link['data']['linkId']);

โš–๏ธ Disputes

$disputes = $driver->listDisputes(['page' => 0, 'size' => 10]);
$dispute = $driver->getDispute('DSP_001');

$driver->updateDispute('DSP_001', ['refundAmount' => 1000]);
$driver->addDisputeEvidence('DSP_001', ['evidence' => 'Proof of delivery']);

$driver->resolveDispute('DSP_001', 'accept');
$driver->resolveDispute('DSP_001', 'reject');

๐Ÿ’ฐ Balance

$balance = $driver->getBalance();      // all currencies
$ngn = $driver->getBalance('NGN');     // specific currency
$ledger = $driver->getLedger();        // ledger balance

๐Ÿ’ธ Refunds

$driver->refundPayment('ORDER_123', 2500.00);

// Merchant API for advanced refund management
$refunds = $driver->listRefunds(['page' => 0, 'size' => 10]);
$refund = $driver->getRefund('REF_001');

$driver->createRefund([
    'paymentReference' => 'ORDER_001',
    'refundAmount'     => 2500.00,
    'refundReason'     => 'Customer request',
]);

๐Ÿ” Encryption

$response = $driver->encryptKey();
// $response['data']['EncryptedSecKey']['encryptedKey']

๐Ÿ”” Webhooks

POST https://yoursite.com/webhooks/payment-gateways/seerbit
Event Description
SUCCESS Payment completed
FAILED Payment failed
ABANDONED Payment abandoned

๐Ÿ” Configuration

SEERBIT_PUBLIC_KEY=your_seerbit_public_key
SEERBIT_SECRET_KEY=your_seerbit_secret_key
SEERBIT_BASE_URL=https://seerbitapi.com/api/v2
SEERBIT_CALLBACK_URL=https://yoursite.com/payment/callback
SEERBIT_WEBHOOK_SECRET=your_seerbit_webhook_secret

---

โšก Stripe

Global ยท 135+ currencies

Base URL: https://api.stripe.com/v1 Interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท PaymentLinkDriverInterface Package: Requires stripe/stripe-php:^17.4

โœ… Feature Matrix

Feature Status Key Methods
๐Ÿ’ณ One-Time Payments โœ… initializePayment, verifyPayment
๐Ÿ”„ Subscriptions โœ… createPlan (Product+Price), createSubscription, cancelSubscription
๐Ÿ”— Payment Links โœ… createPaymentLink, listPaymentLinks

Note: Stripe maps plans to Product + Price. createPlan() returns a Price ID in plan_code โ€” pass that to createSubscription(). Supported intervals: day, week, month, year.

// Core payments
$response = Payment::driver('stripe')->initializePayment([
    'email'    => 'customer@example.com',
    'amount'   => 50.00,
    'currency' => 'usd',
]);

// Subscriptions
$plan = Payment::driver('stripe')->createPlan([
    'name'     => 'Pro Monthly',
    'amount'   => 29.99,
    'currency' => 'usd',
    'interval' => 'month',
]);
$priceId = $plan['data']['plan_code'];

Payment::driver('stripe')->createSubscription([
    'email'     => 'customer@example.com',
    'plan_code' => $priceId,
]);

๐Ÿ” Configuration

STRIPE_PUBLIC_KEY=pk_live_xxxxx
STRIPE_SECRET_KEY=sk_live_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
STRIPE_CALLBACK_URL=https://yoursite.com/payment/callback

---

๐Ÿฆ Other Gateways

These gateways provide core payment capabilities through the shared PaymentDriverInterface.


๐Ÿ’ผ Monnify

Nigeria ยท Virtual accounts + disbursements + Direct Debit mandates + card tokenization Interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท ChargeAuthorizationDriverInterface ยท DisbursementDriverInterface ยท VirtualAccountDriverInterface ยท BalanceDriverInterface ยท SettlementDriverInterface ยท PaymentLinkDriverInterface

Authentication: Automatic Bearer token via Basic Auth (API key + Secret key).

Direct Debit Mandates (Subscriptions)

// Create mandate
$sub = Payment::driver('monnify')->createSubscription([
    'email'          => 'customer@example.com',
    'name'           => 'Jane Doe',
    'amount'         => 5000.00,
    'bank_code'      => '044',
    'account_number' => '0123456789',
    'mandate_type'   => 'CLOSED_FLEXIBLE',
    'frequency'      => 'MONTHLY',
]);
$mandateRef = $sub['data']['mandate_reference'];

// Customer authorizes via authorization link, then:
Payment::driver('monnify')->cancelSubscription($mandateRef);
Payment::driver('monnify')->pauseSubscription($mandateRef);
Payment::driver('monnify')->getSubscription($mandateRef);

Card Tokenization (Charge Auth)

// After first card payment, store the cardToken from transaction response
// Then charge it directly:
Payment::driver('monnify')->chargeAuthorization([
    'authorization_code' => 'card_token_from_first_payment',
    'amount'             => 5000.00,
    'email'              => 'customer@example.com',
]);
// Payments
Payment::driver('monnify')->initializePayment([
    'email'       => 'customer@example.com',
    'amount'      => 5000.00,
    'currency'    => 'NGN',
    'name'        => 'John Doe',
    'description' => 'Order #123',
    'reference'   => 'ORDER_123',
]);

// Virtual accounts (reserved bank accounts)
$va = Payment::driver('monnify')->createVirtualAccount([
    'email'             => 'customer@example.com',
    'name'              => 'Jane Doe',
    'bvn'               => '12345678901',
    'currency_code'     => 'NGN',
    'contract_code'     => config('payment-gateways.gateways.monnify.contract_code'),
    'reference'         => 'VA_' . uniqid(),
    'split_percentages' => [],
]);

// Disbursements
Payment::driver('monnify')->transfer([
    'amount'                => 10000.00,
    'account_number'        => '0123456789',
    'bank_code'             => '044',
    'narration'             => 'Salary payout',
    'reference'             => 'MNFY_PAYOUT_001',
    'wallet_account_number' => config('payment-gateways.gateways.monnify.wallet_account_number'),
]);
MONNIFY_API_KEY=your_monnify_api_key
MONNIFY_SECRET_KEY=your_monnify_secret_key
MONNIFY_CONTRACT_CODE=your_monnify_contract_code
MONNIFY_WALLET_ACCOUNT_NUMBER=your_monnify_wallet_account
MONNIFY_CALLBACK_URL=https://yoursite.com/payment/callback
MONNIFY_WEBHOOK_SECRET=your_monnify_webhook_secret

๐ŸŸข Squad (GTCo)

Nigeria ยท Payments + disbursements + virtual accounts Interfaces: PaymentDriverInterface ยท DisbursementDriverInterface ยท VirtualAccountDriverInterface

Payment::driver('squad')->initializePayment([
    'email'     => 'customer@example.com',
    'amount'    => 5000.00,
    'currency'  => 'NGN',
    'reference' => 'TXN_' . uniqid(),
]);

Payment::driver('squad')->transfer([
    'account_number' => '0123456789',
    'bank_code'      => '044',
    'amount'         => 5000.00,
    'currency'       => 'NGN',
    'reference'      => 'SQ_PAYOUT_001',
    'narration'      => 'Salary',
]);

Payment::driver('squad')->createVirtualAccount([
    'customer_identifier' => 'customer_001',
    'email'               => 'customer@example.com',
    'name'                => 'Jane Doe',
]);
SQUAD_SECRET_KEY=your_squad_secret_key
SQUAD_PUBLIC_KEY=your_squad_public_key
SQUAD_BENEFICIARY_ACCOUNT=your_squad_beneficiary_account
SQUAD_CALLBACK_URL=https://yoursite.com/payment/callback
SQUAD_WEBHOOK_SECRET=your_squad_webhook_secret

๐ŸŸ  OPay

Nigeria ยท Card payments + bank transfers + virtual accounts Interfaces: PaymentDriverInterface ยท DisbursementDriverInterface ยท VirtualAccountDriverInterface ยท BalanceDriverInterface

Auth: Private Key HMAC-SHA512 signing โ€” handled internally. Supports card payments, bank transfers, USSD, OPay wallet, and virtual accounts.

// One-time payment
$payment = Payment::driver('opay')->initializePayment([
    'email'     => 'customer@example.com',
    'amount'    => 5000.00,
    'currency'  => 'NGN',
    'reference' => 'OPAY_' . uniqid(),
]);
// Redirect to $payment['data']['checkout_url']
Payment::driver('opay')->verifyPayment($payment['data']['reference']);

// Refund
Payment::driver('opay')->refundPayment([
    'reference' => 'OPAY_001',
    'amount'    => 2500.00,
]);

// Disbursements
Payment::driver('opay')->transfer([
    'amount'         => 5000.00,
    'currency'       => 'NGN',
    'account_number' => '0123456789',
    'bank_code'      => '044',
    'narration'      => 'Payout',
    'reference'      => 'OPAY_PAYOUT_001',
]);

// Bulk transfers
Payment::driver('opay')->bulkTransfer([
    'currency'    => 'NGN',
    'source'      => 'balance',
    'batch_reference' => 'OPAY_BATCH_001',
    'entries' => [
        ['amount' => 5000.00, 'account_number' => '0123456789', 'bank_code' => '044', 'narration' => 'Payout'],
    ],
]);

// Virtual accounts
Payment::driver('opay')->createVirtualAccount([
    'name'        => 'Jane Doe',
    'email'       => 'customer@example.com',
    'phone'       => '+2348012345678',
    'reference'   => 'VA_' . uniqid(),
    'bvn'         => '12345678901',
    'bank_code'   => '044',
    'currency'    => 'NGN',
    'expiration'  => '365',
]);

// Beneficiaries
Payment::driver('opay')->createBeneficiary([
    'name'          => 'John Doe',
    'account_number' => '0123456789',
    'bank_code'     => '044',
    'currency'      => 'NGN',
]);

// Balance
$balance = Payment::driver('opay')->getBalance('NGN');
$ledger = Payment::driver('opay')->getLedger('NGN');
$statement = Payment::driver('opay')->getWalletStatement('NGN');

Webhooks: HMAC-SHA512 signed.

OPAY_MERCHANT_ID=your_opay_merchant_id
OPAY_PRIVATE_KEY=your_opay_private_key
OPAY_BASE_URL=https://live.opayng.com
OPAY_ENVIRONMENT=production
OPAY_CURRENCY=NGN
OPAY_CALLBACK_URL=https://yoursite.com/payment/callback
OPAY_WEBHOOK_SECRET=your_opay_webhook_secret

๐Ÿ”ต Remita

Nigeria ยท RRR-based payments + Direct Debit mandates + disbursements Interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท DisbursementDriverInterface ยท ChargeAuthorizationDriverInterface ยท SplitDriverInterface ยท PaymentLinkDriverInterface

Auth: SHA512 hash of merchantId + serviceTypeId + orderId + amount + apiKey in the Authorization header.

Payments (RRR flow)

$result = Payment::driver('remita')->initializePayment([
    'email'       => 'customer@example.com',
    'amount'      => 5000.00,
    'description' => 'Invoice #1001',
    'reference'   => 'ORDER_1001',
]);
// Redirect to $result['authorization_url']
Payment::driver('remita')->verifyPayment($result['RRR']);

Subscriptions (Direct Debit Mandates)

Remita uses mandates for recurring billing โ€” customer provides bank details, merchant sends debit instructions.

// Create mandate (subscription)
$sub = Payment::driver('remita')->createSubscription([
    'email'          => 'customer@example.com',
    'payer_name'     => 'Jane Doe',
    'amount'         => 5000.00,
    'bank_code'      => '044',
    'account_number' => '0123456789',
    'start_date'     => '2026-01-01',
    'end_date'       => '2026-12-31',
    'max_debits'     => 12,
]);
$mandateId = $sub['data']['mandate_id'];

// Send debit instruction
Payment::driver('remita')->chargeAuthorization([
    'authorization_code' => $mandateId,
    'amount'             => 5000.00,
]);

// Pause (cancel debit instruction)
Payment::driver('remita')->pauseSubscription($mandateId);

// Cancel (stop mandate)
Payment::driver('remita')->cancelSubscription($mandateId);

// Get mandate status
Payment::driver('remita')->getSubscription($mandateId);

Split Payments (LineItems)

Remita supports splits via lineItems in the RRR generation payload:

Payment::driver('remita')->initializePayment([
    'email'  => 'customer@example.com',
    'amount' => 10000.00,
    'line_items' => [
        ['lineItemsId' => 'VEN1', 'beneficiaryName' => 'Vendor 1', 'beneficiaryAccount' => '0123456789', 'bankCode' => '044', 'beneficiaryAmount' => '6000', 'deductFeeFrom' => '1'],
        ['lineItemsId' => 'VEN2', 'beneficiaryName' => 'Vendor 2', 'beneficiaryAccount' => '9876543210', 'bankCode' => '033', 'beneficiaryAmount' => '4000', 'deductFeeFrom' => '0'],
    ],
]);

Remita payment links are RRR-based hosted checkout pages:

$link = Payment::driver('remita')->createPaymentLink([
    'amount'      => 5000.00,
    'name'        => 'Product Launch',
    'description' => 'Early-bird ticket',
]);
// $link['data']['url'] โ†’ shareable checkout URL

Disbursements

Payment::driver('remita')->transfer([
    'amount'         => 10000.00,
    'account_number' => '0123456789',
    'bank_code'      => '044',
    'narration'      => 'Salary payout',
]);

Payment::driver('remita')->bulkTransfer([
    'batchRef'  => 'BATCH_001',
    'narration' => 'Payroll',
    'transfers' => [
        ['amount' => 5000.00, 'account_number' => '0123456789', 'bank_code' => '044', 'name' => 'Jane Doe'],
    ],
]);
REMITA_API_KEY=your_remita_api_key
REMITA_MERCHANT_ID=your_remita_merchant_id
REMITA_SERVICE_TYPE_ID=your_remita_service_type_id
REMITA_SOURCE_ACCOUNT=your_remita_source_account
REMITA_CALLBACK_URL=https://yoursite.com/payment/callback

๐ŸŸฃ Mono

Nigeria ยท One-time DirectPay and Direct Debit mandate collections Shared interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท ChargeAuthorizationDriverInterface ยท BalanceDriverInterface Mono-specific endpoints: Customers, DirectPay, mandate operations, settlement payout queries, split sub-accounts, Mono Disburse, and Owo/WhatsApp payments.

Auth: mono-sec-key header with the Payments app secret key. For sandbox use Mono's test secret key. The package accepts amounts in major currency units and converts to kobo for amount-bearing requests.

One-time DirectPay

$driver = Payment::driver('mono');

$checkout = $driver->initializePayment([
    'email'        => 'customer@example.com',
    'name'         => 'Jane Doe',
    'amount'       => 5000.00,
    'method'       => 'account', // account, transfer, or whatsapp
    'reference'    => 'ORDER_123',
    'callback_url' => 'https://yoursite.com/payment/callback',
    'meta'         => ['order_id' => 123],
]);
// Redirect to $checkout['data']['mono_url'].

// Verify before fulfilling the order; then query/filter transaction history.
$payment = $driver->verifyPayment('ORDER_123');
$payments = $driver->getTransactions(['page' => 1, 'status' => 'successful', 'limit' => 100]);

// Mono's documented refund endpoint performs full refunds only.
$driver->refundPayment('ORDER_123');
$driver->initiateRefund('ORDER_123', 'wallet'); // optional source: wallet (omit for pending payout)

DirectPay accepts method values account (default), transfer, or whatsapp. It may also include a linked account ID (account), institution details (institution, including an id and optional auth_method), or a split configuration in initializePayment(). Percentage split values remain percentages; fixed values and max caps are provided in major units and converted to kobo.

Customers and Direct Debit mandates

Create a Mono customer before mandate authorization; Mono requires KYC details for this endpoint:

$customer = $driver->createCustomer([
    'email' => 'jane@example.com', 'first_name' => 'Jane', 'last_name' => 'Doe',
    'phone' => '08012345678', 'address' => 'Lagos', 'bvn' => '12345678901',
]);
$customerId = $customer['data']['id'];

// Hosted mandate authorization via the widget flow.
$mandate = $driver->initializeMandate([
    'customer_id' => $customerId,
    'amount' => 200000.00, // variable mandate total cap (or fixed per-debit amount)
    'debit_type' => 'variable', // or fixed
    'mandate_type' => 'emandate', // or sweep
    'start_date' => '2026-10-01',
    'end_date' => '2027-10-01',
]);
// Redirect to $mandate['data']['mono_url'].

// Alternatively, create a mandate directly with your own authorization experience.
$created = $driver->createSubscription([
    'customer_id' => $customerId, 'amount' => 200000.00,
    'account_number' => '0123456789', 'bank_code' => '044',
    'debit_type' => 'variable', 'mandate_type' => 'emandate',
    'start_date' => '2026-10-01', 'end_date' => '2027-10-01',
]);
$mandateId = $created['data']['mandate_id'];

$driver->chargeAuthorization([
    'authorization_code' => $mandateId, 'amount' => 5000.00,
    'reference' => 'DEBIT_001', 'narration' => 'Subscription payment',
]);
$driver->getMandateDebit($mandateId, 'DEBIT_001');
$driver->listMandateDebits($mandateId);
$driver->pauseSubscription($mandateId);
$driver->resumeSubscription($mandateId);
$driver->cancelSubscription($mandateId);
$driver->checkBalance($mandateId, 5000.00); // sufficiency check; โ‚ฆ10 fee, amount must be at least โ‚ฆ200
$driver->getMandateBalance($mandateId); // actual balance; โ‚ฆ50 fee
$driver->getCustomerTransactions($customerId, ['period' => 'last12months', 'page' => 1]);

// Fixed mandates are scheduled by Mono; provide their required schedule:
$driver->initializeMandate([
    'customer_id' => $customerId, 'amount' => 5000.00, 'debit_type' => 'fixed',
    'start_date' => '2026-10-01', 'end_date' => '2027-10-01',
    'frequency' => 'monthly', 'initial_debit_date' => '2026-11-01',
    'retrial_frequency' => 2, 'grace_period' => 3, 'minimum_due' => 1000.00,
]);

// Partial Sweep is for variable sweep mandates only and requires Mono account enablement.
$driver->initializeMandate([
    'customer_id' => $customerId, 'amount' => 200000.00, 'debit_type' => 'variable',
    'mandate_type' => 'sweep', 'allow_partial_sweep' => true,
]);
$driver->setPartialSweep($mandateId, true); // Toggle an existing eligible mandate, if enabled for your account

For fixed mandates, frequency, initial_debit_date, retrial_frequency, grace_period, and minimum_due are required schedule fields. interval is optional for custom frequencies; initial_debit_amount is optional. Fixed mandates are scheduled by Mono rather than triggered with the debit endpoint. Partial Sweep is only supported for variable sweep mandates.

The CustomerDriverInterface shared contract is not implemented because Mono's customer create method requires additional KYC fields; Mono-specific methods createCustomer, getCustomer, listCustomers, getCustomerTransactions, updateCustomer, and deleteCustomer are available instead. Mono does not provide payout transfers or the generic DisbursementDriverInterface.

Split accounts, payout queries, and banks

$subaccount = $driver->createSplitSubaccount([
    'nip_code' => '000000', 'account_number' => '0123456789',
]);
$subaccounts = $driver->listSplitSubaccounts();
$banks = $driver->listBanks();

// View settlements and the transactions included in a payout.
$payouts = $driver->getPayouts(['status' => 'successful']);
$payoutTransactions = $driver->getPayoutTransactions('payout-id', [
    'account' => 'account-id', 'status' => 'successful',
]);

Mono Disburse

Disburse requires a Disburse app and separately entitled secret key. Mono currently documents this product as live-only. This driver accepts total_amount and distribution amount in major currency units and converts them to kobo.

$source = $driver->createDisbursementSourceAccount([
    'app' => 'disburse-app-id', 'account_number' => '0123456789',
    'bank_code' => '044', 'email' => 'business@example.com',
]);
$driver->listDisbursementSourceAccounts();
$driver->getDisbursementSourceAccount($source['data']['id']);
$driver->updateDisbursementSourceAccountStatus($source['data']['id'], 'enable');

$batch = $driver->createDisbursement([
    'reference' => 'PAYROLL_001', 'source' => 'mandate', 'account' => $source['data']['id'],
    'type' => 'instant', 'total_amount' => 5000.00,
    'distribution' => [[
        'reference' => 'SALARY_001', 'recipient_email' => 'staff@example.com',
        'account' => ['account_number' => '1234567890', 'bank_code' => '044'],
        'amount' => 5000.00, 'narration' => 'Salary',
    ]],
]);
$driver->listDisbursements();
$driver->getDisbursement($batch['data']['id']);
$driver->transitionDisbursement($batch['data']['id'], 'trigger'); // or cancel
$driver->addDisbursementDistributions($batch['data']['id'], [[
    'reference' => 'BONUS_001', 'account_number' => '0123456789',
    'bank_code' => '058', 'amount' => 100.00,
]]);
$driver->updateDisbursementDistribution($batch['data']['id'], 'distribution-id', ['amount' => 120.00]);
$driver->deleteDisbursementDistribution($batch['data']['id'], 'distribution-id');
$driver->listDisbursementDistributions($batch['data']['id']);
$driver->getDisbursementDistribution($batch['data']['id'], 'distribution-id');

Owo / WhatsApp payments

Owo requires an Owo-enabled app/key. This feature includes user status, beneficiary link lifecycle, one-time or recurring fund requests, and payment history. Supply request amounts in kobo as required by Mono's Owo API.

$driver->getOwoUserStatus('2348012345678');
$driver->linkOwoBeneficiary([
    'phone' => '2348012345678',
    'fund_beneficiary' => [
        'name' => 'Jane Doe', 'nip_code' => '000013', 'account_number' => '0123456789',
    ],
]);
$driver->unlinkOwoBeneficiary('beneficiary-id');
$driver->getOwoBeneficiary('beneficiary-id');
$driver->listOwoBeneficiaries(['phone' => '2348012345678', 'limit' => 15]);

$fundRequest = $driver->createOwoFundRequest([
    'phone' => '2348012345678', 'reference' => 'SAVINGS_001',
    'type' => 'recurring', 'description' => 'Weekly savings', 'amount' => 100000,
    'currency' => 'NGN',
    'schedule' => ['period' => 'week', 'interval' => 1, 'start_date' => '2026-10-07T00:00:00Z'],
]);
$driver->listOwoFundRequests(['phone' => '2348012345678']);
$driver->getOwoFundRequest('fund-request-id');
$driver->listOwoFundRequestPayments('fund-request-id');
$driver->getOwoFundRequestPayment('fund-request-id', 'payment-id');

Separate API credentials can be configured with MONO_DISBURSE_SECRET_KEY and MONO_OWO_SECRET_KEY; each falls back to MONO_SECRET_KEY if unset. DirectPay/Direct Debit, payout queries and customer methods use MONO_SECRET_KEY.

Mono webhook signature verification uses mono-webhook-secret. Configure Mono to send DirectPay, Direct Debit, Disburse, and Owo webhook events, and deduplicate deliveries using event_id.

MONO_SECRET_KEY=your_mono_secret_key
MONO_DISBURSE_SECRET_KEY=your_disburse_app_secret_key
MONO_OWO_SECRET_KEY=your_owo_app_secret_key
MONO_BASE_URL=https://api.withmono.com
MONO_CALLBACK_URL=https://yoursite.com/payment/callback
MONO_WEBHOOK_SECRET=your_mono_webhook_secret

๐ŸŸก Budpay

Nigeria ยท Dual-header HMAC auth Interfaces: PaymentDriverInterface ยท DisbursementDriverInterface ยท VirtualAccountDriverInterface

Auth: Bearer token + Encryption header (HMAC-SHA512) โ€” handled internally.

Payment::driver('budpay')->initializePayment([
    'email'     => 'customer@example.com',
    'amount'    => 5000.00,
    'reference' => 'BDP_' . uniqid(),
    'currency'  => 'NGN',
]);

Payment::driver('budpay')->transfer([
    'amount'         => 5000.00,
    'currency'       => 'NGN',
    'account_number' => '0123456789',
    'bank_code'      => '044',
    'narration'      => 'Payout',
    'reference'      => 'BDP_PAYOUT_001',
]);
BUDPAY_SECRET_KEY=your_budpay_secret_key
BUDPAY_PUBLIC_KEY=your_budpay_public_key
BUDPAY_CALLBACK_URL=https://yoursite.com/payment/callback
BUDPAY_WEBHOOK_SECRET=your_budpay_webhook_secret

๐ŸŸ  Interswitch (Webpay)

Nigeria ยท OAuth2 + kobo amounts + OTP authentication Interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท DisbursementDriverInterface ยท PaymentLinkDriverInterface

Auth: OAuth2 client_credentials โ€” fetched and cached automatically. Amounts in kobo. Currency uses ISO 4217 numeric codes (e.g. 566 for NGN).

Native methods: authenticateOtp() ยท tokenizeCard() ยท chargeRecurring() โ€” these map directly to IPG v3 API endpoints.

pay() โ€” convenience OTP flow

Chains initializePayment โ†’ authenticateOtp automatically when the gateway returns responseCode T0 (OTP required) or S0 (3DS redirect):

$result = Payment::driver('interswitch')->pay(
    ['email' => 'customer@example.com', 'amount' => 5000.00],
    function (array $initResponse): ?string {
        return $this->requestOtpFromUser($initResponse);
    }
);

if ($result['status'] === 'completed') {
    // Payment approved (responseCode '00')
} elseif ($result['status'] === 'otp_required') {
    // OTP resolver returned null โ€” user hasn't provided OTP
} elseif ($result['status'] === '3ds_required') {
    // Visa 3D Secure โ€” redirect to $result['response']['paymentUrl']
} else {
    // Failed โ€” check $result['response']['responseCode']
}

Native IPG methods

// OTP authentication (Verve/Mastercard)
$result = Payment::driver('interswitch')->authenticateOtp([
    'paymentId'     => '12345',
    'otp'           => '123456',
    'transactionId' => 'ISW_TXN_001',
]);

// Card tokenization for recurring billing
$token = Payment::driver('interswitch')->tokenizeCard([
    'pan'    => '5061020100000000009',
    'expiry' => '10/25',
    'cvv'    => '123',
    'amount' => 100,
    'email'  => 'customer@example.com',
    'pin'    => '1111',
]);

// Charge a tokenized card
$charge = Payment::driver('interswitch')->chargeRecurring([
    'token'  => 'isw_token_xxx',
    'amount' => 5000.00,
    'email'  => 'customer@example.com',
]);
// Plans
Payment::driver('interswitch')->createPlan([
    'name'     => 'Pro Monthly',
    'amount'   => 5000.00,
    'interval' => 'monthly',
]);

// Transfer
Payment::driver('interswitch')->transfer([
    'amount'         => 10000.00,
    'account_number' => '0123456789',
    'bank_code'      => '044',
    'narration'      => 'Salary payout',
]);

// Payment link
Payment::driver('interswitch')->createPaymentLink([
    'name'        => 'Product Launch',
    'amount'      => 2500.00,
    'description' => 'Early-bird ticket',
]);
INTERSWITCH_CLIENT_ID=your_interswitch_client_id
INTERSWITCH_CLIENT_SECRET=your_interswitch_client_secret
INTERSWITCH_MERCHANT_CODE=your_interswitch_merchant_code
INTERSWITCH_PAYABLE_CODE=your_interswitch_payable_code
INTERSWITCH_TERMINAL_ID=your_interswitch_terminal_id
INTERSWITCH_CALLBACK_URL=https://yoursite.com/payment/callback
INTERSWITCH_WEBHOOK_SECRET=your_interswitch_webhook_secret

๐ŸŸฃ PayPal

Global ยท OAuth2 + Payouts Interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท DisbursementDriverInterface

Auth: OAuth2 client_credentials โ€” token fetched and cached automatically. Supports payments, billing subscriptions, and Payouts (bulk disbursements).

$order = Payment::driver('paypal')->initializePayment([
    'amount'   => 50.00,
    'currency' => 'USD',
    'email'    => 'customer@example.com',
]);
// Redirect to $order['links'][n]['href'] where rel == 'approve'
Payment::driver('paypal')->verifyPayment($order['id']);

Webhooks: RSA-SHA256 certificate-based signing (not HMAC).

PAYPAL_CLIENT_ID=your_paypal_client_id
PAYPAL_CLIENT_SECRET=your_paypal_client_secret
PAYPAL_CURRENCY=USD
PAYPAL_BASE_URL=https://api.paypal.com
PAYPAL_CALLBACK_URL=https://yoursite.com/payment/callback
PAYPAL_CANCEL_URL=https://yoursite.com/payment/cancel

๐Ÿ“ฑ M-Pesa (Safaricom Kenya)

Kenya ยท Full Daraja API Interfaces: PaymentDriverInterface ยท BalanceDriverInterface ยท DisbursementDriverInterface ยท SubscriptionDriverInterface

Flow: STK Push (collections), B2C (payouts), B2B (merchant transfers), Ratiba (standing orders).

// STK Push โ€” prompt on customer's phone
$response = Payment::driver('mpesa')->initializePayment([
    'phone'  => '254712345678', // international format, no +
    'amount' => 500,            // KES whole number
]);
$checkoutRequestId = $response['data']['checkout_request_id'];
Payment::driver('mpesa')->verifyPayment($checkoutRequestId);

// B2C payout โ€” send money to customer
Payment::driver('mpesa')->transfer([
    'recipient_code' => '254712345678',
    'amount'         => 1000,
    'reason'         => 'Refund for order #123',
]);

// Account balance
Payment::driver('mpesa')->getBalance();

// Standing order (Ratiba)
Payment::driver('mpesa')->createSubscription([
    'phone'     => '254712345678',
    'amount'    => 2000,
    'frequency' => 'Monthly',
]);

// Register C2B callback URLs (run once)
Payment::driver('mpesa')->registerC2BUrls([
    'confirmation_url' => 'https://yoursite.com/mpesa/confirm',
    'validation_url'   => 'https://yoursite.com/mpesa/validate',
]);

// Dynamic QR code
Payment::driver('mpesa')->generateQRCode([
    'amount' => 500,
    'phone'  => '254712345678',
]);

Note: M-Pesa does not send HMAC signatures โ€” secure callback URLs via HTTPS + IP allowlisting.

MPESA_CONSUMER_KEY=your_mpesa_consumer_key
MPESA_CONSUMER_SECRET=your_mpesa_consumer_secret
MPESA_SHORTCODE=your_mpesa_shortcode
MPESA_PASSKEY=your_mpesa_passkey
MPESA_INITIATOR_NAME=your_mpesa_initiator
MPESA_SECURITY_CREDENTIAL=your_mpesa_security_credential
MPESA_CALLBACK_URL=https://yoursite.com/webhooks/payment-gateways/mpesa
MPESA_RESULT_URL=https://yoursite.com/webhooks/payment-gateways/mpesa
MPESA_TIMEOUT_URL=https://yoursite.com/webhooks/payment-gateways/mpesa

๐ŸŸค MTN MoMo

Uganda ยท Ghana ยท MTN markets ยท Mobile Money Interfaces: PaymentDriverInterface ยท DisbursementDriverInterface

Flow: Two products (Collections + Disbursements) with separate API credentials. Push sent to customer's wallet โ€” asynchronous.

$response = Payment::driver('mtnmomo')->initializePayment([
    'phone'    => '256771234567', // MSISDN, no +
    'amount'   => 1000,
    'currency' => 'UGX',
]);
$referenceId = $response['data']['reference'];

Payment::driver('mtnmomo')->verifyPayment($referenceId);

// Disbursement
Payment::driver('mtnmomo')->transfer([
    'phone'     => '256771234567',
    'amount'    => 1000,
    'currency'  => 'UGX',
    'narration' => 'Salary payout',
]);
MTNMOMO_COLLECTION_USER_ID=your_mtnmomo_collection_user_id
MTNMOMO_COLLECTION_API_KEY=your_mtnmomo_collection_api_key
MTNMOMO_COLLECTION_SUBSCRIPTION_KEY=your_mtnmomo_collection_subscription_key
MTNMOMO_DISBURSEMENT_USER_ID=your_mtnmomo_disbursement_user_id
MTNMOMO_DISBURSEMENT_API_KEY=your_mtnmomo_disbursement_api_key
MTNMOMO_DISBURSEMENT_SUBSCRIPTION_KEY=your_mtnmomo_disbursement_subscription_key
MTNMOMO_ENVIRONMENT=sandbox
MTNMOMO_CURRENCY=EUR
MTNMOMO_CALLBACK_URL=https://yoursite.com/webhooks/payment-gateways/mtnmomo

๐Ÿ”ด Razorpay

India ยท Paise amounts + payouts Interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท DisbursementDriverInterface ยท PaymentLinkDriverInterface

Auth: Basic Auth (key_id:key_secret) on every request. Amounts in paise (1 INR = 100 paise) โ€” handled internally.

$order = Payment::driver('razorpay')->initializePayment([
    'email'    => 'customer@example.com',
    'amount'   => 499.00,
    'currency' => 'INR',
    'name'     => 'Jane Doe',
    'phone'    => '+919876543210',
]);
// Pass $order['id'], $order['data']['key_id'] to Razorpay checkout.js
RAZORPAY_KEY_ID=rzp_live_xxxxx
RAZORPAY_KEY_SECRET=your_razorpay_key_secret
RAZORPAY_ACCOUNT_NUMBER=your_razorpay_x_account_number
RAZORPAY_CURRENCY=INR
RAZORPAY_CALLBACK_URL=https://yoursite.com/payment/callback
RAZORPAY_WEBHOOK_SECRET=your_razorpay_webhook_secret

๐Ÿ›๏ธ Paddle

Global ยท Merchant of Record (SaaS / digital goods) Interfaces: PaymentDriverInterface ยท SubscriptionDriverInterface ยท PaymentLinkDriverInterface

Plans map to Prices, payments to Transactions, payment links to Paddle's native Payment Links.

$transaction = Payment::driver('paddle')->initializePayment([
    'amount'      => 29.99,
    'currency'    => 'USD',
    'description' => 'Pro plan',
    'email'       => 'customer@example.com',
]);
// Redirect to $transaction['data']['authorization_url']

Webhooks: HMAC-SHA256 with Paddle-Signature: ts=...;h1=... format.

PADDLE_API_KEY=your_paddle_api_key
PADDLE_CLIENT_TOKEN=your_paddle_client_token
PADDLE_WEBHOOK_SECRET=your_paddle_webhook_secret
PADDLE_CURRENCY=USD
PADDLE_BASE_URL=https://api.paddle.com
PADDLE_CALLBACK_URL=https://yoursite.com/payment/callback

---

# ๐Ÿ› ๏ธ Services & Utilities

๐Ÿ“Š Balance Aggregation Service

Namespace: Alphabalex\PaymentMadeEasy\Services\BalanceAggregator

Query balances across all configured gateways in a single call.

use Alphabalex\PaymentMadeEasy\Services\BalanceAggregator;

$aggregator = app(BalanceAggregator::class);

// All gateways
$result = $aggregator->aggregate();

// Filter by currency
$result = $aggregator->aggregate(currency: 'NGN');

// Specific gateways
$result = $aggregator->aggregate(gateways: ['paystack', 'flutterwave']);

Response:

[
    'gateways' => [
        'paystack' => [
            'status' => 'success',
            'data'   => [['currency' => 'NGN', 'balance' => 500000.0]],
        ],
        'flutterwave' => [
            'status' => 'success',
            'data'   => [['currency' => 'NGN', 'balance' => 300000.0]],
        ],
        'squad' => [
            'status' => 'skipped',
            'data'   => [],
            'error'  => 'Gateway does not support balance queries',
        ],
    ],
    'totals'     => ['NGN' => 800000.0],
    'queried_at' => '2025-08-21T12:00:00+00:00',
    'successful' => 2,
    'failed'     => 0,
    'skipped'    => 1,
]

Helpers:

$gateways = $aggregator->getBalanceGateways();     // ['paystack', 'flutterwave']
$supported = $aggregator->gatewaySupportsBalance('paystack'); // true

Features: - ๐Ÿ”’ Graceful error handling โ€” individual gateway failures don't break aggregation - ๐Ÿ” Auto-discovery โ€” finds all gateways implementing BalanceDriverInterface - ๐Ÿ’ฑ Currency filtering โ€” query specific currency across all gateways - ๐Ÿ“Š Multi-currency totals โ€” sums balances per currency - โš–๏ธ Balance normalization โ€” handles different response formats - ๐Ÿ”„ Subunit conversion โ€” converts kobo/cents to major units automatically


๐Ÿ“ Payment Recorder

Namespace: Alphabalex\PaymentMadeEasy\Services\PaymentRecorder

Persist payment activity to your database when PAYMENT_RECORDING_ENABLED=true.

use Alphabalex\PaymentMadeEasy\Services\PaymentRecorder;

$recorder = app(PaymentRecorder::class);

// Record a transaction
$transaction = $recorder->recordTransaction('paystack', $requestData, $gatewayResponse);

// Update status after verification/webhook
$recorder->updateTransactionStatus('ORDER_001', 'successful', 'PS_GATEWAY_REF');

// Record a transfer
$transfer = $recorder->recordTransfer('paystack', $transferData, $transferResponse);

// Log webhook event
$recorder->logWebhookEvent('paystack', $webhookEvent);

// Handle refund webhook
$recorder->handleRefundWebhook('paystack', 'ORDER_001', 50.0, 'NGN', $rawPayload, 're_abc123');

With PAYMENT_RECORDING_AUTO_WEBHOOK_EVENTS=true, listeners also persist RefundProcessed, DisputeCreated, and ChargebackCreated events.

Models:

Model Table Purpose
PaymentTransaction payment_transactions One-time payment records
PaymentRefund payment_refunds Refund history (multi-refund support)
PaymentDispute payment_disputes Dispute / chargeback events
PaymentSubscription payment_subscriptions Subscription / billing records
PaymentTransfer payment_transfers Disbursement / transfer records
PaymentWebhookLog payment_webhook_logs Audit log for incoming webhooks
// Scope helpers
PaymentTransaction::successful()->gateway('paystack')->get();
PaymentSubscription::active()->forEmail('user@example.com')->get();
PaymentTransfer::successful()->gateway('mtnmomo')->get();

// Refund reporting
$transaction->refunds;
$transaction->totalRefundedAmount();

*Built with โค๏ธ by [Alphabalex](https://github.com/alphabalex)*