vntrungld / zalo-bot-php-sdk
PHP SDK for the Zalo Bot Platform API (https://bot.zapps.me/docs/)
Requires
- php: >=8.1
- ext-json: *
- guzzlehttp/guzzle: ^7.5
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2026-07-07 08:49:51 UTC
README
A typed PHP client for the Zalo Bot Platform API.
- Full coverage of the documented endpoints (
getMe,getUpdates, webhooks, and allsend*methods). - Typed model objects for every response — full IDE autocompletion.
- A webhook helper that verifies the secret token and parses payloads into typed updates.
- Built on Guzzle; PHP 8.1+.
Installation
composer require vntrungld/zalo-bot-php-sdk
Quick start
use ZaloBot\Client; $bot = new Client('123456789:your-bot-token'); $me = $bot->getMe(); echo $me->accountName; // "bot.VDKyGxQvc" $sent = $bot->sendMessage('chat_id_here', 'Xin chào 👋'); echo $sent->messageId;
The bot token is issued when you create a bot — see Create a bot.
Sending messages
use ZaloBot\Enum\ParseMode; use ZaloBot\Enum\ChatAction; use ZaloBot\Model\TextStyle; // Plain text $bot->sendMessage($chatId, 'Hello!'); // Markdown / HTML $bot->sendMessage($chatId, '**bold**', ParseMode::Markdown); // Inline styles: bold + colour over the first 7 characters $bot->sendMessage($chatId, 'Xin chào bạn', null, [ new TextStyle(start: 0, len: 7, styles: ['b', 'c_db342e']), ]); // Photo with a caption $bot->sendPhoto($chatId, 'https://example.com/image.jpg', 'A caption'); // Sticker (IDs from https://stickers.zaloapp.com/) $bot->sendSticker($chatId, 'sticker_id'); // Voice — 1-on-1 only, must be a public .aac URL $bot->sendVoice($userId, 'https://example.com/audio.aac'); // Typing indicator $bot->sendChatAction($chatId, ChatAction::Typing);
Receiving updates
Long polling (development)
foreach ($bot->getUpdates(timeout: 30) as $update) { if ($update->message->text !== null) { $bot->sendMessage($update->message->chat->id, 'You said: ' . $update->message->text); } }
getUpdatesdoes not work while a webhook is registered.
Webhooks (production)
Register your endpoint once:
$bot->setWebhook('https://your-app.com/webhook', 'your-8-to-256-char-secret');
Then handle incoming deliveries. WebhookHandler verifies the
X-Bot-Api-Secret-Token header (constant-time) and parses the body:
use ZaloBot\Webhook\WebhookHandler; use ZaloBot\Enum\EventName; use ZaloBot\Exception\WebhookException; $handler = new WebhookHandler('your-8-to-256-char-secret'); try { $update = $handler->handle( file_get_contents('php://input'), $_SERVER['HTTP_X_BOT_API_SECRET_TOKEN'] ?? null, ); } catch (WebhookException $e) { http_response_code(403); exit; } if ($update->event() === EventName::TextReceived) { // ... react to $update->message } // Acknowledge the delivery header('Content-Type: application/json'); echo json_encode(['ok' => true]);
Other webhook helpers: parse($rawBody) (parse without verifying) and
verifySignature($headerValue) (verify only).
Error handling
| Exception | Raised when |
|---|---|
ZaloBot\Exception\ApiException |
The API returns {"ok": false}. Exposes getErrorCode() and the description via getMessage(). |
ZaloBot\Exception\NetworkException |
A transport-level failure (connection, TLS, timeout). |
ZaloBot\Exception\WebhookException |
An incoming webhook fails verification or parsing. |
All three extend ZaloBot\Exception\ZaloBotException.
use ZaloBot\Exception\ApiException; try { $bot->sendMessage($chatId, 'hi'); } catch (ApiException $e) { // e.g. 401 invalid token, 429 quota exceeded error_log("Zalo API error {$e->getErrorCode()}: {$e->getMessage()}"); }
See the error code reference.
API reference
| Method | Returns |
|---|---|
getMe() |
Model\BotInfo |
getUpdates(int $timeout = 30) |
list<Model\Update> |
setWebhook(string $url, string $secretToken) |
Model\WebhookInfo |
deleteWebhook() |
Model\WebhookInfo |
getWebhookInfo() |
Model\WebhookInfo |
sendMessage(string $chatId, string $text, ?ParseMode $parseMode = null, ?array $textStyles = null) |
Model\SentMessage |
sendPhoto(string $chatId, string $photo, ?string $caption = null) |
Model\SentMessage |
sendSticker(string $chatId, string $sticker) |
Model\SentMessage |
sendVoice(string $chatId, string $voiceUrl) |
Model\SentMessage |
sendChatAction(string $chatId, ChatAction $action) |
bool |
Configuration
You can inject a preconfigured Guzzle client (e.g. to set proxies, timeouts, or middleware) or override the base URL:
use GuzzleHttp\Client as GuzzleClient; $bot = new Client( token: '123:abc', http: new GuzzleClient(['timeout' => 10]), );
Examples
Runnable scripts live in examples/:
send_message.php— send text, a photo, and a typing indicator.long_polling.php— a minimal long-polling echo bot.webhook.php— a standalone HTTPS webhook endpoint.
Development
composer install composer test # PHPUnit composer analyse # PHPStan (max level) composer cs # PHP-CS-Fixer (dry-run); use `composer cs:fix` to apply composer check # all of the above
License
MIT