Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Turbosms Laravel Package

laravel-notification-channels/turbosms

A Laravel notification channel for sending SMS messages through TurboSMS. Integrate TurboSMS with Laravel notifications and deliver SMS using a simple, familiar channel API.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up

composer require laravel-notification-channels/turbosms
php artisan vendor:publish --provider="NotificationChannels\TurboSms\TurboSmsServiceProvider"

Pin at least 1.1.1 if you're on a modern Laravel — that release (PRs #10 and #12) is what added Laravel 11 and Laravel 12 compatibility. Older tags declare tighter framework constraints and Composer will refuse or downgrade you on Laravel 11/12 skeletons. On 1.1.1 the install constraint can be:

"laravel-notification-channels/turbosms": "^1.1.1"

The 1.1.0 → 1.1.1 diff is framework-compatibility only: no channel API, config key, or message-method changes, so nothing in the usage below shifts. The service provider is auto-discovered on Laravel 5.5+; on older skeletons register NotificationChannels\TurboSms\TurboSmsServiceProvider in config/app.php.

Publishing creates config/turbosms.php. Populate it from .env — typically a login/password pair for the TurboSMS account plus a sender name that has already been approved in the TurboSMS dashboard:

TURBOSMS_LOGIN=your-login
TURBOSMS_PASSWORD=your-password
TURBOSMS_SENDER=MyApp

Where to look first

  1. The README's "Usage" block — it defines the exact channel key ('turbosms' vs TurboSmsChannel::class) and message factory name.
  2. config/turbosms.php — confirms the env keys and any base URL override.
  3. src/TurboSmsMessage.php — the fluent surface you'll actually call (content(), sender(), to() in most channels of this family).
  4. src/TurboSmsChannel.php — the send() method tells you how responses and errors are handled, which is what you'll debug against.

First use case: transactional OTP or order status

use NotificationChannels\TurboSms\TurboSmsChannel;
use NotificationChannels\TurboSms\TurboSmsMessage;

class OrderShipped extends Notification
{
    public function __construct(public Order $order) {}

    public function via($notifiable): array
    {
        return [TurboSmsChannel::class];
    }

    public function toTurboSms($notifiable): TurboSmsMessage
    {
        return TurboSmsMessage::create("Order #{$this->order->id} shipped");
    }
}

Give the notifiable a routing method so the channel can find a number:

public function routeNotificationForTurboSms($notification): ?string
{
    return $this->phone; // normalize to 380XXXXXXXXX before returning
}

That's the whole loop: one via() entry, one toTurboSms() method, one routing method.

Implementation Patterns

On-demand / anonymous recipients. For users without a phone column, or for pre-signup OTP, bypass the notifiable entirely:

Notification::route('turbosms', '380501234567')
    ->notify(new PhoneVerification($code));

If the key isn't 'turbosms', substitute TurboSmsChannel::class — the README is authoritative here, and it differs across channels in this org.

Fan-out across channels. SMS is usually the fallback, not the primary. Return ['mail', TurboSmsChannel::class] and keep the message text short and channel-agnostic; SMS silently truncates anything over the segment limit.

Queue it. SMS is a network call inside the request cycle. Implement ShouldQueue on the notification and let a worker absorb latency and retries. If you need strict once-only delivery, add ShouldBeUnique keyed on the OTP/order ID rather than relying on the channel.

Throttle deliberately. OTP endpoints are abuse targets. Combine a RateLimiter in the controller with queued dispatch so a burst doesn't burn your TurboSMS balance:

RateLimiter::attempt("otp:{$user->id}", 3, fn () => $user->notify(new SendOtp($code)), 300);

Test with the notification fakes. The channel doesn't need to hit the network in CI:

Notification::fake();

$user->notify(new OrderShipped($order));

Notification::assertSentTo($user, OrderShipped::class, function ($n, $channels) {
    return in_array(TurboSmsChannel::class, $channels, true);
});

For content assertions, instantiate toTurboSms($user) directly and assert the message body — cheaper than faking the transport.

Observe delivery. Listen to Laravel's Illuminate\Notifications\Events\NotificationSent and NotificationFailed. Log failures with the notifiable ID and the message text (redact OTPs), so you can reconcile against the TurboSMS dashboard when a user says "I never got the code."

Wrap the transport for non-notification sends. If you need to send SMS outside the notification system (admin alerts, batch jobs), put a thin SmsService around the channel or message class rather than scattering Notification::route() calls. It gives you one place to swap providers later.

Gotchas and Tips

Phone format is the #1 failure. TurboSMS expects digits in international form without + or separators (380XXXXXXXXX). Normalize inside routeNotificationForTurboSms() — not at the call site — so every send path inherits the fix. Strip parentheses, dashes, and leading 0 from local numbers.

Sender names must be pre-approved. TURBOSMS_SENDER has to match a signature registered in the TurboSMS account. An unapproved sender generally surfaces as an API-level rejection, not a transport exception, so it looks like a silent failure.

Check the response body, not just the HTTP status. TurboSMS's API commonly returns 200 OK with an error payload. Read TurboSmsChannel::send() to see whether the package throws on that; if it doesn't, your try/catch around notify() will never fire and failed sends will vanish. Wrap the channel or add your own assertion on the response in a subclass if this matters.

Encoding changes your bill. Cyrillic text is UCS-2: ~70 characters per segment instead of 160. A 140-character Ukrainian message is two billable SMS. Keep messages under 70 characters where possible and test with real Cyrillic text, not lorem ipsum.

Config caching bites. After changing any TURBOSMS_* env var, run php artisan config:clear. In containerized deploys the config is baked at build time — make sure credentials are injected at runtime, not compiled into the image.

Credentials are per-environment. Use a separate TurboSMS sender or account for staging, and point staging at a test number. There's no sandbox mode in the channel itself.

Version compatibility is the one thing that just changed. 1.1.1 is a compatibility release: Laravel 11 (#10) and Laravel 12 (#12). Practical consequences:

  • On Laravel 11/12, require ^1.1.1. An unpinned composer require will land here anyway, but a stale lockfile copied from a Laravel 10 project won't.
  • No breaking changes between 1.1.0 and 1.1.1 — the TurboSmsChannel / TurboSmsMessage surface and config keys are untouched, so existing code needs no edits. Treat this as a "bump and run the suite" upgrade, not a migration.
  • The bump confirms the package is still maintained, which meaningfully de-risks the low-adoption concern below — but adoption numbers (stars, dependents) are still small, so the mitigations stand.

Low adoption is a real risk. Very few dependents and low star count means few people have exercised edge cases. Mitigations:

  • Pin the constraint deliberately ("^1.1.1" or an exact tag) rather than floating.
  • Read TurboSmsChannel::send() end-to-end before going live; it's short.
  • Keep an interface (SmsSender) between your app and the channel so a provider swap is a single binding change.
  • Add one integration test against the real API in a nightly job, not in the PR pipeline — Laravel 11/12 support was verified by the maintainers against the framework, not against your account's sender/signature setup.

Timeouts and retries. If the message class or config exposes a timeout, set it low (3–5s) and let the queue handle retries. Without a timeout, a stalled connection hangs a worker. Cap $tries and $backoff on the notification to avoid double-sending OTPs on transient errors.

toTurboSms() returning null. Several channels in this family treat a null message as "skip this channel"; others throw. Verify in the source before you rely on conditional sending — if it throws, guard with an early return in via() instead.

Message length truncation is silent. The API cuts overlong bodies at a segment boundary. Assert length in a unit test on toTurboSms($user)->content if the text is dynamic.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel