mortezamasumi / fb-sms
SMS notification channel for Laravel & Filament with pluggable operators (Fake, Sabanovin, Smsir).
Requires
- php: ^8.5
- ipe/smsir-php: ^1.0
- spatie/laravel-package-tools: ^1.0
Requires (Dev)
- filament/filament: ^5.0
- filament/upgrade: ^5.0
- larastan/larastan: ^3.10
- laravel/pint: ^1.30
- orchestra/testbench: ^10.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-browser: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- pestphp/pest-plugin-livewire: ^4.0
- phpstan/phpstan: ^2.2
README
A Laravel notification channel that sends SMS through pluggable operators. It ships with the Sabanovin and Smsir (sms.ir) operators, plus a Fake operator for local development and testing.
Features
- Drop-in Laravel channel — send via the standard
Notification::route('sms', ...)->notify(...)flow - Pluggable operators — swap providers through config without touching your notification classes
- Built-in operators —
Sabanovin,Smsir, and aFakelogger for local development - Prepend/append text — configure a prefix/suffix (e.g. an opt-out line) applied to every message
- Recipient fallback — uses
routeNotificationFor('sms', $notification), then$notifiable->mobile - Lifecycle hooks —
beforeSend(),send(),afterSend(), plussucceeded()/failed()on notifications - Graceful failure — send/credit errors are caught, logged, and reported to your notification's
failed()
Installation
composer require mortezamasumi/fb-sms
Publish the config file:
php artisan vendor:publish --tag="fb-sms-config"
Configuration
// config/fb-sms.php return [ // Sabanovin API domain (without scheme) 'sabanovin_domain' => env('SABANOVIN_DOMAIN', 'api.sabanovin.com'), // Sabanovin reports the balance in Toman; multiplied to convert to Rial (1 Toman = 10 Rial) 'sabanovin_balance_multiplier' => env('SABANOVIN_BALANCE_MULTIPLIER', 10), // Active operator class — one of the bundled operators or your own 'operator' => env('SMS_CHANNEL_OPERATOR', \Mortezamasumi\FbSms\Operators\Fake::class), // API key/username used by Sabanovin and Smsir 'api_key' => env('SMS_CHANNEL_API_KEY', 'key'), // SMS line/number used as the sender gateway 'gateway' => env('SMS_CHANNEL_GATEWAY', 'gateway'), // Fixed receiver, overrides the notifiable's phone when set 'receiver' => env('SMS_CHANNEL_RECEIVER', null), // Optional prefix/suffix applied to every message 'prepend_text' => env('SMS_CHANNEL_PREPEND_TEXT', ''), 'append_text' => env('SMS_CHANNEL_APPEND_TEXT', "\n\n\n لغو ۱۱"), ];
Usage
Create a notification and send it through the sms channel:
use Illuminate\Notifications\Notification; class InvoicePaid extends Notification { public function via(object $notifiable): array { return ['sms']; } public function toSms(object $notifiable): string { return "Your invoice was paid. Amount: {$notifiable->amount}"; } }
Send it to a notifiable model that exposes a phone number (either mobile or routeNotificationFor('sms', ...)):
$user->notify(new InvoicePaid());
Or send to a phone number directly:
use Illuminate\Support\Facades\Notification; Notification::route('sms', '09121234567')->notify(new InvoicePaid());
Recipient resolution
$notifiable->routeNotificationFor('sms', $notification)if it exists, then$notifiable->mobileif it is set, then- the
fb-sms.receiverconfig value, or - the route value passed to
Notification::route('sms', ...).
Success and failure hooks
class InvoicePaid extends Notification { public function succeeded($operator): void { // $operator->getCode() / getMessage() hold the provider response } public function failed(\Throwable $exception): void { // log, alert, retry... } }
Credit lookup
use Mortezamasumi\FbSms\Facades\FbSms; FbSms::credit(); // '125000' — 'N/A' when the provider is unreachable
Custom operators
Implement your own provider by extending Mortezamasumi\FbSms\Contracts\Operator and implement send() and credit():
use Mortezamasumi\FbSms\Contracts\Operator; class MyOperator extends Operator { public function send(): void { // $this->getText() — message body // $this->getTo() — recipient(s) // $this->getGateway()— sender line // $this->setCode('1'); $this->setMessage('ok'); } public function credit(): string { return '10000'; } }
Register it via config:
'operator' => env('SMS_CHANNEL_OPERATOR', App\Sms\MyOperator::class),
If your operator needs app-level setup, override public static function initialize(FbSmsServiceProvider $provider): void — it runs once before the operator is first used.
Support policy
| PHP | Laravel |
|---|---|
| 8.3 | 12 |
Testing
composer test
The test suite covers operator responses, recipient resolution, lifecycle hooks, and failure paths using Http::fake() and the bundled Fake operator — no real SMS is sent.
Contributing
Please see CONTRIBUTING for details.
Security
If you discover a security vulnerability, please review our security policy on how to report it.
Changelog
Please see CHANGELOG for recent changes.
License
The MIT License (MIT). See LICENSE.md for details.