bahricanli/whatsapp-business

Laravel notification channel for WhatsApp Business Cloud API (Meta)

Maintainers

Package info

github.com/bahricanli/whatsapp-business

pkg:composer/bahricanli/whatsapp-business

Transparency log

Statistics

Installs: 11

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.2.0 2026-07-31 13:25 UTC

This package is auto-updated.

Last update: 2026-07-31 13:27:25 UTC


README

Latest Version License: MIT

Laravel notification channel for sending template messages via Meta's official WhatsApp Business Cloud API — no self-hosted bridge, no unofficial WhatsApp Web automation.

Designed as a parallel channel alongside bahricanli/corvass (SMS) — the API is intentionally similar for easy integration.

Requirements

  • PHP 7.4+
  • Laravel 5.5+
  • A WhatsApp Business Platform phone number set up in Meta Business Manager
  • At least one pre-approved message template (business-initiated messages outside the 24h customer service window require an approved template — free-form text is not allowed)

Installation

composer require bahricanli/whatsapp-business

Laravel's auto-discovery registers the service provider automatically.

Publish the config file:

php artisan vendor:publish --tag=whatsapp-business-config

Configuration

Add the following to your .env:

WHATSAPP_BUSINESS_TOKEN=              # permanent system-user access token
WHATSAPP_BUSINESS_PHONE_NUMBER_ID=    # from Meta Business Manager > WhatsApp > API Setup
WHATSAPP_BUSINESS_API_VERSION=v21.0
WHATSAPP_BUSINESS_TIMEOUT=10

Or edit config/whatsapp-business.php directly.

Usage

1. Direct usage (no notification system)

use NotificationChannels\WhatsAppBusiness\WhatsAppFacade as WhatsAppBusiness;
use NotificationChannels\WhatsAppBusiness\WhatsAppMessage;

WhatsAppBusiness::sendMessage(
    WhatsAppMessage::create('appointment_created')
        ->language('tr')
        ->parameters(['Ahmet Uzman', '2026-08-01', '14:00'])
        ->to('905551234567')
);

2. As a Laravel notification channel

In your notification class:

use Illuminate\Notifications\Notification;
use NotificationChannels\WhatsAppBusiness\WhatsAppChannel;
use NotificationChannels\WhatsAppBusiness\WhatsAppMessage;

class AppointmentCreatedNotification extends Notification
{
    public function __construct(private array $appointment) {}

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

    public function toWhatsAppBusiness($notifiable): WhatsAppMessage
    {
        return WhatsAppMessage::create('appointment_created')
            ->parameters([
                $this->appointment['specialist'],
                $this->appointment['date'],
                $this->appointment['time'],
            ]);
        // Recipient is pulled from routeNotificationForWhatsAppBusiness()
        // if ->to() isn't called explicitly.
    }
}

In your notifiable model (e.g. Member):

public function routeNotificationForWhatsAppBusiness(): string
{
    return $this->phone_number; // e.g. 905551234567
}

3. Parallel with SMS (Corvass)

Send via both SMS and WhatsApp, falling back to SMS-only when WhatsApp is off:

public function via($notifiable): array
{
    if (! filter_var(env('WHATSAPP_ENABLED', false), FILTER_VALIDATE_BOOLEAN)) {
        return [\NotificationChannels\Corvass\CorvassChannel::class];
    }

    return [
        \NotificationChannels\WhatsAppBusiness\WhatsAppChannel::class,
        \NotificationChannels\Corvass\CorvassChannel::class,
    ];
}

Dynamic URL Button Templates

Some templates put their only variable in a button's URL instead of the body (e.g. a button registered as https://example.com/{{1}}). This is a separate placeholder from the body's, sent as its own component:

WhatsAppMessage::create('belge_dogrulama')
    ->buttonParameter('AL845') // fills https://tarti.me/{{1}} -> just the suffix, not the full URL
    ->to('905551234567');

You can combine parameters() (body) and buttonParameter() (button) on the same message if a template has both.

Free-Form Replies (24h Window)

If a contact has messaged you within the last 24 hours, you may reply with plain text — no approved template required:

use NotificationChannels\WhatsAppBusiness\WhatsAppFacade as WhatsAppBusiness;

WhatsAppBusiness::sendText('905551234567', 'Teşekkürler, hemen bakıyorum.');

Outside that window, Meta rejects free-form text — fall back to sendMessage() with an approved template.

Message Templates

Unlike a plain-text bridge, the official Cloud API requires every business-initiated message to use a pre-approved template. Submit templates for approval in Meta Business Manager, then reference the exact approved name/language in your notification:

WhatsAppMessage::create('phone_confirmation_code')
    ->language('tr')
    ->parameters(['482917']); // fills the template's {{1}}

The parameters() array maps positionally to {{1}}, {{2}}, ... in the template body — order matters and must match what was approved.

Phone Number Formats

The library normalizes phone numbers automatically:

Input Stored as
905551234567 905551234567
+905551234567 905551234567
0905551234567 905551234567
05551234567 905551234567
5551234567 905551234567

Events

Event Fired
SendingMessage Before a message is sent
MessageWasSent After successful delivery
use NotificationChannels\WhatsAppBusiness\Events\MessageWasSent;

Event::listen(MessageWasSent::class, function (MessageWasSent $event) {
    Log::info('WhatsApp Business sent', [
        'to'       => $event->message->to,
        'template' => $event->message->template,
        'wamid'    => $event->response['messages'][0]['id'] ?? null,
    ]);
});

License

MIT — see LICENSE.md.