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

All My Sms Laravel Package

laravel-notification-channels/all-my-sms

Send SMS notifications from your Laravel app through the AllMySMS API. This channel integrates with Laravel's notification system so you can deliver text messages to users alongside mail, database, and other channels.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require laravel-notification-channels/all-my-sms

The service provider is auto-discovered — no config/app.php edit. There is no publish step in the package description; credentials live in config/services.php.

Add credentials

// config/services.php
'all-my-sms' => [
    'api_key' => env('ALLMY_SMS_API_KEY'),
    'from'    => env('ALLMY_SMS_FROM'),
],
ALLMY_SMS_API_KEY=
ALLMY_SMS_FROM=MyBrand

Before writing a single notification, open src/AllMySmsChannel.php and confirm the exact config() path it reads. Guessing this key is the most common reason "nothing sends" — and because the channel is fire-and-forget, it fails quietly.

Route the notifiable

public function routeNotificationForAllMySms($notification = null): ?string
{
    return $this->phone; // E.164: +33612345678
}

The method name follows the channel alias (all-my-sms). Confirm the exact name against the package README — it is derived from the registered driver key, not the class name.

First notification

use NotificationChannels\AllMySms\{AllMySmsChannel, AllMySmsMessage};

class SendOtpCode extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(public string $code) {}

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

    public function toAllMySms(object $notifiable): AllMySmsMessage
    {
        return (new AllMySmsMessage)->content("Votre code : {$this->code}");
    }
}

Verify delivery, not code

Notification::route('all-my-sms', '+33612345678')->notify(new SendOtpCode('482913'));

Run this from tinker against a staging key. The first hour's goal is a message arriving on a real handset and a response body you can read — not a finished feature.

Where to look first

  • src/AllMySmsChannel.php — config reads, HTTP call, response handling, error semantics. You will vendor this eventually.
  • src/AllMySmsMessage.php — the fluent surface: content(), and whether from() or similar exists.
  • config/services.php (package docs) — credential keys and defaults.
  • composer.json — PHP floor and illuminate/* constraint. v1.4.0 was a Laravel 12 compatibility bump and nothing else.

Implementation Patterns

One notification class per message type. This is the single highest-leverage habit. SendOtpCode, OrderShippedSms, AppointmentReminderSms — each is small, testable, and reusable across notifiables. Do not build a generic SmsNotification with a $body string; you lose type safety, you lose via() logic, and message logic spreads into controllers.

Keep the number on the model, never in the notification. With routeNotificationForAllMySms() defined, the same notification works for $user, $admin, and an anonymous Notification::route() recipient. Hardcoding a to field in the message class forces a new class per recipient.

Compose channels in via() and think about order.

public function via(object $notifiable): array
{
    return array_values(array_filter([
        'mail',
        $notifiable->phone_verified_at ? AllMySmsChannel::class : null,
        'database',
    ]));
}

Guard on verification so you do not pay to text unverified or malformed numbers. Laravel runs channels in array order; if one throws, subsequent channels in that loop may not run. Put the channel that must not be skipped first, or send SMS as a separate notify() call.

Queue with the failure semantics in hand. implements ShouldQueue gives you retries and backoff — but only if the channel throws on provider failure. Verify that first (see Gotchas). Then:

public function backoff(): array { return [10, 60, 300]; }

Centralise formatting. Accented characters are the budget killer, so decide the budget in one place:

public function toAllMySms(object $notifiable): AllMySmsMessage
{
    return (new AllMySmsMessage)->content(
        Str::ascii(Str::limit($this->body, 160))
    );
}

Str::ascii() on a French brand name can be the difference between one segment and two. Be deliberate rather than hoping.

Wrap the vendor channel behind your own seam. One star, zero dependents, one maintainer. Give yourself a one-file provider swap:

// app/Notifications/Channels/SmsChannel.php
class SmsChannel
{
    public function send(object $notifiable, Notification $notification): void
    {
        app(AllMySmsChannel::class)->send($notifiable, $notification);
    }
}

Notifications reference SmsChannel::class and keep using toAllMySms(). When AllMySMS needs replacing, you edit one delegation. The driver is small enough that vendoring it outright under app/ is also a defensible few-hour decision.

On-demand OTP for unregistered users. Signup flows have a phone but no model. Anonymous routing keeps the notification class unchanged:

Notification::route('all-my-sms', $normalisedPhone)->notify(new SendOtpCode($code));

Combine with a cache-backed cooldown so a user cannot trigger ten sends:

RateLimiter::attempt("otp:{$phone}", 3, fn () => /* dispatch */, 600);

Split tests by layer.

// Behaviour: the notification exists and targets the right person
Notification::fake();
$user->notify(new SendOtpCode('123456'));
Notification::assertSentTo($user, SendOtpCode::class);

// Channel: exercises the HTTP boundary
Http::fake(['*' => Http::response(['status' => 'success'], 200)]);
app(AllMySmsChannel::class)->send($user, new SendOtpCode('123456'));
Http::assertSent(fn ($r) => $r['to'] === '+33612345678');

The second test only works if the channel uses Laravel's Http facade. If it uses raw Guzzle, fork it to Http — you gain fakeability for a few lines of change.

Drive environments through config. Different sender ID or sandbox per environment belongs in config/services.php, never in if (app()->environment()) inside a notification.

Gotchas and Tips

Check failure semantics on day one — this is the whole review. Read the channel's send(). Confirm it throws (NotificationFailed, CouldNotSendNotification, or a provider exception) on non-2xx and on error payloads returned inside a 200 OK body. Many SMS gateways reject with HTTP 200 plus {"status": "error"}. If the package swallows that, your queued job reports success and the OTP never lands — the worst possible failure mode for anything security-adjacent. It's a one-line fix in a vendored copy, and worth doing before the feature ships.

Log the response body during the spike. AllMySMS rejects for invalid number, unregistered sender ID, insufficient credit, and STOP-listed recipients — often with a 200. Watch the body, not just the status.

config:clear after every credential change. A stale cached config in a container is the classic "works locally, silent in prod" bug here. And in this package, silent is the default failure mode.

Sender ID registration is product work, not code work. Register your alphanumeric sender with AllMySMS before deploying. France/EU has registration rules; unregistered IDs get rejected or rewritten to a shared shortcode, which destroys brand recognition and can invalidate OTPs. Budget this as days, not hours, and cap an SMS budget for deliverability testing across your target carriers.

Normalise phone numbers at the boundary. Store and send E.164 (+33612345678). Never pass a user-typed string through. Depending on the gateway, a malformed number is either rejected (fine) or accepted and billed (not fine).

Segment math. GSM-7: 160 chars single, 153 per part concatenated. One accented character drops the whole message to UCS-2: 70 / 67. An OTP with a French brand prefix can quietly become two billed segments. Keep OTP bodies pure ASCII digits and punctuation.

Never log message bodies or phone numbers. OTPs in laravel.log are an incident. Also grep the package source for Log:: calls and confirm what it writes at debug level.

No delivery receipts, no inbound, no failover, no rate-limit handling. The channel is fire-and-forget. If the roadmap says "did it land?" or "can they reply?", that's additional engineering — do not assume a future release adds it. For bulk sends, throttle yourself with ->delay() or RateLimited middleware rather than expecting the channel to be polite.

Abandonment is a live operational risk. v1.4.0 (June 2025) added Laravel 12 and changed nothing else. Expect a lag window at every future major. Mitigate: pin ^1.4, keep the seam described above, and accept that forking under MIT is legal, cheap, and probably inevitable within two framework majors.

Extend by subclassing, not editing. Add helper methods (template(), from()) on a subclass of AllMySmsMessage; subclass the channel rather than patching vendor/. composer update should never produce a merge conflict.

Verify from() exists before relying on it. The description advertises a configured sender, not a per-message override. Check AllMySmsMessage — if there's no fluent setter, the sender is whatever config/services.php says, and per-campaign identities are a fork away.

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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox