Skip to content

renzojohnson/whatsapp-webhook

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WhatsApp Webhook

Latest Version PHP Version License

Lightweight WhatsApp Cloud API webhook parser. Typed notifications for messages, statuses, and errors. HMAC signature verification. Zero dependencies.

Author: Renzo Johnson

Requirements

  • PHP 8.4 or higher (uses readonly classes and enums)
  • ext-json
  • No other extensions or libraries required (zero dependencies)

Installation

composer require renzojohnson/whatsapp-webhook

Quick Start

use RenzoJohnson\WhatsAppWebhook\WebhookHandler;
use RenzoJohnson\WhatsAppWebhook\MessageType;
use RenzoJohnson\WhatsAppWebhook\Notification\MessageNotification;

$handler = new WebhookHandler('your-app-secret');

// Register listeners
$handler->onMessage(MessageType::Text, function (MessageNotification $msg): void {
    echo "Message from {$msg->from}: {$msg->text()}";
});

// Verify signature and handle
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';

if (!$handler->verifySignature($rawBody, $signature)) {
    http_response_code(401);
    exit;
}

$handler->handle($rawBody);
http_response_code(200);

Webhook Endpoint Setup

use RenzoJohnson\WhatsAppWebhook\WebhookHandler;
use RenzoJohnson\WhatsAppWebhook\WebhookException;

$handler = new WebhookHandler('your-app-secret');

// Step 1: Handle Meta's verification challenge (GET)
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $challenge = $handler->handleVerification($_GET, 'your-verify-token');

    if ($challenge !== null) {
        echo $challenge;
        exit;
    }

    http_response_code(403);
    exit;
}

// Step 2: Verify signature (POST)
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';

if (!$handler->verifySignature($rawBody, $signature)) {
    http_response_code(401);
    exit;
}

// Step 3: Parse and handle
try {
    $notifications = $handler->handle($rawBody);
    http_response_code(200);
} catch (WebhookException $e) {
    http_response_code(400);
}

Message Types

Type Enum Description
text MessageType::Text Plain text message
image MessageType::Image Image with optional caption
video MessageType::Video Video with optional caption
audio MessageType::Audio Audio message
document MessageType::Document Document with filename
sticker MessageType::Sticker Sticker
location MessageType::Location GPS coordinates with name/address
contacts MessageType::Contacts Contact cards
button MessageType::Button Quick reply button tap
interactive MessageType::Interactive Button reply or list selection
reaction MessageType::Reaction Emoji reaction
order MessageType::Order Product order

Listen by Message Type

use RenzoJohnson\WhatsAppWebhook\MessageType;
use RenzoJohnson\WhatsAppWebhook\Notification\MessageNotification;

$handler->onMessage(MessageType::Text, function (MessageNotification $msg): void {
    echo $msg->text();
});

$handler->onMessage(MessageType::Image, function (MessageNotification $msg): void {
    echo "Image: {$msg->mediaId()} ({$msg->mediaMimeType()})";
    echo "Caption: {$msg->caption()}";
});

$handler->onMessage(MessageType::Location, function (MessageNotification $msg): void {
    echo "Location: {$msg->latitude()}, {$msg->longitude()}";
    echo "Name: {$msg->locationName()}";
});

$handler->onMessage(MessageType::Interactive, function (MessageNotification $msg): void {
    if ($msg->interactiveType() === 'button_reply') {
        echo "Button: {$msg->interactiveButtonTitle()} (ID: {$msg->interactiveButtonId()})";
    }
    if ($msg->interactiveType() === 'list_reply') {
        echo "List: {$msg->interactiveListTitle()}";
    }
});

$handler->onMessage(MessageType::Reaction, function (MessageNotification $msg): void {
    echo "Reacted with {$msg->reactionEmoji()} to {$msg->reactionMessageId()}";
});

Listen to All Messages

$handler->onAnyMessage(function (MessageNotification $msg): void {
    error_log($msg->getSummary());
});

Status Updates

use RenzoJohnson\WhatsAppWebhook\StatusType;
use RenzoJohnson\WhatsAppWebhook\Notification\StatusNotification;

$handler->onStatus(StatusType::Delivered, function (StatusNotification $status): void {
    echo "Delivered to {$status->recipientId}";
});

$handler->onStatus(StatusType::Read, function (StatusNotification $status): void {
    echo "Read by {$status->recipientId}";
});

$handler->onStatus(StatusType::Failed, function (StatusNotification $status): void {
    if ($status->hasErrors()) {
        echo "Error {$status->errorCode()}: {$status->errorTitle()}";
    }
});

$handler->onAnyStatus(function (StatusNotification $status): void {
    error_log($status->getSummary());
});

Pricing and Conversation Info

$handler->onAnyStatus(function (StatusNotification $status): void {
    if ($status->isBillable()) {
        echo "Category: {$status->pricingCategory()}";
        echo "Model: {$status->pricingModel()}";
    }

    if ($status->conversationId()) {
        echo "Conversation: {$status->conversationId()}";
        echo "Origin: {$status->conversationOrigin()}";
    }
});

Forwarded Messages

$handler->onAnyMessage(function (MessageNotification $msg): void {
    if ($msg->isForwarded()) {
        echo "This message was forwarded";
    }

    if ($msg->isFrequentlyForwarded()) {
        echo "This message has been forwarded many times";
    }

    if ($msg->contextMessageId()) {
        echo "Reply to: {$msg->contextMessageId()}";
    }
});

Parse Without Dispatching

$notifications = $handler->parse($rawBody);

foreach ($notifications as $notification) {
    if ($notification instanceof MessageNotification) {
        echo $notification->getSummary();
    }
}

Error Handling

use RenzoJohnson\WhatsAppWebhook\WebhookException;

try {
    $handler->parse($rawBody);
} catch (WebhookException $e) {
    // Invalid JSON, wrong object type, empty entry
}

Related Packages

Links

License

MIT License. Copyright (c) 2026 Renzo Johnson.

About

Lightweight WhatsApp Cloud API webhook parser. Typed notifications, HMAC signature verification, zero dependencies.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages