alphabalex / messaging-made-easy
A Laravel package for sending SMS, WhatsApp and OTP messages through one consistent API with Termii, Africa's Talking, Twilio, Nexmo (Vonage) and Infobip
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.8.2|^8.0
- illuminate/http: ^8.83|^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^8.83|^9.0|^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^6.24|^7.0|^8.0|^9.0|^10.0|^11.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^9.5.10|^10.5|^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A Laravel package for sending SMS, WhatsApp and one-time passwords through one consistent API. Termii, Africa's Talking, Twilio, Nexmo (Vonage) and Infobip are supported, and the driver/registry design makes adding more providers straightforward.
- SMS — single or bulk via Termii, Africa's Talking, Twilio, Nexmo or Infobip; Termii adds promotional (
generic) or transactional (dnd) channels, plain/unicode/encrypted messages and voice. - WhatsApp — conversational messages, media, and pre-approved templates with variables via Termii, Africa's Talking (preview), Twilio, Nexmo or Infobip.
- OTP tokens — generate, send and verify one-time passwords (Termii).
- Delivery reports — provider webhook route, normalized events, optional idempotency.
- Account tools — wallet balance, message history, sender IDs, DND and network insight (Termii).
- Capability interfaces — use
instanceofto check what a provider supports.
Requirements
- PHP 8.1+
- Laravel 8.83–13 (Laravel 13 requires PHP 8.3+)
Install
composer require alphabalex/messaging-made-easy
Publish the configuration:
php artisan vendor:publish --tag=messaging-config
Add your Termii credentials to .env:
MESSAGING_PROVIDER=termii
TERMII_API_KEY=your-api-key
TERMII_SENDER_ID=YourSender
TERMII_WHATSAPP_DEVICE_ID=your-device-id
TERMII_WEBHOOK_SECRET=your-webhook-secret
Or use Africa's Talking for SMS:
MESSAGING_PROVIDER=africastalking
AFRICASTALKING_API_KEY=your-api-key
AFRICASTALKING_USERNAME=sandbox
AFRICASTALKING_BASE_URL=https://api.sandbox.africastalking.com
AFRICASTALKING_SENDER_ID=YourSender
AFRICASTALKING_WHATSAPP_NUMBER=+254700000000
AFRICASTALKING_WEBHOOK_TOKEN=a-long-random-token
Switch the username and base URL to your production app credentials when going live. Africa's Talking WhatsApp is a preview product and currently has no sandbox.
Or use Twilio for SMS and WhatsApp:
MESSAGING_PROVIDER=twilio
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your-auth-token
TWILIO_FROM=+14155550123
TWILIO_WHATSAPP_FROM=whatsapp:+14155238886
TWILIO_STATUS_CALLBACK=https://your-app.com/webhooks/messaging/twilio
Or use Nexmo (now Vonage) for SMS and WhatsApp:
MESSAGING_PROVIDER=nexmo
NEXMO_API_KEY=your-api-key
NEXMO_API_SECRET=your-api-secret
NEXMO_FROM=Acme
NEXMO_WHATSAPP_FROM=14155238886
NEXMO_STATUS_CALLBACK=https://your-app.com/webhooks/messaging/nexmo
NEXMO_SIGNATURE_SECRET=your-signature-secret
VONAGE_API_KEY, VONAGE_API_SECRET and the other VONAGE_* aliases are also accepted.
Quick start
SMS
use Alphabalex\MessagingMadeEasy\Facades\Messaging;
Messaging::driver('termii')->sendSms([
'to' => '08031234567', // normalized to 2348031234567
'message' => 'Your order has shipped.',
'channel' => 'dnd', // dnd for transactional, generic for promotional
]);
Bulk SMS
Messaging::driver('termii')->sendBulkSms([
'to' => ['08031234567', '09012345678'], // up to 100 recipients
'message' => 'Black Friday starts now.',
'channel' => 'generic',
]);
Africa's Talking uses the same sendSms() / sendBulkSms() API:
Messaging::driver('africastalking')->sendSms([
'to' => '08031234567',
'message' => 'Your order has shipped.',
'from' => 'YourSender', // optional; defaults to AFRICASTKNG
]);
Africa's Talking WhatsApp:
Messaging::driver('africastalking')->sendWhatsAppMessage([
'to' => '+254712345678',
'message' => 'Your order has shipped.',
]);
Messaging::driver('africastalking')->sendWhatsAppTemplate([
'to' => '+254712345678',
'template_id' => 'approved-template-id',
'data' => ['Ada', 'order 42'],
]);
The WhatsApp sender defaults to AFRICASTALKING_WHATSAPP_NUMBER; pass wa_number to override it per call.
Twilio uses the same methods with whatsapp: addresses handled for you:
Messaging::driver('twilio')->sendSms([
'to' => '08031234567',
'message' => 'Your order has shipped.',
]);
Messaging::driver('twilio')->sendWhatsAppMessage([
'to' => '08031234567',
'message' => 'Your order has shipped.',
]);
Messaging::driver('twilio')->sendWhatsAppTemplate([
'to' => '08031234567',
'content_sid' => 'HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'data' => ['1' => 'Ada', '2' => 'order 42'],
]);
Twilio templates use a Content Template SID; see the Twilio guide.
Nexmo uses its SMS API and the Vonage Messages API:
Messaging::driver('nexmo')->sendSms([
'to' => '08031234567',
'message' => 'Your order has shipped.',
]);
Messaging::driver('nexmo')->sendWhatsAppMessage([
'to' => '08031234567',
'message' => 'Your order has shipped.',
]);
Messaging::driver('nexmo')->sendWhatsAppTemplate([
'to' => '08031234567',
'template_name' => 'order_update',
'parameters' => ['Ada', 'order 42'],
]);
Nexmo templates use a template name (optionally namespace-prefixed); see the Nexmo guide.
Messaging::driver('termii')->sendWhatsAppMessage([
'to' => '08031234567',
'message' => 'Here is your receipt.',
'media' => [
'url' => 'https://example.com/receipt.pdf',
'caption' => 'Receipt',
],
]);
WhatsApp template
Messaging::driver('termii')->sendWhatsAppTemplate([
'phone_number' => '08031234567',
'template_id' => 'your-approved-template-id',
'data' => ['1' => 'Ada', '2' => 'order 42'],
]);
One-time password
$token = Messaging::driver('termii')->sendToken([
'to' => '08031234567',
'pin_length' => 6,
]);
// Store $token['pin_id'] with the user, then verify the code they enter.
$result = Messaging::driver('termii')->verifyToken($token['pin_id'], $request->code);
sendToken() defaults to the dnd channel, 6 digits, 3 attempts and a 10-minute lifetime.
Delivery reports
The package registers POST /webhooks/messaging/{provider}. Register these URLs in the provider dashboards:
https://your-app.com/webhooks/messaging/termii
https://your-app.com/webhooks/messaging/africastalking?token=your-webhook-token
https://your-app.com/webhooks/messaging/twilio
https://your-app.com/webhooks/messaging/nexmo
Termii signs the raw payload with HMAC SHA512 in X-Termii-Signature. The package verifies it using TERMII_WEBHOOK_SECRET, falling back to TERMII_API_KEY.
Africa's Talking does not provide a documented signature for delivery reports, so set AFRICASTALKING_WEBHOOK_TOKEN and append ?token=... to the callback URL (or send it in X-Webhook-Token). Without a token the endpoint accepts unsigned callbacks. Africa's Talking delivery reports here cover SMS; WhatsApp status callbacks are not handled.
Twilio signs status and inbound webhooks with X-Twilio-Signature, verified using TWILIO_AUTH_TOKEN. The same route handles Twilio SMS and WhatsApp statuses plus inbound messages.
Nexmo SMS delivery receipts can be verified with the optional sig parameter, and Messages API callbacks with a signed Bearer JWT. Set NEXMO_SIGNATURE_SECRET to enforce verification; the route accepts both SMS and WhatsApp status/inbound payloads.
Listen for normalized events:
use Alphabalex\MessagingMadeEasy\Events\MessageDelivered;
use Alphabalex\MessagingMadeEasy\Events\MessageFailed;
use Illuminate\Support\Facades\Event;
Event::listen(MessageDelivered::class, function (MessageDelivered $event) {
// $event->data['message_id'], $event->data['receiver'], ...
});
Event::listen(MessageFailed::class, function (MessageFailed $event) {
// $event->reason, $event->data
});
Available events: MessageSent, MessageDelivered, MessageRead, MessageFailed, MessageReceived, DeviceStatusChanged.
Documentation
- Installation
- Configuration
- SMS and WhatsApp
- OTP tokens
- Webhooks and events
- Termii provider guide
- Africa's Talking provider guide
- Twilio provider guide
- Nexmo (Vonage) provider guide
- Infobip provider guide
- API reference
Adding a provider
- Create a driver extending
AbstractMessagingDriverand implementMessagingDriverInterface, plus optional capability interfaces (SmsDriverInterface,WhatsAppDriverInterface,TokenDriverInterface,MessageHistoryDriverInterface,BalanceDriverInterface,SenderIdDriverInterface,NumberInsightDriverInterface). - Add a webhook handler extending
AbstractWebhookHandlerif the provider posts delivery reports. - Register both in
MessagingRegistry. - Add provider configuration in
config/messaging.php.
Testing
composer test
composer phpstan
License
MIT. See LICENSE.