softlandtech / notify
Laravel notification channels for Email, WhatsApp (Facebook Graph API) and SMS (Taqnyat)
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.0
- illuminate/http: ^11.0|^12.0
- illuminate/notifications: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: ^9.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
This package is auto-updated.
Last update: 2026-05-17 04:52:35 UTC
README
A powerful Laravel package that extends Laravel's notification system with advanced features for Email, SMS (Taqnyat), and WhatsApp channels.
Features
- Universal Channel Support: Works with any Laravel notification channel from laravel-notification-channels.com (100+ channels available)
- Built-in Channels: Email (Mail), SMS (Taqnyat), WhatsApp Business API
- Flexible Delivery Strategies: Send to all channels or use round-robin fallback
- Channel Priority/Ordering: Define the order in which channels are attempted
- Retry Logic: Automatic retry with exponential or linear backoff
- Rate Limiting: Prevent notification spam
- User Preferences: Allow users to choose their preferred channels
- Sandbox Mode: Test notifications without actually sending them
- Events System: Monitor notification lifecycle with dispatchable events
- Notification History: Log all sent notifications to database
- Channel Health Monitoring: Check channel configuration and API connectivity
- Conditional Channel Rules: Custom logic to determine when each channel should be used
- Template Management: Named WhatsApp templates with helper methods
- Localization Support: Multi-language notification support
- Testing Helpers: Sandbox assertions for easy testing
Installation
Step 1: Install the Package
composer require softlandtech/notify
Step 2: Publish Configuration
php artisan vendor:publish --tag=notify-config
Step 3: (Optional) Publish Migrations
If you want to use notification history:
php artisan vendor:publish --tag=notify-migrations php artisan migrate
Configuration
The package is configured via config/notify.php and environment variables.
Environment Variables
# Notification Strategy NOTIFICATION_STRATEGY=all # or 'round_robin' # Channels to use (comma-separated) NOTIFICATION_CHANNELS=mail,sms,whatsapp # Channel Order NOTIFICATION_CHANNEL_ORDER=mail,sms,whatsapp # Sandbox Mode NOTIFICATION_SANDBOX=false # Retry Configuration NOTIFICATION_RETRY_ENABLED=false NOTIFICATION_RETRY_ATTEMPTS=3 NOTIFICATION_RETRY_BACKOFF=exponential # or 'linear' NOTIFICATION_RETRY_DELAY=1000 # Rate Limiting NOTIFICATION_RATE_LIMIT_ENABLED=true NOTIFICATION_RATE_LIMIT_MAX=5 NOTIFICATION_RATE_LIMIT_DECAY=1 # Notification History NOTIFICATION_HISTORY_ENABLED=false NOTIFICATION_HISTORY_PRUNE_DAYS=30 # User Preferences NOTIFICATION_USER_PREFERENCES_ENABLED=false # Events NOTIFICATION_EVENTS_ENABLED=true # Taqnyat SMS TAQNYAT_BASE_URL=https://api.taqnyat.sa TAQNYAT_TOKEN=your-token TAQNYAT_SENDER=your-sender # WhatsApp WHATSAPP_API_VERSION=v22.0 WHATSAPP_PHONE_NUMBER_ID=your-phone-number-id WHATSAPP_TOKEN=your-access-token WHATSAPP_TEMPLATE_NAME=your-default-template WHATSAPP_TEMPLATE_LANGUAGE=en_US WHATSAPP_DEFAULT_LANGUAGE=ar # WhatsApp Templates WHATSAPP_TEMPLATE_OTP=otp_template WHATSAPP_TEMPLATE_VERIFICATION=verification_template WHATSAPP_TEMPLATE_PASSWORD_RESET=password_reset_template
Also configure channels in config/notifications.php:
'channels' => array_filter(array_map('trim', explode(',', env('NOTIFICATION_CHANNELS', 'mail')))), 'channel_mapping' => [ 'mail' => \SoftLandTech\Notify\Channels\MailChannel::class, 'sms' => \SoftLandTech\Notify\Channels\TaqnyatChannel::class, 'whatsapp' => \SoftLandTech\Notify\Channels\WhatsappChannel::class, ],
Usage
Basic Notification
Create a notification that extends the package's base class:
<?php namespace App\Notifications; use SoftLandTech\Notify\Notification; use SoftLandTech\Notify\Contracts\MailNotification; use SoftLandTech\Notify\Contracts\SmsNotification; use SoftLandTech\Notify\Contracts\WhatsAppNotification; class WelcomeNotification extends Notification implements MailNotification, SmsNotification, WhatsAppNotification { public function toMail(object $notifiable): array { return [ 'view' => 'emails.welcome', 'subject' => 'Welcome to Our App', 'data' => ['user' => $notifiable], ]; } public function toSms(object $notifiable): string { return 'Welcome to our app!'; } public function toWhatsapp(object $notifiable): array { return [ 'template_name' => 'welcome_template', 'language' => 'ar', 'parameters' => [$notifiable->name], ]; } }
Important: No need to define the via() method! The package handles channel routing automatically based on configuration.
Sending Notifications
$user->notify(new WelcomeNotification());
Advanced Features
1. Delivery Strategies
Send to All Channels (Default)
NOTIFICATION_STRATEGY=all
All configured channels will receive the notification, regardless of success/failure.
Round-Robin with Fallback
NOTIFICATION_STRATEGY=round_robin
Channels are tried in order. The first successful send stops the process.
You can also override per notification:
class UrgentNotification extends Notification { protected function getStrategy(): string { return 'round_robin'; // Try channels until one succeeds } }
2. Channel Priority/Ordering
Define the order in which channels are attempted:
NOTIFICATION_CHANNEL_ORDER=whatsapp,sms,mail
Or override per notification:
class SmsFirstNotification extends Notification { protected function getChannelOrder(): array { return ['sms', 'whatsapp', 'mail']; } }
3. Retry Logic
Enable automatic retries for failed notifications:
NOTIFICATION_RETRY_ENABLED=true NOTIFICATION_RETRY_ATTEMPTS=3 NOTIFICATION_RETRY_BACKOFF=exponential # or 'linear' NOTIFICATION_RETRY_DELAY=1000 # milliseconds
Or customize per notification:
class ImportantNotification extends Notification { protected function shouldRetry(): bool { return true; } protected function getRetryConfig(): array { return [ 'attempts' => 5, 'backoff' => 'exponential', 'delay' => 2000, ]; } }
4. Rate Limiting
Prevent notification spam:
NOTIFICATION_RATE_LIMIT_ENABLED=true NOTIFICATION_RATE_LIMIT_MAX=5 # Max 5 notifications NOTIFICATION_RATE_LIMIT_DECAY=1 # Per minute
Override per notification:
class LimitedNotification extends Notification { protected function shouldRateLimit(): bool { return true; } protected function getRateLimitConfig(): array { return [ 'max_attempts' => 3, 'decay_minutes' => 5, ]; } }
5. User Channel Preferences
Allow users to control which channels they receive notifications on:
NOTIFICATION_USER_PREFERENCES_ENABLED=true
Add a notification_preferences attribute to your User model:
// User model protected $casts = [ 'notification_preferences' => 'array', ]; // Set user preferences $user->notification_preferences = ['mail', 'whatsapp']; $user->save();
6. Sandbox Mode
Test notifications without sending them:
NOTIFICATION_SANDBOX=true
use SoftLandTech\Notify\Channels\SandboxChannel; // In tests SandboxChannel::assertSent(WelcomeNotification::class); SandboxChannel::assertSent(WelcomeNotification::class, function ($notification, $notifiable) use ($user) { return $notifiable->id === $user->id; }); SandboxChannel::assertNotSent(SpamNotification::class); SandboxChannel::clear(); // Clear sandbox history
7. Events System
Listen to notification events:
use SoftLandTech\Notify\Events\NotificationSending; use SoftLandTech\Notify\Events\NotificationSent; use SoftLandTech\Notify\Events\NotificationFailed; use SoftLandTech\Notify\Events\ChannelSkipped; use SoftLandTech\Notify\Events\RoundRobinFallback; Event::listen(NotificationSent::class, function ($event) { Log::info('Notification sent', [ 'notification' => get_class($event->notification), 'channel' => $event->channel, ]); });
8. Notification History
Enable database logging of all notifications:
NOTIFICATION_HISTORY_ENABLED=true
php artisan vendor:publish --tag=notify-migrations php artisan migrate
Query notification history:
use SoftLandTech\Notify\Models\NotificationHistory; // Get all sent notifications for a user $history = NotificationHistory::where('notifiable_type', User::class) ->where('notifiable_id', $user->id) ->where('status', 'sent') ->get(); // Get failed notifications $failed = NotificationHistory::where('status', 'failed')->get();
9. Channel Health Monitoring
Check if your notification channels are properly configured:
# Check all channels php artisan notifications:check-health # Check specific channel php artisan notifications:check-health mail php artisan notifications:check-health sms php artisan notifications:check-health whatsapp
Programmatically:
use SoftLandTech\Notify\ChannelHealthMonitor; $monitor = app(ChannelHealthMonitor::class); $results = $monitor->checkAll(); // ['mail' => [...], 'sms' => [...], 'whatsapp' => [...]] $mailHealth = $monitor->checkMail();
10. Conditional Channel Rules
Implement custom logic to determine when each channel should be used:
class PremiumNotification extends Notification { protected function shouldUseChannel(string $channelName, object $notifiable): bool { // Only use WhatsApp for premium users if ($channelName === 'whatsapp') { return $notifiable->isPremium(); } // Only send emails during business hours if ($channelName === 'mail') { return now()->hour >= 9 && now()->hour < 17; } return true; } }
11. Template Management
Use the template management trait for WhatsApp notifications:
use SoftLandTech\Notify\Notification; use SoftLandTech\Notify\Concerns\HasTemplates; use SoftLandTech\Notify\Contracts\WhatsAppNotification; class OtpNotification extends Notification implements WhatsAppNotification { use HasTemplates; public function __construct(public string $code) {} public function toWhatsapp(object $notifiable): array { // Helper method for OTP templates return $this->buildOtpTemplate($this->code, 5); } }
Available template helpers:
$this->buildOtpTemplate($code, $expiryMinutes); $this->buildVerificationTemplate($code); $this->buildPasswordResetTemplate($code, $expiryMinutes); $this->buildTemplate('custom_template', ['param1', 'param2'], 'ar'); $this->getTemplate('otp'); // Gets template name from config
12. Localization Support
Use the localization trait for multi-language support:
use SoftLandTech\Notify\Notification; use SoftLandTech\Notify\Concerns\Localizable; use SoftLandTech\Notify\Contracts\MailNotification; class LocalizedNotification extends Notification implements MailNotification { use Localizable; public function toMail(object $notifiable): array { return $this->withLocale($notifiable, function () use ($notifiable) { return [ 'view' => 'emails.welcome', 'subject' => $this->trans('notifications.welcome.subject', [], $notifiable), 'data' => [ 'message' => $this->trans('notifications.welcome.message', ['name' => $notifiable->name]), ], ]; }); } }
The trait will automatically detect the user's locale from:
$notifiable->localeattribute$notifiable->preferredLocale()method- Application default locale
Adding Community Notification Channels
The package supports any Laravel notification channel from laravel-notification-channels.com. All features (retry, rate limiting, sandbox mode, events, etc.) automatically work with any channel!
Example: Adding Slack
- Install the Slack channel:
composer require laravel/slack-notification-channel
- Add to
config/notifications.php:
'channels' => array_filter(array_map('trim', explode(',', env('NOTIFICATION_CHANNELS', 'mail,slack')))), 'channel_mapping' => [ 'mail' => \SoftLandTech\Notify\Channels\MailChannel::class, 'sms' => \SoftLandTech\Notify\Channels\TaqnyatChannel::class, 'whatsapp' => \SoftLandTech\Notify\Channels\WhatsappChannel::class, 'slack' => \Illuminate\Notifications\Slack\SlackChannel::class, // Add this ],
- Update your
.env:
NOTIFICATION_CHANNELS=mail,slack SLACK_WEBHOOK_URL=your-webhook-url
- Use in notifications:
use SoftLandTech\Notify\Notification; use SoftLandTech\Notify\Contracts\MailNotification; use Illuminate\Notifications\Slack\SlackMessage; class OrderShipped extends Notification implements MailNotification { public function toMail(object $notifiable): array { return [ 'view' => 'emails.order-shipped', 'subject' => 'Your order has shipped!', ]; } public function toSlack(object $notifiable): SlackMessage { return (new SlackMessage) ->content('Your order has shipped!'); } } // Add routing in your User model public function routeNotificationFor(string $channel): mixed { return match($channel) { 'mail' => $this->email, 'slack' => env('SLACK_WEBHOOK_URL'), 'sms', 'whatsapp' => $this->phone, default => null, }; }
That's it! Slack notifications now have retry logic, rate limiting, sandbox mode, and all other features.
More Channel Examples
Discord
composer require laravel-notification-channels/discord
'discord' => \NotificationChannels\Discord\DiscordChannel::class,
Telegram
composer require laravel-notification-channels/telegram
'telegram' => \NotificationChannels\Telegram\TelegramChannel::class,
Vonage (SMS)
composer require laravel/vonage-notification-channel
'vonage' => \Illuminate\Notifications\VonageChannel::class,
Twilio
composer require laravel-notification-channels/twilio
'twilio' => \NotificationChannels\Twilio\TwilioChannel::class,
Firebase (Push Notifications)
composer require laravel-notification-channels/fcm
'fcm' => \NotificationChannels\Fcm\FcmChannel::class,
Browse All Channels
Visit laravel-notification-channels.com to see 100+ available channels including:
- Social: Facebook, Twitter, LinkedIn
- Messaging: Discord, Telegram, Slack, Microsoft Teams
- SMS: Twilio, Nexmo, Vonage, MessageBird
- Push: Firebase, OneSignal, Pusher
- And many more!
Facades
The package provides convenient facades:
use Taqnyat; use Whatsapp; // Send SMS via Taqnyat Taqnyat::sendSms('966500000000', 'Your OTP is 123456'); // Send WhatsApp template Whatsapp::sendTemplate('966500000000', [ 'template_name' => 'otp', 'language' => 'ar', 'parameters' => ['123456', '5'], ]);
Testing
Unit Tests
vendor/bin/pest
Using Sandbox Mode in Tests
use SoftLandTech\Notify\Channels\SandboxChannel; class NotificationTest extends TestCase { protected function setUp(): void { parent::setUp(); config(['notify.sandbox' => true]); SandboxChannel::clear(); } public function test_welcome_notification_is_sent() { $user = User::factory()->create(); $user->notify(new WelcomeNotification()); SandboxChannel::assertSent(WelcomeNotification::class, function ($notification, $notifiable) use ($user) { return $notifiable->id === $user->id; }); } }
Customization
Custom Notification Stub
The package modifies the make:notification stub to automatically extend the package's base class:
php artisan make:notification MyNotification
This will generate:
<?php namespace App\Notifications; use SoftLandTech\Notify\Notification; use Illuminate\Bus\Queueable; class MyNotification extends Notification { use Queueable; // No via() method needed! }
Events Reference
| Event | Properties | Description |
|---|---|---|
NotificationSending |
$notification, $notifiable, $channel |
Fired before sending |
NotificationSent |
$notification, $notifiable, $channel, $response |
Fired after successful send |
NotificationFailed |
$notification, $notifiable, $channel, $exception |
Fired on failure |
ChannelSkipped |
$notification, $notifiable, $channel, $reason |
Fired when a channel is skipped |
RoundRobinFallback |
$notification, $notifiable, $failedChannel, $nextChannel, $exception |
Fired during round-robin fallback |
Architecture
The package uses the Decorator pattern to layer features around any Laravel notification channel:
SandboxChannel (if enabled)
└─ RateLimitedChannel (if enabled)
└─ RetryableChannel (if enabled)
└─ Any Channel (MailChannel, SlackChannel, DiscordChannel, etc.)
This architecture ensures that:
- All features (retry, rate limiting, sandbox, events, history) work with any Laravel notification channel
- Features can be enabled/disabled independently
- New channels can be added instantly without code changes
- The decorator layers are applied automatically to all channels
Simply add any community channel to your channel_mapping config and all features will work immediately!
License
This package is proprietary software.
Credits
Created by Abdallah Isham