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.

Maintainers

Package info

github.com/mg-techlegend/laravel-notify-africa

pkg:composer/mg-techlegend/laravel-notify-africa

Fund package maintenance!

TechLegend

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 1

v1.0.0 2026-03-21 10:21 UTC

This package is auto-updated.

Last update: 2026-03-28 05:12:32 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

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.