Laravel
Official Laravel integration for ZevPay Checkout. Provides a service provider, facade, webhook handling with signature verification, and event dispatching.
Installation
composer require zevpay/laravelThe package uses Laravel auto-discovery — no manual provider registration needed.
Publish the config file:
php artisan vendor:publish --tag=zevpay-configRequires PHP 8.1+ and Laravel 10, 11, or 12.
Configuration
Add your API keys to .env:
ZEVPAY_SECRET_KEY=sk_live_your_secret_key
ZEVPAY_PUBLIC_KEY=pk_live_your_public_key
ZEVPAY_WEBHOOK_SECRET=whsec_your_webhook_secretGet your keys from the ZevPay Dashboard.
The published config file (config/zevpay.php) supports these options:
| Variable | Default | Description |
|---|---|---|
ZEVPAY_SECRET_KEY | — | Your sk_live_* or sk_test_* key |
ZEVPAY_PUBLIC_KEY | — | Your pk_live_* or pk_test_* key (optional, for frontend) |
ZEVPAY_WEBHOOK_SECRET | — | Webhook signing secret from Dashboard |
ZEVPAY_WEBHOOK_PATH | webhooks/zevpay | Webhook endpoint path |
ZEVPAY_BASE_URL | https://api.zevpaycheckout.com | API base URL |
ZEVPAY_TIMEOUT | 30 | Request timeout in seconds |
ZEVPAY_MAX_RETRIES | 2 | Retries on 5xx errors |
Usage
Facade
use ZevPay\Laravel\Facades\ZevPay;
// Initialize a checkout session
$session = ZevPay::checkout()->initialize([
'amount' => 500000, // ₦5,000 in kobo
'email' => 'customer@example.com',
'reference' => 'ORDER-123',
'callback_url' => 'https://yoursite.com/callback',
]);
return redirect($session['checkout_url']);Dependency injection
use ZevPay\ZevPay;
class PaymentController extends Controller
{
public function checkout(ZevPay $zevpay)
{
$session = $zevpay->checkout->initialize([
'amount' => 500000,
'email' => 'customer@example.com',
]);
return redirect($session['checkout_url']);
}
}Available resources
| Resource | Facade | Methods |
|---|---|---|
| Checkout | ZevPay::checkout() | initialize, verify, get, selectPaymentMethod |
| Transfers | ZevPay::transfers() | create, list, get, verify, listBanks, resolveAccount, calculateCharges, getBalance |
| Invoices | ZevPay::invoices() | create, list, get, update, send, cancel |
| Static PayID | ZevPay::staticPayId() | create, list, get, update, deactivate, reactivate |
| Dynamic PayID | ZevPay::dynamicPayId() | create, list, get, deactivate |
| Virtual Accounts | ZevPay::virtualAccounts() | create, list, get |
| Wallet | ZevPay::wallet() | get, update, listMembers, addMember, removeMember, updateMember |
Webhooks
The package registers a webhook route at POST /webhooks/zevpay with automatic HMAC-SHA256 signature verification.
Setup
- Copy the webhook URL:
https://yoursite.com/webhooks/zevpay - Go to your ZevPay Dashboard webhook settings
- Paste the URL and save
- Copy the webhook secret and add it to
.envasZEVPAY_WEBHOOK_SECRET
Always configure webhooks
Webhooks provide reliable server-to-server payment confirmation. Client-side callbacks may fail if the customer closes their browser.
Listening to events
Register listeners in your EventServiceProvider or use closures:
use ZevPay\Laravel\Events\ChargeSuccess;
use ZevPay\Laravel\Events\WebhookReceived;
// Listen to a specific event
class HandleChargeSuccess
{
public function handle(ChargeSuccess $event): void
{
$reference = $event->payload['data']['reference'];
$amount = $event->payload['data']['amount'];
// Update your order, send receipt, etc.
Order::where('reference', $reference)->update([
'status' => 'paid',
]);
}
}
// Or listen to all webhook events
class LogAllWebhooks
{
public function handle(WebhookReceived $event): void
{
logger()->info("ZevPay: {$event->eventType}", $event->payload);
}
}Available events
| Event class | Webhook type |
|---|---|
ChargeSuccess | charge.success |
TransferSuccess | transfer.success |
TransferFailed | transfer.failed |
TransferReversed | transfer.reversed |
InvoiceCreated | invoice.created |
InvoiceSent | invoice.sent |
InvoicePaymentReceived | invoice.payment_received |
InvoicePaid | invoice.paid |
InvoiceOverdue | invoice.overdue |
InvoiceCancelled | invoice.cancelled |
WebhookReceived | All events (generic catch-all) |
All specific event classes extend WebhookEvent and have a $payload property containing the raw webhook data.
Custom webhook path
ZEVPAY_WEBHOOK_PATH=api/webhooks/zevpayCSRF exemption
The webhook route uses VerifyWebhookSignature middleware instead of CSRF. For Laravel 10 and older, add the path to your VerifyCsrfToken middleware $except array:
protected $except = [
'webhooks/zevpay',
];Laravel 11+ handles this automatically.
Artisan commands
Verify API keys
php artisan zevpay:verify-keysChecks that your keys are configured, have valid format (sk_*, pk_*), and tests API connectivity.
Test webhook
php artisan zevpay:webhook-test
php artisan zevpay:webhook-test transfer.successSends a signed test webhook payload to your local endpoint. Useful for testing your event listeners during development.
Error handling
The package uses the PHP SDK exception hierarchy:
use ZevPay\Laravel\Facades\ZevPay;
use ZevPay\Exceptions\ValidationException;
use ZevPay\Exceptions\AuthenticationException;
use ZevPay\Exceptions\NotFoundException;
use ZevPay\Exceptions\RateLimitException;
try {
$session = ZevPay::checkout()->initialize([
'amount' => 500000,
'email' => 'customer@example.com',
]);
} catch (ValidationException $e) {
// $e->details contains field-level errors
// e.g. ['email' => 'Email is required']
} catch (AuthenticationException $e) {
// Invalid API key
} catch (NotFoundException $e) {
// Resource not found
} catch (RateLimitException $e) {
// $e->retryAfter — seconds to wait
}Resources
- API Reference — Full endpoint documentation
- PHP SDK — Underlying server-side SDK
- Webhook Events — All event types
- Packagist — Package registry