njoguamos/laravel-pesapal
Laravel 11+ package for Pesapal v3 API: generates and caches short-lived access tokens, submits order requests, checks transaction status, and stores Instant Payment Notifications (IPNs) in your database for easier payment integrations.
Installation Add the package via Composer:
composer require njoguamos/laravel-pesapal
Publish the config file:
php artisan vendor:publish --provider="Njoguamos\Pesapal\PesapalServiceProvider" --tag="pesapal-config"
Update .env with your Pesapal API credentials:
PESAPAL_CONSUMER_KEY=your_consumer_key
PESAPAL_CONSUMER_SECRET=your_consumer_secret
PESAPAL_ENVIRONMENT=sandbox|live
First Use Case: Initialize Client In a controller or service, inject the Pesapal client:
use Njoguamos\Pesapal\Facades\Pesapal;
public function createPayment()
{
$client = Pesapal::client();
// Use $client to interact with Pesapal API
}
Quick Test Verify the connection with a simple API call (e.g., fetch merchant details):
$merchant = Pesapal::merchant()->get();
dd($merchant);
Payment Processing
$payment = Pesapal::payment()->create([
'amount' => 1000, // in cents
'currency' => 'KES',
'reference' => 'ORDER-' . uniqid(),
'metadata' => ['user_id' => 1],
]);
$status = Pesapal::payment()->get($payment->id);
if ($status->status === 'completed') {
// Mark order as paid in your system
}
Webhooks
routes/web.php:
Route::post('/pesapal/webhook', [PaymentController::class, 'handleWebhook']);
public function handleWebhook(Request $request)
{
$payload = $request->validate([
'event' => 'required',
'data' => 'required',
]);
Pesapal::webhook()->handle($payload['event'], $payload['data']);
}
Recurring Payments
$subscription = Pesapal::subscription()->create([
'amount' => 5000,
'currency' => 'KES',
'reference' => 'SUB-' . uniqid(),
'interval' => 'monthly',
]);
Laravel Cashier Synergy
Extend Laravel Cashier’s Billable trait to sync Pesapal subscriptions:
use Laravel\Cashier\Billable;
use Njoguamos\Pesapal\Facades\Pesapal;
class User extends Authenticatable
{
use Billable;
public function subscribeToPlan($plan)
{
$subscription = Pesapal::subscription()->create([
'amount' => $plan->price * 100,
'reference' => 'SUB-' . $this->id,
]);
$this->newSubscription('pesapal', $plan)->create($subscription->token);
}
}
Middleware for Authenticated Payments Protect payment routes:
Route::middleware(['auth:sanctum'])->group(function () {
Route::post('/pay', [PaymentController::class, 'processPayment']);
});
Environment Mismatch
sandbox and live environments.PESAPAL_ENVIRONMENT in .env before going live. Use:
if (config('pesapal.environment') === 'live') {
// Live-specific logic
}
Webhook Signature Validation
'webhook' => [
'strict_signature_validation' => true,
],
$isValid = Pesapal::webhook()->validateSignature(
$request->header('pesapal-signature'),
$request->getContent()
);
Currency and Amount Handling
1000 for KES 10.00).$amountInCents = $amountInKsh * 100;
Enable API Logging
Add to config/pesapal.php:
'log' => [
'enabled' => true,
'channel' => 'single',
],
Check logs in storage/logs/laravel.log.
Test with Sandbox First Use Pesapal’s sandbox docs to test flows like:
status=completed).status=failed).Customize API Responses Override the default response handler:
Pesapal::extend(function ($client) {
$client->onResponse(function ($response) {
// Transform response data (e.g., convert timestamps)
return $response->json();
});
});
Add Custom Fields to Payments Extend the payment payload schema:
Pesapal::payment()->extendPayload(function ($payload) {
$payload['custom_field'] = 'your_value';
return $payload;
});
Mock Pesapal for Testing Use Laravel’s HTTP mocking:
$mock = Mockery::mock('overload', Pesapal::class);
$mock->shouldReceive('payment')
->andReturnSelf()
->shouldReceive('create')
->andReturn(['status' => 'completed']);
How can I help you explore Laravel packages today?