felixmuhoro / laravel-mpesa-notify
Multi-channel notifications (mail, SMS via Africa's Talking, Slack, database) for M-Pesa payment events.
Package info
github.com/felixmuhoro/laravel-mpesa-notify
pkg:composer/felixmuhoro/laravel-mpesa-notify
Requires
- php: ^8.1
- felixmuhoro/laravel-mpesa: ^1.2
- illuminate/notifications: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
This package is auto-updated.
Last update: 2026-06-06 23:34:47 UTC
README
Multi-channel notifications for M-Pesa payment events in Laravel 10/11/12/13.
Listens to events from felixmuhoro/laravel-mpesa and fans them out to mail, SMS via Africa's Talking, Slack, and the database — fully configurable per event type.
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.1 |
| Laravel | 10, 11, 12, or 13 |
| felixmuhoro/laravel-mpesa | ^1.2 |
Installation
composer require felixmuhoro/laravel-mpesa-notify
Publish the config file:
php artisan vendor:publish --tag=mpesa-notify-config
Optionally publish migrations and views:
php artisan vendor:publish --tag=mpesa-notify-migrations php artisan vendor:publish --tag=mpesa-notify-views php artisan migrate
Configuration
Add the following variables to your .env:
# Channels to enable globally (comma-separated) # Supported: mail, mpesa-sms, slack, database MPESA_NOTIFY_CHANNELS=mail,mpesa-sms,database # Africa's Talking credentials (required for mpesa-sms channel) AT_API_KEY=your_africas_talking_api_key AT_USERNAME=your_username AT_SENDER_ID=MPESA AT_SANDBOX=false # Slack incoming webhook (required for slack channel) MPESA_NOTIFY_SLACK_WEBHOOK=https://hooks.slack.com/services/... # Optional: auto-resolve the notifiable by phone from your User model MPESA_NOTIFY_USER_MODEL=App\Models\User MPESA_NOTIFY_PHONE_COLUMN=phone_number # Set to false to register event listeners manually MPESA_NOTIFY_AUTO_LISTEN=true
Full config reference: config/mpesa-notify.php.
Channels
| Channel | Identifier | Driver |
|---|---|---|
mail |
Laravel built-in mailer | |
| SMS | mpesa-sms |
Africa's Talking REST API |
| Slack | slack |
Laravel Slack notification |
| Database | database |
Laravel notifications table |
Per-event channel override
// config/mpesa-notify.php 'channels_per_event' => [ 'payment_successful' => ['mail', 'mpesa-sms', 'slack', 'database'], 'payment_failed' => ['mail', 'mpesa-sms', 'database'], 'stk_push_initiated' => ['mpesa-sms'], ],
Notifiable Model Setup
Your User (or any notifiable model) should implement the NotifiablePayer interface:
use FelixMuhoro\MpesaNotify\Contracts\NotifiablePayer; use Illuminate\Notifications\Notifiable; class User extends Authenticatable implements NotifiablePayer { use Notifiable; public function routeNotificationForMpesaSms(): string { return $this->phone_number; // E.164 format, e.g. +254712345678 } public function routeNotificationForMail(): ?string { return $this->email; } public function routeNotificationForSlack(): ?string { return null; // Use the global webhook from config } public function displayName(): string { return $this->name; } }
How It Works
-
felixmuhoro/laravel-mpesafires one of three events:PaymentSuccessfulPaymentFailedStkPushInitiated
-
SendPaymentNotificationlistener picks it up (auto-wired by the service provider). -
The listener:
- Extracts the payment payload into a
PaymentDatavalue object. - Looks up the notifiable
Userby phone number (configurable). - Dispatches the appropriate
Notificationclass.
- Extracts the payment payload into a
-
The notification's
via()method asksMpesaNotificationManagerwhich channels are active for this event type. -
Each channel's payload method is called (
toMail,toSms,toSlack,toDatabase).
Notifications
| Class | Event |
|---|---|
PaymentSuccessfulNotification |
PaymentSuccessful |
PaymentFailedNotification |
PaymentFailed |
StkPushInitiatedNotification |
StkPushInitiated |
All three implement ShouldQueue and are dispatched on the notifications queue.
Manual Dispatch
You can send a notification manually without relying on events:
use FelixMuhoro\MpesaNotify\Notifications\PaymentSuccessfulNotification; use FelixMuhoro\MpesaNotify\ValueObjects\PaymentData; $payment = PaymentData::fromArray([ 'transactionId' => 'QKA12BC3DE', 'amount' => 1500, 'currency' => 'KES', 'phone' => '+254712345678', 'payerName' => 'John Doe', 'description' => 'Order #123', 'eventType' => 'payment_successful', ]); $user->notify(new PaymentSuccessfulNotification($payment));
SMS via Africa's Talking
The SmsChannel hits the Africa's Talking v1 messaging API directly using Guzzle:
- Live endpoint:
https://api.africastalking.com/version1/messaging - Sandbox endpoint:
https://api.sandbox.africastalking.com/version1/messaging
Set AT_SANDBOX=true and AT_USERNAME=sandbox to use the sandbox during development.
All recipients and statuses are logged to Laravel's default log channel. A RuntimeException is thrown on HTTP failure so the queued job retries automatically.
Audit Log Table
The migration creates mpesa_notification_logs with the following columns:
| Column | Description |
|---|---|
transaction_id |
M-Pesa transaction reference |
event_type |
payment_successful / payment_failed / stk_push_initiated |
amount / currency |
Settlement amount |
phone / payer_name |
Payer details |
channels |
JSON array of active channels |
delivery_status |
JSON map of channel to status |
notifiable_* |
Polymorphic link to User / model |
reference_id |
Internal order reference |
meta |
Extra callback metadata |
Testing
composer install vendor/bin/phpunit
The test suite uses Orchestra Testbench and Guzzle's MockHandler — no real API calls are made.
License
MIT