misaf / laravel-sms-gateway
Driver-based SMS gateway manager for Laravel.
Fund package maintenance!
Requires
- php: ^8.4
- guzzlehttp/guzzle: ^7.10
- illuminate/contracts: ^13.0
- illuminate/http: ^13.0
- illuminate/support: ^13.0
- spatie/laravel-package-tools: ^1.93
Requires (Dev)
- larastan/larastan: ^3.10
- laravel/boost: ^2.5
- laravel/pint: ^1.3
- mockery/mockery: ^1.6
- monorepo-php/monorepo: ^12.7
- nunomaduro/collision: ^8.9
- orchestra/testbench: ^11.1
- pestphp/pest: ^5.1
- pestphp/pest-plugin-arch: ^5.0
- pestphp/pest-plugin-laravel: ^5.0
- pestphp/pest-plugin-profanity: ^5.0
- pestphp/pest-plugin-type-coverage: ^5.0
- phpstan/extension-installer: ^1.4
Suggests
- misaf/laravel-sms-gateway-ghasedak: Adds the "ghasedak" driver, backed by the Ghasedak API (https://ghasedak.me)
- misaf/laravel-sms-gateway-ippanel: Adds the "ippanel" driver, backed by the IPPanel API (https://ippanel.com)
- misaf/laravel-sms-gateway-kavenegar: Adds the "kavenegar" driver, backed by the Kavenegar API (https://kavenegar.com)
- misaf/laravel-sms-gateway-magfa: Adds the "magfa" driver, backed by the Magfa API (https://magfa.com)
- misaf/laravel-sms-gateway-melipayamak: Adds the "melipayamak" driver, backed by the Melipayamak API (https://melipayamak.com)
- misaf/laravel-sms-gateway-messagebird: Adds the "messagebird" driver, backed by the MessageBird API (https://messagebird.com)
- misaf/laravel-sms-gateway-plivo: Adds the "plivo" driver, backed by the Plivo API (https://plivo.com)
- misaf/laravel-sms-gateway-smsir: Adds the "smsir" driver, backed by the SMS.ir API (https://sms.ir)
- misaf/laravel-sms-gateway-sunway: Adds the "sunway" driver, backed by the Sunway API (https://sunwaysms.com)
- misaf/laravel-sms-gateway-textlocal: Adds the "textlocal" driver, backed by the Textlocal API (https://textlocal.in)
- misaf/laravel-sms-gateway-twilio: Adds the "twilio" driver, backed by the Twilio API (https://twilio.com)
- misaf/laravel-sms-gateway-vonage: Adds the "vonage" driver, backed by the Vonage API (https://vonage.com)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-03 05:25:23 UTC
README
A driver-based SMS gateway manager for Laravel, with one installable package per provider. Payloads pass through untouched, so every provider's own API fields stay available, and every send attempt, success and failure dispatches an event.
Requires PHP 8.4+ and Laravel 13.
Installation
composer require misaf/laravel-sms-gateway
php artisan sms-gateway:install # or: vendor:publish --tag=sms-gateway-config
The core ships only the null driver, which sends nothing and returns a fake
successful response — fine for local and testing, but it delivers no messages.
For that, install one or more driver packages:
| Package | Driver | Provider |
|---|---|---|
misaf/laravel-sms-gateway-ghasedak |
ghasedak |
Ghasedak |
misaf/laravel-sms-gateway-ippanel |
ippanel |
IPPanel |
misaf/laravel-sms-gateway-kavenegar |
kavenegar |
Kavenegar |
misaf/laravel-sms-gateway-magfa |
magfa |
Magfa |
misaf/laravel-sms-gateway-melipayamak |
melipayamak |
Melipayamak |
misaf/laravel-sms-gateway-messagebird |
messagebird |
MessageBird |
misaf/laravel-sms-gateway-plivo |
plivo |
Plivo |
misaf/laravel-sms-gateway-smsir |
smsir |
SMS.ir |
misaf/laravel-sms-gateway-sunway |
sunway |
Sunway |
misaf/laravel-sms-gateway-textlocal |
textlocal |
Textlocal |
misaf/laravel-sms-gateway-twilio |
twilio |
Twilio |
misaf/laravel-sms-gateway-vonage |
vonage |
Vonage |
All service providers auto-register, and each driver registers itself on the core manager. See each driver's README for its credentials and options.
Usage
SMS_GATEWAY_DRIVER=ghasedak SMS_GATEWAY_GHASEDAK_API_KEY=your-api-key
use Misaf\LaravelSmsGateway\Facades\SmsGateway; $response = SmsGateway::driver()->send([ 'message' => 'Hello', 'receptor' => '09123456789', ]); SmsGateway::driver('kavenegar')->send($data); // a named driver SmsGateway::driver('ghasedak')->request()->post('sms/send/simple', $data); // the raw HTTP client
Drivers can coexist, and the payload goes straight to the provider, so use the
fields its API expects. The facade is the entry point: the container binds only
SmsGatewayManager and its sms-gateway alias — the SmsGateway contract is
deliberately not bound, so type-hinting it for injection will not resolve.
Events
Every HTTP driver dispatches four events from Misaf\LaravelSmsGateway\Events,
each carrying the driver name:
SmsSending— before the request leaves, with the payloadSmsSent— after a successful (2xx) response, with theRequestandResponseSmsSendFailed— after a failed (non-2xx) response, with theRequestandResponseSmsSendUnreachable— when the gateway was never reached (connection error or timeout), with theexception; there is no request or response to report
use Misaf\LaravelSmsGateway\Events\SmsSendFailed; final class ReportSmsGatewayFailure { public function handle(SmsSendFailed $event): void { logger()->error($event->driverName, [ 'status' => $event->response->status(), 'body' => $event->response->body(), ]); } }
The retry policy uses throw: false, so a rejected send does not raise —
SmsSendFailed is how you observe it. A connection error or timeout still
raises after the retries are spent, with SmsSendUnreachable dispatched just
before it surfaces.
Configuration
config/sms-gateway.php:
| Key | Env | Default |
|---|---|---|
default |
SMS_GATEWAY_DRIVER |
null |
defaults.server_timeout |
SMS_GATEWAY_SERVER_TIMEOUT |
5 |
defaults.client_timeout |
SMS_GATEWAY_CLIENT_TIMEOUT |
6 |
defaults.retry_times |
SMS_GATEWAY_RETRY_TIMES |
2 |
defaults.retry_sleep_milliseconds |
SMS_GATEWAY_RETRY_SLEEP_MILLISECONDS |
100 |
The client timeout sits one second above the connection timeout so a slow gateway loses the race. Only connection failures and gateway 5xx responses are retried; a rejected credential or a malformed payload fails on the first attempt.
That is the whole core configuration. The defaults.* values are the fallback
for custom drivers only — each first-party driver owns its own timeout.* and
retry.* keys with driver-specific environment variables (e.g.
SMS_GATEWAY_TWILIO_SERVER_TIMEOUT), so one gateway can be tuned without
touching the others. Endpoints and credentials belong to the driver packages,
where every credential key and every base_url is required and may not be empty:
a missing or empty value fails when the driver is resolved, rather than sending
an unauthenticated request or one to a relative URL.
Registering a custom driver
Extend Misaf\LaravelSmsGateway\Drivers\SmsGatewayDriver. The base class owns
the timeouts, the retry policy and the events; the driver supplies its name, its
authentication, and the call it makes. Constructor values take no defaults —
config is the only place a value is written down — and every one the driver
cannot work without is guarded with self::requireConfigured(), since a config
key that is present but empty passes Config::string().
namespace App\SmsGateways; use Illuminate\Http\Client\PendingRequest; use Illuminate\Http\Client\Response; use Misaf\LaravelSmsGateway\Drivers\SmsGatewayDriver; final class CustomDriver extends SmsGatewayDriver { public function __construct( string $baseUrl, private readonly string $token, int $serverTimeout, int $clientTimeout, int $retryTimes, int $retrySleepMilliseconds, ) { parent::__construct($baseUrl, $serverTimeout, $clientTimeout, $retryTimes, $retrySleepMilliseconds); } protected function driverName(): string { return 'custom'; } /** * @param array<string, mixed> $data */ protected function sendRequest(array $data): Response { return $this->request()->post('messages', $data); } protected function configure(PendingRequest $request): PendingRequest { return $request->withToken($this->token); } }
A driver that needs neither the shared retry policy nor the events may implement
Misaf\LaravelSmsGateway\Contracts\SmsGateway directly instead.
Register it from a service provider:
use Illuminate\Support\Facades\Config; SmsGateway::extend('custom', fn (): SmsGateway => new CustomDriver( baseUrl: Config::string('services.custom.base_url'), token: Config::string('services.custom.token'), serverTimeout: Config::integer('sms-gateway.defaults.server_timeout'), clientTimeout: Config::integer('sms-gateway.defaults.client_timeout'), retryTimes: Config::integer('sms-gateway.defaults.retry_times'), retrySleepMilliseconds: Config::integer('sms-gateway.defaults.retry_sleep_milliseconds'), ));
From a package service provider, defer the registration so provider discovery order cannot matter:
use Misaf\LaravelSmsGateway\SmsGatewayManager; $this->callAfterResolving( SmsGatewayManager::class, fn (SmsGatewayManager $manager) => $manager->extend('custom', $factory), );
The registration key is the name used by SmsGateway::driver('custom'); the
driver reads its own configuration and reports its own driverName() on the
events, so nothing is inferred from that key.
Contributing
This repository is a monorepo: the core package lives at the root, and every
driver lives in Drivers/laravel-sms-gateway-<driver> and is split out to its
own read-only repository on release. Open issues and pull requests here.
composer test # Pest composer analyse # PHPStan / Larastan composer format # Pint
Changelog
See CHANGELOG for what has changed recently.
License
MIT. See LICENSE.