DEV Community

Cover image for Laravel Fast2SMS v2.0.0 — WhatsApp Support, Notification Channels & a Smarter DX
Shakil Alam
Shakil Alam

Posted on • Originally published at blog.shakiltech.com

Laravel Fast2SMS v2.0.0 — WhatsApp Support, Notification Channels & a Smarter DX

When I shipped v1, the goal was simple: send SMS to Indian phone numbers from Laravel without wrestling raw API arrays. v2.0.0 takes that same idea much further.

The headline is WhatsApp Business API support — text, images, documents, locations, templates, reactions, stickers, and interactive messages, all through the same familiar Fast2sms facade. Beyond that, v2 brings drop-in Laravel Notification Channels, a typed exception hierarchy, expressive testing utilities, and cost-saving guards that protect your wallet in production.

⚠️ This is a major release with breaking changes. See the Upgrade Guide before updating.

Let me walk you through what matters.


💬 WhatsApp Business API

This is the big one. Starting in v2.0.0, the package supports the Fast2SMS WhatsApp Business API through Fast2sms::whatsapp().

Send a plain text message:

use Shakil\Fast2sms\Facades\Fast2sms;

Fast2sms::whatsapp()
    ->to('9999999999')
    ->text('Your order has been shipped and is on its way!')
    ->send();
Enter fullscreen mode Exit fullscreen mode

Send an image with a caption:

Fast2sms::whatsapp()
    ->to('9999999999')
    ->image('https://yourapp.com/invoice.png', caption: 'Your invoice for order #1042')
    ->send();
Enter fullscreen mode Exit fullscreen mode

Send a document:

Fast2sms::whatsapp()
    ->to('9999999999')
    ->document('https://yourapp.com/receipt.pdf', filename: 'receipt.pdf')
    ->send();
Enter fullscreen mode Exit fullscreen mode

Send a location:

Fast2sms::whatsapp()
    ->to('9999999999')
    ->location(lat: 28.6139, lng: 77.2090, name: 'New Delhi')
    ->send();
Enter fullscreen mode Exit fullscreen mode

Send a registered template:

Fast2sms::whatsapp()
    ->to('9999999999')
    ->template(id: 'YOUR_TEMPLATE_ID', variables: ['John', 'Order #1042'])
    ->send();
Enter fullscreen mode Exit fullscreen mode

React to a message, send a sticker, or build interactive button flows — the full WhatsApp API surface is available through the same fluent chain.


🔔 Laravel Notification Channels

v2 ships two drop-in notification channels: SmsChannel and WhatsAppChannel. Your existing notification classes work exactly the way you'd expect.

use Shakil\Fast2sms\Channels\SmsChannel;
use Shakil\Fast2sms\Channels\WhatsAppChannel;
use Shakil\Fast2sms\Messages\SmsMessage;
use Shakil\Fast2sms\Messages\WhatsAppMessage;

class OrderShipped extends Notification
{
    public function via($notifiable): array
    {
        return [SmsChannel::class, WhatsAppChannel::class];
    }

    public function toFast2sms($notifiable): SmsMessage
    {
        return SmsMessage::create()
            ->otp('Your OTP is 482910. Valid for 10 minutes.');
    }

    public function toWhatsApp($notifiable): WhatsAppMessage
    {
        return WhatsAppMessage::create()
            ->text('Your order has been shipped!');
    }
}
Enter fullscreen mode Exit fullscreen mode

Add the routing methods to your User model and you're done:

public function routeNotificationForFast2sms(): string
{
    return $this->phone_number;
}

public function routeNotificationForWhatsapp(): string
{
    return $this->phone_number;
}
Enter fullscreen mode Exit fullscreen mode

No facade, no manual number passing. Just idiomatic Laravel.


🧱 Typed Exception Hierarchy

In v1, every API failure threw a single Fast2smsException. In v2 you can catch exactly what you need:

use Shakil\Fast2sms\Exceptions\AuthenticationException;
use Shakil\Fast2sms\Exceptions\RateLimitException;
use Shakil\Fast2sms\Exceptions\InsufficientBalanceException;
use Shakil\Fast2sms\Exceptions\NetworkException;

try {
    Fast2sms::otp('9999999999', '845621');
} catch (InsufficientBalanceException $e) {
    // Notify your team — wallet is empty
} catch (RateLimitException $e) {
    // Back off and retry
} catch (AuthenticationException $e) {
    // API key is wrong or expired
} catch (NetworkException $e) {
    // Fast2SMS was unreachable
}
Enter fullscreen mode Exit fullscreen mode

The full list of typed exceptions is in the changelog.


🧪 Rich Fake Assertions for Testing

v2 ships 16 assertion methods on Fast2smsFake so your feature tests are expressive and readable — no more inspecting raw recorded calls.

Fast2sms::fake();

// ... trigger your code ...

Fast2sms::assertSmsSentTo('9999999999');

Fast2sms::assertSmsSentTo('9999999999', fn ($sms) =>
    str_contains($sms->message, '482910')
);

Fast2sms::assertWhatsAppSentTo('9999999999');

Fast2sms::assertNothingSent();

// Clean up for tests that need real sending
Fast2sms::stopFaking();
Enter fullscreen mode Exit fullscreen mode

💡 Fluent Message Builders with Credit Helpers

SmsMessage and WhatsAppMessage are first-class objects with named constructors and chainable setters. SmsMessage also ships with credit helpers so you can estimate cost before you send:

use Shakil\Fast2sms\Messages\SmsMessage;

$message = SmsMessage::create()
    ->withContent('Your appointment is confirmed for tomorrow at 10am.')
    ->withRoute(SmsRoute::QUICK);

$message->charCount();       // character count
$message->isUnicode();       // true if Unicode encoding is needed
$message->creditCount();     // SMS credits this will consume
$message->exceedsOneSms();   // true if longer than one SMS
Enter fullscreen mode Exit fullscreen mode

No more guessing whether a long message costs 1 credit or 3.


🛡️ Cost-Saving Guards

v2 introduces a set of opt-in guards that protect your wallet in production. All are off by default — enable only what your use case needs in config/fast2sms.php:

Guard What it does
Recipient deduplication Strips duplicate numbers before every send
Invalid recipient stripping Validates numbers and logs warnings instead of failing hard
Idempotency guard Blocks the same message being sent twice within a TTL window
Rate throttle Sliding-window per-minute cap via Laravel cache
Balance gate Checks your wallet before sending; fires LowBalanceDetected when low
Batch splitting Splits large recipient lists into chunks automatically

⚙️ New Artisan Commands

# List every event the package fires, with descriptions
php artisan fast2sms:events

# Generate an IDE helper file for full autocompletion
php artisan fast2sms:ide-helper
Enter fullscreen mode Exit fullscreen mode

Run fast2sms:ide-helper once after install and get full autocompletion on every facade method in PhpStorm or VS Code.


🚀 Getting Started

Requirements: PHP ^8.3 (8.4 and 8.5 also tested), Laravel 11, 12, or 13.

composer require itxshakil/laravel-fast2sms
Enter fullscreen mode Exit fullscreen mode
php artisan vendor:publish --tag=fast2sms-config
Enter fullscreen mode Exit fullscreen mode
FAST2SMS_API_KEY="your-api-key"
FAST2SMS_DEFAULT_SENDER_ID="FSTSMS"
FAST2SMS_DEFAULT_ROUTE="dlt"
Enter fullscreen mode Exit fullscreen mode

Upgrading from v1

The public sending API — Fast2sms::quick(), ::dlt(), ::otp(), and now ::viaWhatsApp() — is completely unchanged. Most v1 code will work without modification.

The breaking changes are in internals: exception types, DTOs, and return type hints. The full step-by-step migration is in UPGRADING.md.


Links

If this saved you time, a star on GitHub helps other Laravel developers find the package.


Built by Shakil Alam — Laravel developer, open-source contributor.

Top comments (0)