vibrantcoder/whatsapp-notifier

Reusable Laravel package for sending WhatsApp notifications via multiple drivers (Twilio, Meta Cloud API).

Maintainers

Package info

github.com/vibrantcoder/whatsapp-notifier

pkg:composer/vibrantcoder/whatsapp-notifier

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-04-30 10:07 UTC

This package is auto-updated.

Last update: 2026-04-30 10:13:34 UTC


README

Reusable Laravel package for sending WhatsApp messages through multiple drivers (Twilio, Meta Cloud API) with dynamic templates, queues, events, and DB logging. Works with Laravel 10, 11, 12, 13.

Features

  • Driver patternmeta, twilio, or your own custom driver.
  • Fluent template buildermetaTemplate() for any Meta template (text body, image/video/document headers, URL/quick-reply/copy-code buttons, location, named placeholders).
  • Template registry — declare each template once in config, reference by alias from app code.
  • Queue support — fire-and-forget async delivery via Laravel queues.
  • EventsWhatsAppMessageSending, WhatsAppMessageSent, WhatsAppMessageFailed.
  • DB logging — every send attempt persisted to whatsapp_messages (success or failure, with full Meta response).
  • Service provider + facade — auto-discovered, no manual registration.

Requirements

  • PHP 8.1+
  • Laravel 10, 11, 12, or 13
  • Composer 2.x

Installation

There are three ways to install — pick the one that matches where the package lives.

Option 1 — Local path repository (during development)

Use this when the package source lives inside or next to your Laravel app (e.g. packages/vibrantcoders/whatsapp-notifier).

In your Laravel app's composer.json:

{
    "require": {
        "vibrantcoder/whatsapp-notifier": "@dev"
    },
    "repositories": [
        {
            "type": "path",
            "url": "../packages/vibrantcoders/whatsapp-notifier",
            "options": { "symlink": true }
        }
    ]
}

The @dev constraint is required because path repos publish as dev-main. symlink: true means edits inside the package are picked up immediately.

composer update vibrantcoder/whatsapp-notifier

Option 2 — Private Git repository

{
    "require": {
        "vibrantcoder/whatsapp-notifier": "^1.0"
    },
    "repositories": [
        {
            "type": "vcs",
            "url": "git@github.com:vibrantcoder/whatsapp-notifier.git"
        }
    ]
}
composer require vibrantcoder/whatsapp-notifier

Option 3 — Public Packagist (once published)

composer require vibrantcoder/whatsapp-notifier

Setup (after install)

One command does everything — publishes config, migrations, env keys, and runs migrate:

php artisan whatsapp:install

Service provider and facade are auto-discovered — no manual registration in config/app.php is required.

For the manual breakdown, see INSTALL.md.

Environment variables

Add the relevant block to your .env:

# Default driver: meta | twilio | <custom>
WHATSAPP_DRIVER=meta

# Meta Cloud API
META_WHATSAPP_PHONE_ID=1001922139681184
META_WHATSAPP_TOKEN=EAAxxx...
META_WHATSAPP_VERSION=v25.0

# Twilio (only if using the twilio driver)
# TWILIO_SID=ACxxxx
# TWILIO_AUTH_TOKEN=xxxx
# TWILIO_WHATSAPP_FROM=whatsapp:+14155238886

# Queue
WHATSAPP_QUEUE=true
WHATSAPP_QUEUE_NAME=whatsapp

# Logging
WHATSAPP_LOG=true
WHATSAPP_LOG_DB=true

⚠️ The Meta temporary access token expires every 24 hours. For production, generate a System User token from Meta Business Settings (60-day or never-expiring).

Usage

1. Inline free-form message (Meta or Twilio)

Free-form messages are only deliverable in the 24-hour customer-care window on Meta. Use templates outside that window.

use VibrantCoders\WhatsAppNotifier\Facades\WhatsApp;

WhatsApp::message('Hello {{name}}, this is a test.')
    ->with(['name' => 'Ali'])
    ->send('919727927400');

2. Meta template via the registry (recommended)

Declare each template once in config/whatsapp.php:

'meta_templates' => [
    'order_confirmed' => [
        'name'     => 'jaspers_market_order_confirmation_v1',
        'language' => 'en_US',
        'body'     => ['customer_name', 'order_id', 'expected_delivery'],
    ],
],

Then in your code:

WhatsApp::metaTemplate('order_confirmed', [
    'customer_name'     => $order->customer_name,
    'order_id'          => (string) $order->id,
    'expected_delivery' => $order->expectedDeliveryFormatted(),
])->send($order->customer_phone);

3. Meta template via the fluent builder

Use this when the template has dynamic header/buttons that vary per call, or when you don't want to maintain the registry.

WhatsApp::metaTemplate('jaspers_market_order_confirmation_v1', 'en_US')
    ->body('Ali', '1042', '4 May 2026')
    ->send('919727927400');

// Image header + URL button
WhatsApp::metaTemplate('order_shipped_v2')
    ->headerImage('https://cdn.example.com/box.jpg')
    ->body('Ali', '1042')
    ->urlButton(0, '1042')
    ->send($phone);

// PDF document header
WhatsApp::metaTemplate('invoice_ready')
    ->headerDocument('https://cdn.example.com/inv-1042.pdf', 'invoice-1042.pdf')
    ->body('Ali', '1042', 'INR 1,499.00')
    ->send($phone);

// Quick-reply buttons
WhatsApp::metaTemplate('feedback_request')
    ->body('Ali')
    ->quickReplyPayload(0, 'YES')
    ->quickReplyPayload(1, 'NO')
    ->send($phone);

// Named placeholders ({{customer_name}} style)
WhatsApp::metaTemplate('order_confirmed_named')
    ->bodyNamed(['customer_name' => 'Ali', 'order_id' => '1042'])
    ->send($phone);

// Escape hatch — supply the full components array
WhatsApp::metaTemplate('any_template')->withRawComponents([...])->send($phone);

4. Override driver per call

WhatsApp::via('twilio')->message('Hi!')->send('+923001234567');
WhatsApp::via('meta')->metaTemplate('order_confirmed', [...])->send($phone);

5. Force sync or queue

WhatsApp::message('OTP: 123456')->queue(false)->send($phone);  // sync
WhatsApp::message('Receipt')->queue(true)->send($phone);        // async

6. Event-driven delivery (recommended for app code)

// app/Events/OrderPlaced.php
class OrderPlaced
{
    use Dispatchable, SerializesModels;
    public function __construct(public Order $order) {}
}

// app/Listeners/SendOrderConfirmationWhatsApp.php
class SendOrderConfirmationWhatsApp
{
    public function handle(OrderPlaced $event): void
    {
        WhatsApp::metaTemplate('order_confirmed', [
            'customer_name'     => $event->order->customer_name,
            'order_id'          => (string) $event->order->id,
            'expected_delivery' => $event->order->expectedDeliveryFormatted(),
        ])->send($event->order->customer_phone);
    }
}

// Anywhere in your code:
event(new OrderPlaced($order));

Laravel auto-discovers the listener from the typehint — no EventServiceProvider mapping required (Laravel 11+).

Supported template features

Feature Builder method Registry key
Body {{1}} {{2}} body(...$values) 'body' => [...]
Named body {{name}} bodyNamed(['k' => 'v']) (use builder)
Header text headerText('...') 'header' => 'text'
Header image headerImage($url) 'header' => 'image'
Header video headerVideo($url) 'header' => 'video'
Header document headerDocument($url, $filename) 'header' => 'document'
Header location headerLocation($lat, $lng, $name, $addr) (use builder)
URL button urlButton($index, $value) 'buttons' => [['type' => 'url']]
Quick-reply button quickReplyPayload($index, $payload) 'buttons' => [['type' => 'quick_reply']]
Copy-code button copyCodeButton($index, $code) 'buttons' => [['type' => 'copy_code']]
Raw passthrough withRawComponents([...])

Custom drivers

Register in any service provider's boot():

use VibrantCoders\WhatsAppNotifier\Contracts\WhatsAppDriver;
use VibrantCoders\WhatsAppNotifier\Services\WhatsAppManager;

app(WhatsAppManager::class)->extend('myprovider', function ($app, $config) {
    return new class implements WhatsAppDriver {
        public function name(): string { return 'myprovider'; }
        public function send(string $to, string $message, array $options = []): array
        {
            // your provider call here...
            return ['success' => true, 'id' => '...', 'response' => [...], 'error' => null];
        }
    };
});

Then WhatsApp::via('myprovider')->message(...)->send(...).

Logged messages

Every attempt — sync or queued, success or failure — is persisted:

use VibrantCoders\WhatsAppNotifier\Models\WhatsAppMessage;

WhatsAppMessage::orderByDesc('id')->limit(50)->get();

Columns:

Column Type
id bigint
driver varchar (32)
to varchar (32)
body text
status pending|sent|failed
provider_id varchar
error text (nullable)
response json (nullable)
sent_at timestamp
created_at timestamp
updated_at timestamp

Quick test (5 minutes, no real templates needed)

# 1. Install + setup
composer update vibrantcoder/whatsapp-notifier
php artisan vendor:publish --tag=whatsapp-config
php artisan vendor:publish --tag=whatsapp-migrations
php artisan migrate

# 2. Add a tinker test
php artisan tinker
>>> use VibrantCoders\WhatsAppNotifier\Facades\WhatsApp;
>>> WhatsApp::metaTemplate('order_confirmed', [
...     'customer_name' => 'Ali',
...     'order_id' => '999',
...     'expected_delivery' => '4 May 2026',
... ])->send('919727927400');

You should receive a real WhatsApp message and see a row in the whatsapp_messages table with status = sent and a provider_id returned by Meta.

Troubleshooting

Symptom Cause / fix
(#132001) Template name does not exist in the translation Template not approved, wrong name, or wrong language code. Check WhatsApp Manager.
(#131030) Recipient phone number not in allowed list You're using Meta's free test number — add the recipient under "Send messages to" in API Setup.
Invalid OAuth access token Temp token expired (24h). Generate a new one in API Setup, paste into .env, run php artisan config:clear.
Meta template alias [...] is not registered Add the alias to meta_templates in config/whatsapp.php, or use builder mode.
Sends silently fail outside 24h window Meta only allows template messages outside the customer-care window. Use metaTemplate(), not message().
Queue jobs not running Make sure a worker is running: php artisan queue:work --queue=whatsapp

Roadmap

  • Webhook receiver to update whatsapp_messages status from Meta callbacks (delivered / read / failed)
  • Inbound message handling (conversations + agents)
  • Carousel + authentication (OTP) template helpers
  • Rate limiting per Meta tier (1k / 10k / 100k / unlimited messages-per-day)

License

MIT © VibrantCoders