misaf/laravel-sms-gateway
Driver-based SMS gateway manager for Laravel with separate first-party packages for providers like Twilio, Vonage, Kavenegar, and more. Switch drivers per request, use Laravel’s HTTP client, listen for sent events, and register custom drivers.
Install the core plus exactly one driver. The core package ships no providers, so composer require misaf/laravel-sms-gateway alone gets you a manager that throws on first send.
composer require misaf/laravel-sms-gateway
composer require misaf/laravel-sms-gateway-kavenegar # pick one
php artisan vendor:publish --tag=sms-gateway-config
Where to look first: config/sms-gateway.php. It holds the default driver name and a drivers map with per-provider credentials. Read the driver package's README next — the core README only tells you which packages exist, not the method signatures, and driver-level quirks (sender numbers, template/pattern IDs, line numbers) live there.
First use case: an OTP send. Set the default driver from env, put credentials in .env, then send from a controller or, better, a queued job:
// .env
SMS_GATEWAY_DRIVER=kavenegar
KAVENEGAR_API_KEY=...
// anywhere
$response = SmsGateway::driver()->send($user->phone, 'Your code is 123456');
Two things to confirm before writing app code: (1) the exact manager method name (send / sendMessage / a message object) and (2) the return type — read the driver's source, since it's a one-star package and the README is a feature list rather than an API reference. Nail both down with a tinker session and a real send to your own number before building around it.
Hard gate: PHP 8.3+ and Laravel 13+. This will not install on a Laravel 10/11 app without conflict, so check composer why-not early — that alone rules the package out for most existing projects.
Keep the default driver in config, override per call. The advertised feature is "switch drivers per request," which is useful for failover and for multi-region/multi-tenant apps where different brands have different panel contracts. Prefer:
$driver = config("sms.tenants.{$tenant->id}.driver", config('sms-gateway.default'));
SmsGateway::driver($driver)->send($to, $body);
Don't sprinkle driver('x') literals through controllers — resolve from config or a SmsSender service so a panel migration is a one-line change.
Wrap it in your own small interface. Given the maturity signals (1 star, 0 dependents, a version bump that will track Laravel majors), the pragmatic pattern is to bind your own SmsSender contract and have one adapter class own the package calls, the phone normalization, and the error mapping. If the package stalls, you swap the adapter and nothing else moves.
final class GatewaySmsSender implements SmsSender
{
public function send(string $to, string $body): void { /* SmsGateway::driver(...)->... */ }
}
Send from a queued job, never inline. Every provider call is an outbound HTTP request with a multi-second timeout. A SendSms job with $tries = 3 and backoff() gives you retries the package almost certainly doesn't provide itself.
Use the events for the audit trail. The package emits SMS-sent events carrying request and response payloads. Register a listener that persists them (driver, recipient, body hash, provider message id, cost) — that's your deliverability debugging and your billing reconciliation. Also log failures there, since a thrown exception on the send path should be caught in the job, not bubble to the user.
Register a log / null driver for local and CI. The custom-driver registration hook is the most valuable extension point in the package. Implement a driver that writes to the log and returns a fake response, register it under local, and set SMS_GATEWAY_DRIVER=log in .env.testing. This keeps your test suite off the network and off your real SMS balance.
Laravel HTTP client access means the underlying calls go through Http:: — so Http::fake() in tests and a global Http::macro for correlation IDs / proxy config will apply to driver traffic. Use that for timeouts and retry middleware rather than trusting per-driver defaults.
SMS_GATEWAY_DRIVER=plivo and didn't install the Plivo package, you get a resolution error, not a helpful message. Add the driver package to composer.json in the same commit that sets the env var.config:cache will bite you if you read credentials from env inside the config after caching in a deploy script that orders steps wrong. Standard Laravel rule, but more painful here because a stale cache means sends silently go to the wrong panel or fail auth.09xxxxxxxxx or 989xxxxxxxxx depending on provider; MessageBird/Plivo want E.164. Normalize at the boundary of your adapter, not at each call site.Redis::throttle() in the job's middleware, keyed per driver."misaf/laravel-sms-gateway-kavenegar": "^1.0") and pin the core too — with zero dependents, minor releases get limited real-world testing.SmsSender interface makes that fork a contained, boring job instead of a refactor.How can I help you explore Laravel packages today?