mg-techlegend / laravel-notify-africa
Laravel Notify Africa is a lightweight Laravel package for sending SMS via the Notify Africa API. It provides a clean, expressive interface for single and bulk messaging, integrates with Laravel Notifications, and simplifies SMS delivery without dealing with raw HTTP requests.
Package info
github.com/mg-techlegend/laravel-notify-africa
pkg:composer/mg-techlegend/laravel-notify-africa
Fund package maintenance!
Requires
- php: ^8.4
- illuminate/contracts: ^11.0||^12.0||^13.0
- illuminate/http: ^11.0||^12.0||^13.0
- illuminate/notifications: ^11.0||^12.0||^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/boost: ^2.3
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^9.0.0||^10.0.0||^11.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
This package is auto-updated.
Last update: 2026-03-28 05:12:32 UTC
README
Send SMS through the Notify Africa HTTP API from Laravel apps. This package provides a small typed client, a fluent message builder, structured response objects, Laravel Notification channel support, and a facade—using Laravel’s HTTP client (no direct Guzzle usage in your code).
Installation
composer require mg-techlegend/laravel-notify-africa
Publish the configuration file:
php artisan vendor:publish --tag="laravel-notify-africa-config"
Laravel discovers the service provider and facade automatically from composer.json (extra.laravel.providers and extra.laravel.aliases). No manual registration is required in typical apps.
Set the following in your .env (values are read only through config/notify-africa.php):
| Variable | Description |
|---|---|
NOTIFY_AFRICA_API_TOKEN |
Bearer API token from Notify Africa |
NOTIFY_AFRICA_SENDER_ID |
Default sender ID (can be overridden per message) |
NOTIFY_AFRICA_BASE_URL |
Optional; default https://api.notify.africa |
NOTIFY_AFRICA_TIMEOUT |
Request timeout in seconds (default 10) |
NOTIFY_AFRICA_CONNECT_TIMEOUT |
Connect timeout in seconds (default 5) |
NOTIFY_AFRICA_HTTP_RETRY_ATTEMPTS |
Total HTTP attempts per call (default 1 = no retries); see HTTP retries |
NOTIFY_AFRICA_HTTP_RETRY_DELAY_MS |
Delay in milliseconds between retries (default 250) |
NOTIFY_AFRICA_DEFAULT_COUNTRY_CODE |
Optional; see Phone numbers |
Configuration
Published config: config/notify-africa.php.
api_token— required for real API calls (missing token throws when the client is built).sender_id— default sender; omit on the message object to use this value.http_retry_attempts/http_retry_delay_ms— optional resilient requests; see below.default_country_calling_code— digits only, no+(e.g.255). Used only for “local-looking” numbers; see below.
HTTP retries
When http_retry_attempts is greater than 1, the client retries only on connection failures and on HTTP 408, 425, 429, and 5xx responses. 4xx errors such as 401 and 422 are not retried.
Retries use Laravel’s HTTP client (throw: false on the pending request) so the last response is always parsed and mapped to the same package exceptions. Increasing retries can mean duplicate SMS if a request succeeds at the gateway but the response never reaches your server—keep attempts conservative unless you accept that trade-off.
Direct usage
Inject the entry service or use the facade:
use TechLegend\LaravelNotifyAfrica\Facades\LaravelNotifyAfrica; use TechLegend\LaravelNotifyAfrica\LaravelNotifyAfrica as NotifyAfrica; use TechLegend\LaravelNotifyAfrica\NotifyAfricaMessage; // Facade $response = LaravelNotifyAfrica::sendSms( LaravelNotifyAfrica::message() ->to('255689737459') ->content('Hello from Laravel!') // ->senderId('CUSTOM') // optional override ); // Container (class or string alias registered by the package) $notify = app(NotifyAfrica::class); // $notify = app('notify-africa'); $response = $notify->sendSms( NotifyAfricaMessage::make() ->to('255689737459') ->content('Hello!') );
$response is a SendSmsResponse with messageId, deliveryStatus (e.g. PROCESSING), and envelope metadata.
Bulk SMS
Uses the documented batch endpoint POST /api/v1/api/messages/batch (not a client-side loop):
use TechLegend\LaravelNotifyAfrica\Facades\LaravelNotifyAfrica; $response = LaravelNotifyAfrica::sendBulkSms( ['255763765548', '255689737839'], 'Same text for everyone', // optional third argument: sender ID override; otherwise config default is used ); // $response->messageCount, creditsDeducted, remainingBalance
Delivery status
$status = LaravelNotifyAfrica::getMessageStatus('156022'); // $status->status, $status->deliveredAt, etc.
Exceptions
| Exception | When |
|---|---|
NotifyAfricaAuthenticationException |
HTTP 401 / 403 |
NotifyAfricaValidationException |
HTTP 400 / 422, or JSON envelope status ≠ 200 with HTTP 200 |
NotifyAfricaRequestException |
Other failures, non-JSON responses, 5xx, connection issues |
All extend NotifyAfricaException and expose ?array $payload with the decoded JSON when available.
Local validation (empty phone, empty message, missing sender, invalid notification setup) throws InvalidArgumentException before any HTTP call. Many messages are prefixed with [Notify Africa] so logs are easy to filter.
Laravel notifications
Use the channel class in via() and implement toNotifyAfrica() on your notification. The notifiable must define routeNotificationForNotifyAfrica() returning the recipient number (string).
use Illuminate\Notifications\Notification; use TechLegend\LaravelNotifyAfrica\Channels\NotifyAfricaChannel; use TechLegend\LaravelNotifyAfrica\NotifyAfricaMessage; class OrderShippedSms extends Notification { public function via(object $notifiable): array { return [NotifyAfricaChannel::class]; } public function toNotifyAfrica(object $notifiable): NotifyAfricaMessage { return NotifyAfricaMessage::make() ->content('Your order has shipped.'); // Phone comes from routeNotificationForNotifyAfrica() when `to()` is omitted } }
On your notifiable (e.g. User model):
public function routeNotificationForNotifyAfrica(): string { return $this->phone; // e.g. 2557… (see below) }
If toNotifyAfrica() already sets ->to(...), that number is used; otherwise the channel applies the routed number.
Phone numbers
The API expects international format without a leading + (e.g. 255XXXXXXXXX). The package strips spaces and non-digits and removes a leading +.
If you set default_country_calling_code (e.g. 255) and the number looks “local” (9–10 digits after normalization), that prefix is prepended. This is a simple heuristic—not a substitute for libphonenumber or full validation. Prefer passing fully qualified international numbers in production.
Testing your app
In tests, use Laravel’s HTTP client fakes so no real SMS is sent:
use Illuminate\Support\Facades\Http; Http::fake([ 'https://api.notify.africa/api/v1/api/messages/send' => Http::response([ 'status' => 200, 'message' => 'SMS sent successfully', 'data' => ['messageId' => '1', 'status' => 'PROCESSING'], ], 200), ]);
If you change notify-africa config inside a test, clear resolved singletons or boot a fresh application before resolving NotifyAfricaClient, since it is registered as a singleton.
Testing this package
composer test composer analyse # PHPStan composer format # Pint
Assumptions (v1)
- Error semantics follow common HTTP usage (401/403 auth, 400/422 validation). If the live API differs, adjust mapping in
NotifyAfricaClient::mapFailure()and keep tests in sync. - Bulk sending uses the official batch API; behavior matches Notify Africa SMS API.
- No database tables, queues, webhooks, or logging persistence in v1.
Changelog
See CHANGELOG.md.
Contributing
See CONTRIBUTING.md.
Security
Please review our security policy on how to report security vulnerabilities.
Credits
Notify Africa and iPF Softwares
The SMS API behind this package is Notify Africa, built and operated by iPF Softwares. Documentation: SMS API. Official PHP client (separate from this Laravel package): notify-africa-php.
License
The MIT License. See LICENSE.md.