symfony/sinch-notifier
Symfony Notifier integration for Sinch. Send SMS via Sinch using a simple DSN like sinch://SERVICE_PLAN_ID:AUTH_TOKEN@default?from=FROM, where FROM is your sender. Configure with your service plan ID and auth token.
Install the Package Add the package via Composer (though it’s Symfony-focused, we’ll adapt it):
composer require symfony/sinch-notifier
Configure Sinch DSN
Add to .env:
SINCH_DSN=sinch://SERVICE_PLAN_ID:AUTH_TOKEN@default?from=YOUR_SENDER_ID
Note: Laravel’s .env format differs from Symfony’s parameters.yaml.
Create a Laravel Service Provider Register the Symfony Sinch client as a Laravel binding:
// app/Providers/SinchServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Transport\SinchTransportFactory;
class SinchServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('sinch.notifier', function ($app) {
$dsn = config('services.sinch.dsn');
$factory = new SinchTransportFactory();
$transport = $factory->create($dsn);
return new Notifier([$transport]);
});
}
}
First Use Case: Send an SMS Inject the notifier into a controller or command:
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\Notification\Notification;
public function sendWelcomeSms()
{
$notifier = app('sinch.notifier');
$message = new SmsMessage('Welcome to our app!', '1234567890');
$notification = new Notification('Welcome', $message);
$notifier->send($notification);
}
Sending Notifications
SmsMessage for text messages.
$message = new SmsMessage('Your OTP is 1234', 'user_phone_number');
$notifier->send(new Notification('OTP', $message));
VoiceMessage for call notifications (limited to Sinch’s voice API).
$message = new VoiceMessage('Hello, this is a test call', 'user_phone_number');
$notifier->send(new Notification('Voice Alert', $message));
ChatMessage for platforms like WhatsApp (if supported by Sinch).
$message = new ChatMessage('Hi!', 'user_chat_id');
Handling Responses
$message = new SmsMessage('Hello', '1234567890');
$message->withCallback(function ($response) {
if ($response->failed()) {
Log::error('SMS failed', ['error' => $response->getReason()]);
}
});
Batch Processing
foreach ($userPhones as $phone) {
SendSmsJob::dispatch($phone, 'Your message')->onQueue('sinch');
}
Webhook Integration
Route::post('/sinch/webhook', [SinchWebhookController::class, 'handle']);
public function handle(Request $request)
{
$payload = $request->json()->all();
if ($payload['event'] === 'message.sent') {
Message::where('sinch_id', $payload['messageId'])->update(['status' => 'delivered']);
}
}
Laravel Configuration
Define Sinch settings in config/services.php:
'sinch' => [
'dsn' => env('SINCH_DSN'),
'timeout' => env('SINCH_TIMEOUT', 30),
],
Dependency Injection Bind the notifier to Laravel’s container for easier testing:
$this->app->bind(SinchNotifier::class, function ($app) {
return app('sinch.notifier');
});
Testing Mock the Sinch transport in tests:
$transport = $this->createMock(SinchTransport::class);
$transport->method('send')->willReturn(new SentMessage());
$notifier = new Notifier([$transport]);
Fallback Mechanisms
Implement a retry logic for failed sends using Laravel’s retry helper:
try {
$notifier->send($notification);
} catch (TransportException $e) {
retry(3, function () use ($notifier, $notification) {
$notifier->send($notification);
}, function () {
Log::error('Max retries reached for notification');
});
}
Symfony-Laravel DI Conflicts
Notifier expects a specific DI structure. Laravel’s container may throw errors if not properly bridged.class SinchFacade {
public static function send(SmsMessage $message) {
return app('sinch.notifier')->send(new Notification('SMS', $message));
}
}
Sinch API Rate Limits
sleep() or Laravel’s afterCommit() to space out sends:
foreach ($users as $user) {
SendSmsJob::dispatch($user)->delay(now()->addSeconds(2));
}
Webhook Verification
Route::post('/sinch/webhook', function () {
$signature = $request->header('X-Sinch-Signature');
if (!SinchWebhookValidator::validate($signature, $request->getContent())) {
abort(403);
}
// Process webhook
});
Phone Number Formatting
+1234567890). Laravel may receive raw inputs like 1234567890.$phone = PhoneNumber::parse($rawPhone)->formatE164();
Logging and Debugging
$message->withCallback(function ($response) {
Log::debug('Sinch response', [
'status' => $response->getStatus(),
'reason' => $response->getReason(),
]);
});
Environment-Specific Configs
Use Laravel’s .env for different Sinch credentials per environment:
SINCH_DSN_STAGING=sinch://STAGING_ID:TOKEN@default?from=STAGING_SENDER
SINCH_DSN_PROD=sinch://PROD_ID:TOKEN@default?from=PROD_SENDER
Extending the Notifier
Create custom message types by extending Symfony’s Message classes:
class CustomSmsMessage extends SmsMessage {
public function __construct(string $content, string $recipient, array $options = []) {
parent::__construct($content, $recipient, $options);
$this->addOption('custom_key', 'custom_value');
}
}
Monitoring Costs Track Sinch usage in Laravel’s logs or a database:
$message->withCallback(function ($response) use ($userId) {
Log::info('Sinch usage', [
'user_id' => $userId,
'cost' => $this->calculateSinchCost($response),
]);
});
Fallback to Alternative Providers Implement a strategy pattern to switch providers dynamically:
class NotificationService {
public function __construct(private Notifier $sinchNotifier, private Notifier $fallbackNotifier) {}
public function send(SmsMessage $message) {
try {
$this->sinchNotifier->send(new Notification('SMS', $message));
} catch (TransportException $e) {
$this->fallbackNotifier->send(new Notification('SMS', $message));
}
}
}
Testing Webhooks Locally
How can I help you explore Laravel packages today?