laravel-notification-channels/sipgate
A Laravel notification channel for sending SMS messages through sipgate.io. Integrate sipgate into your app’s notification flow and deliver text messages using Laravel’s familiar notification system, with simple configuration for credentials and recipient phone numbers.
What the package is. A thin Laravel notification channel that sends SMS through sipgate's REST API. In practice it's ~2 classes (SipgateChannel, SipgateMessage) and a config entry — an adapter, not a framework. Treat it as a starting point you own rather than a dependency you lean on.
Prerequisites.
e0) — sipgate is a German telephony provider, so numbers are typically +49….Install and configure.
composer require laravel-notification-channels/sipgate
php artisan vendor:publish --provider="NotificationChannels\Sipgate\SipgateServiceProvider"
If no config is published, add the block to config/services.php yourself — the channel reads credentials from there:
'sipgate' => [
'token_id' => env('SIPGATE_TOKEN_ID'),
'token' => env('SIPGATE_TOKEN'),
'sms_extension' => env('SIPGATE_SMS_EXTENSION'), // e.g. "e0"
],
First use case — the one that justifies the package: transactional SMS inside an existing notification flow. OTP/2FA codes, on-call paging, or "your appointment is in 15 minutes" messages where you already have a Notifiable model.
php artisan make:notification SendVerificationCode
use NotificationChannels\Sipgate\SipgateChannel;
use NotificationChannels\Sipgate\SipgateMessage;
public function via($notifiable): array
{
return [SipgateChannel::class];
}
public function toSipgate($notifiable): SipgateMessage
{
return SipgateMessage::create("Your code is {$this->code}");
}
// app/Models/User.php
public function routeNotificationForSipgate(): ?string
{
return $this->phone; // store E.164: +4915112345678
}
$user->notify(new SendVerificationCode('482913'));
Where to look first. Read src/SipgateChannel.php and src/SipgateMessage.php before anything else — that's the whole package. Confirm (a) the exact config keys, (b) the message builder method names (create, line, from/sendAsExtension, …), and (c) the API endpoint and payload shape (POST /sessions/sms with smsId, recipient, message, sendAsExtension). Ten minutes of reading beats an hour of guessing.
Version note for 1.4.0. This release is the first since 2022 and adds Laravel 8.x/9.x support plus StyleCI formatting. Nothing in the public API changed — same classes, same config keys, same payload — so an upgrade from 1.3.x should be a composer.json constraint bump and a composer update. Read the diff on composer.json first to confirm the PHP floor, then smoke-test one send in staging.
Fluent message construction. Most laravel-notification-channels packages follow the same builder idiom; use it instead of string concatenation so the payload stays inspectable:
public function toSipgate($notifiable): SipgateMessage
{
return (new SipgateMessage())
->line('Your appointment with Dr. Weber')
->line('is confirmed for '.$this->slot->format('d.m.Y H:i'))
->from(config('services.sipgate.sms_extension'));
}
Anonymous / on-demand recipients. Essential for paging alerts or notifying a number that isn't a model:
Notification::route('sipgate', '+4915112345678')
->notify(new SystemDownAlert($incident));
Queue it, but deliberately. SMS is an outbound network call — wrap it in ShouldQueue so a sipgate timeout never blocks a request:
class SendVerificationCode extends Notification implements ShouldQueue
{
public $tries = 1; // see Gotchas: retries = duplicate SMS
public $timeout = 10;
}
Multi-channel with a fallback. The channel is a single point of failure for a German-only provider; pair it with something else on critical paths:
public function via($notifiable): array
{
return $notifiable->phone
? [SipgateChannel::class]
: ['mail'];
}
For true failover, catch the send failure in a job and dispatch a secondary notification rather than relying on channel ordering.
Reuse a single notification class across contexts. Because the message is built from properties, one SendVerificationCode / SendAlert class serves login verification, password reset, and admin tooling — keep the message body in the notification, not in the controller. This is the biggest day-to-day leverage: the rest of your app never touches sipgate.
Testing without spending money.
Notification::fake();
$user->notify(new SendVerificationCode('123456'));
Notification::assertSentTo($user, SendVerificationCode::class, function ($n, $channels) {
return in_array(SipgateChannel::class, $channels);
});
For integration-level confidence, bind a fake HTTP client with Http::fake(['api.sipgate.com/*' => Http::response(['smsId' => 'x'], 200)]) and assert the outbound payload — that verifies phone formatting and extension wiring, which is where real bugs live.
Central helper for ad-hoc sends. If you don't have a Notifiable, write a small service that wraps the channel (or Http directly against /sessions/sms) instead of scattering Notification::route() calls. One place to change credentials, throttle, and log.
Laravel version ceiling moved — but only to 9.x. 1.4.0 widens the constraint to include Laravel 8.x and 9.x, which removes the sharpest edge from the previous release: on Laravel 8/9 you can now use upstream directly. Laravel 10/11 still hit composer conflicts on illuminate/support and are not addressed by this release. Options remain: fork it and bump the constraint (the code is small — often zero changes needed beyond composer.json), or reimplement the ~30-line channel yourself and drop the dependency. For a small, low-dependency package like this, forking is still the sane move if you're past Laravel 9 — you keep upstream's API shape without waiting on another release.
Maintenance signal improved, but read it carefully. A community PR landing after a two-year gap is a good sign, but this release is scoped to framework constraints and style fixes — no new features, no new tests, no API change. Budget for ownership: pin the version, and if you're on Laravel 10+ vendor a fork under your org namespace and note in your README that you own it. Do not build a critical paging path on it without a fallback.
Upgrade mechanics on 1.4.0. If you were previously pinned to 1.3.x on Laravel 7, the constraint bump is the whole change — but run composer update laravel-notification-channels/sipgate -W so transitive illuminate/* constraints resolve together, and check the diff of composer.json for the PHP floor before rolling into CI. Then re-verify one real send in staging; the StyleCI pass touched formatting only, so behavioral risk is near zero.
Phone number format. sipgate expects E.164 (+4915112345678). No 00, no leading 0, no spaces, no (0) in the middle. Normalize at the model boundary (a mutator or a libphonenumber cast) — the channel almost certainly does not sanitize for you, and a malformed number surfaces as a generic API error.
Extension IDs, not phone numbers. sendAsExtension must be a sipgate extension ID (e0), which is not the same as the phone number. Mismatches produce 4xx responses that the channel may not surface clearly. Verify in the portal and log the raw response on failure.
No delivery receipts. The API accepts the message; it does not confirm handset delivery. Do not use this channel for anything requiring guaranteed delivery (legal notices, MFA-only fallback for high-value accounts). Pair OTP SMS with email or TOTP.
Queue retries double-send. A retry after a timeout may re-send an already-accepted SMS. Set $tries = 1 on time-sensitive messages, or make the payload idempotent by reusing smsId.
Encoding costs money. GSM-7 handles ASCII in 160 chars; umlauts and emoji flip the message to UCS-2 (70 chars/segment) and multiply your per-SMS cost. German-language notifications with ä/ö/ü/ß are the classic trap. Keep alerts ASCII, and count segments before sending.
Config caching. After changing .env, run php artisan config:clear. This bites everyone once, usually during a credential rotation — and it will bite you again during the 1.4.0 upgrade if you rotate tokens at the same time.
Silent failures. Channels in this family often return the HTTP response without throwing. Wrap sends in a job that inspects the result and logs status + body; otherwise a revoked token means notifications vanish quietly. Add a heartbeat: send a test SMS to an internal number daily.
Environment guard. Hard-block the channel outside production/staging — a stray test run against real credentials costs real euros and pages real people:
if (! app()->environment('production', 'staging')) {
return ['log']; // or Notification::fake() in tests
}
Extension point worth adding yourself. The package has no retry/backoff or rate limiter. sipgate throttles; if you fan out alerts to an on-call rotation, add a RateLimiter::attempt() or ->middleware(['throttle:sipgate']) on the job. Similarly, if you need fax (sipgate supports it), that's a fork-level addition, not a config flag — and 1.4.0 does not change that calculus.
How can I help you explore Laravel packages today?