Search by

alphabalex / messaging-made-easy

alphabalex

A Laravel package for sending SMS, WhatsApp and OTP messages through one consistent API with Termii, Africa's Talking, Twilio, Nexmo (Vonage) and Infobip

Package info

gitlab.com/Alphabalex/messaging-made-easy

Issues

pkg:composer/alphabalex/messaging-made-easy

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-11-08 10:48 UTC

This package is auto-updated.

Last update: 2026-11-08 10:10:39 UTC


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 instanceof to 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.

WhatsApp

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

Adding a provider

  1. Create a driver extending AbstractMessagingDriver and implement MessagingDriverInterface, plus optional capability interfaces (SmsDriverInterface, WhatsAppDriverInterface, TokenDriverInterface, MessageHistoryDriverInterface, BalanceDriverInterface, SenderIdDriverInterface, NumberInsightDriverInterface).
  2. Add a webhook handler extending AbstractWebhookHandler if the provider posts delivery reports.
  3. Register both in MessagingRegistry.
  4. Add provider configuration in config/messaging.php.

Testing

composer test
composer phpstan

License

MIT. See LICENSE.