Biblioteca PHP 8.1+ para integração com a Meta Cloud API (WhatsApp Business).
Construída com Clean Architecture, Fluent Interface, DTOs e client HTTP desacoplado — sem expor nenhum payload bruto da Meta ao consumidor da biblioteca.
Autor: Mário Lucas
Criação: 12 de abril de 2026
Licença: MIT
| Requisito | Versão |
|---|---|
| PHP | ^8.1 |
| Guzzle | ^7.0 |
composer require sixtec/wbapiUse WBMetaClient quando sua aplicação precisa de injeção de dependência, múltiplos números, multi-tenant ou workers long-running:
use Sixtec\WBApi\Config\WBMetaConfig;
use Sixtec\WBApi\WBMetaClient;
$client = WBMetaClient::fromConfig(new WBMetaConfig(
accessToken: 'EAAxxxxxxxxxxxxxxx',
phoneNumberId: '123456789012345',
apiVersion: 'v19.0',
webhookVerifyToken: 'meu-token-secreto',
));
$client->to('+5511999999999')
->text('Olá, tudo bem?')
->send();A facade continua disponível para aplicações simples:
use Sixtec\WBApi\WBMeta;
use Sixtec\WBApi\Config\WBMetaConfig;
WBMeta::configure(new WBMetaConfig(
accessToken: 'EAAxxxxxxxxxxxxxxx',
phoneNumberId: '123456789012345',
apiVersion: 'v19.0', // opcional, padrão: v19.0
webhookVerifyToken: 'meu-token-secreto', // necessário para webhooks
retryAttempts: 3, // opcional, padrão: 3
timeout: 30.0, // opcional, padrão: 30s
));WBMeta::to('+5511999999999')
->text('Olá, tudo bem?')
->send();WBMeta::to('+5511999999999')
->text('Acesse: https://example.com', previewUrl: true)
->send();WBMeta::to('+5511999999999')
->image('https://example.com/foto.jpg', 'Legenda opcional')
->send();WBMeta::to('+5511999999999')
->video('https://example.com/video.mp4', 'Legenda opcional')
->send();WBMeta::to('+5511999999999')
->audio('https://example.com/audio.ogg')
->send();WBMeta::to('+5511999999999')
->document('https://example.com/relatorio.pdf', 'relatorio.pdf', 'Relatório Q1')
->send();WBMeta::to('+5511999999999')
->sticker('https://example.com/sticker.webp')
->send();WBMeta::to('+5511999999999')
->replyTo('wamid.HBgLNTUx...')
->text('Resposta vinculada à mensagem original')
->send();WBMeta::to('+5511999999999')
->reaction('wamid.HBgLNTUx...', '👍')
->send();
WBMeta::to('+5511999999999')
->removeReaction('wamid.HBgLNTUx...')
->send();WBMeta::to('+5511999999999')
->location(-8.0476, -34.8770, 'Recife', 'Recife, PE')
->send();WBMeta::to('+5511999999999')
->contact('Maria Silva', '+55 11 99999-9999', 'Maria')
->send();Para contatos completos, informe diretamente os objetos no formato da Meta:
WBMeta::to('+5511999999999')
->contacts([
[
'name' => [
'formatted_name' => 'Maria Silva',
'first_name' => 'Maria',
],
'phones' => [
['phone' => '5511999999999', 'type' => 'CELL'],
],
],
])
->send();WBMeta::to('+5511999999999')
->buttons('Escolha uma opção', [
['id' => 'confirm', 'title' => 'Confirmar'],
['id' => 'cancel', 'title' => 'Cancelar'],
])
->send();WBMeta::to('+5511999999999')
->list('Escolha um item', 'Ver opções', [
[
'title' => 'Produtos',
'rows' => [
['id' => 'sku-1', 'title' => 'Produto 1'],
['id' => 'sku-2', 'title' => 'Produto 2'],
],
],
])
->send();WBMeta::to('+5511999999999')
->product('Veja este produto', '1234567890', 'sku-1')
->send();WBMeta::to('+5511999999999')
->productList('Veja estes produtos', '1234567890', [
[
'title' => 'Produtos',
'product_items' => [
['product_retailer_id' => 'sku-1'],
['product_retailer_id' => 'sku-2'],
],
],
])
->send();WBMeta::to('+5511999999999')
->interactive([
'type' => 'button',
'body' => ['text' => 'Escolha uma opção'],
'action' => [
'buttons' => [
[
'type' => 'reply',
'reply' => ['id' => 'yes', 'title' => 'Sim'],
],
],
],
])
->send();WBMeta::markAsRead('wamid.HBgLNTUx...');use Sixtec\WBApi\DTOs\TemplateComponentDTO;
use Sixtec\WBApi\DTOs\TemplateParameterDTO;
WBMeta::to('+5511999999999')
->template('hello_world', 'pt_BR', [
new TemplateComponentDTO(
type: 'body',
parameters: [
new TemplateParameterDTO(type: 'text', value: 'João'),
],
),
])
->send();Todos os métodos send() retornam um MessageResponseDTO:
use Sixtec\WBApi\DTOs\MessageResponseDTO;
$response = WBMeta::to('+5511999999999')->text('Olá!')->send();
echo $response->messageId->getValue(); // wamid.HBgLNTUxMTk...
echo $response->status; // accepted
echo $response->to; // 5511999999999$handler = WBMeta::webhook();
$challenge = $handler->verify(
$_GET['hub_mode'],
$_GET['hub_verify_token'],
$_GET['hub_challenge'],
);
http_response_code(200);
echo $challenge;use Sixtec\WBApi\Webhook\Events\MessageReceivedEvent;
use Sixtec\WBApi\Webhook\Events\MessageDeliveredEvent;
use Sixtec\WBApi\Webhook\Events\MessageReadEvent;
$payload = json_decode(file_get_contents('php://input'), true);
$events = WBMeta::webhook()->handle($payload);
foreach ($events as $event) {
match (true) {
$event instanceof MessageReceivedEvent => handleReceived($event),
$event instanceof MessageDeliveredEvent => handleDelivered($event),
$event instanceof MessageReadEvent => handleRead($event),
};
}
function handleReceived(MessageReceivedEvent $event): void
{
// $event->messageId, $event->from, $event->type
// $event->textBody — preenchido quando type === 'text'
// $event->mediaData — preenchido para tipos de mídia
}src/
├── WBMeta.php # Facade estática (ponto de entrada)
├── WBMetaClient.php # Cliente instanciável para DI/multi-tenant
├── Config/
│ └── WBMetaConfig.php # DTO de configuração
├── Contracts/
│ ├── HttpClientInterface.php # Contrato do client HTTP
│ └── TokenStorageInterface.php # Contrato de armazenamento de token
├── Domain/
│ ├── Entities/ # Message, Contact, Conversation, Template
│ └── ValueObjects/ # PhoneNumber, MessageId, MediaUrl
├── DTOs/ # Objetos de transferência (entrada/saída)
│ ├── SendTextMessageDTO.php
│ ├── SendTemplateMessageDTO.php
│ ├── SendMediaMessageDTO.php
│ ├── MessageResponseDTO.php
│ ├── TemplateComponentDTO.php
│ ├── TemplateParameterDTO.php
│ └── MediaType.php (enum)
├── Http/
│ ├── GuzzleHttpClient.php # Adapter Guzzle + retry automático
│ └── HttpResponse.php
├── Auth/
│ ├── TokenManager.php
│ └── InMemoryTokenStorage.php
├── Mappers/ # Convertem DTOs → payload Meta
│ ├── TextMessageMapper.php
│ ├── TemplateMessageMapper.php
│ └── MediaMessageMapper.php
├── Services/
│ └── MessagingService.php # Orquestra envio, desacoplado via interface
├── Builders/
│ └── MessageBuilder.php # Fluent interface
├── Webhook/
│ ├── WebhookHandler.php # verify() + handle()
│ ├── WebhookPayloadParser.php
│ └── Events/
│ ├── MessageReceivedEvent.php
│ ├── MessageDeliveredEvent.php
│ └── MessageReadEvent.php
└── Exceptions/
├── WBMetaException.php
├── HttpException.php
└── WebhookVerificationException.php
| Princípio | Aplicação |
|---|---|
| Clean Architecture | Domain isolado de infraestrutura (HTTP, Auth) |
| Fluent Interface | WBMetaClient::fromConfig($config)->to()->text()->send() e WBMeta::to()->text()->send() |
| DTOs | Nunca expostos payloads brutos da Meta |
| Mappers | Conversão DTO → payload em classes dedicadas |
| Dependency Inversion | HttpClientInterface e TokenStorageInterface |
| PSR-4 | Autoload Sixtec\WBApi\ → src/ |
Para integrar em um container DI (Laravel, Symfony, etc.), registre WBMetaClient:
use Sixtec\WBApi\Config\WBMetaConfig;
use Sixtec\WBApi\WBMetaClient;
$client = WBMetaClient::fromConfig(
new WBMetaConfig(accessToken: env('WA_TOKEN'), phoneNumberId: env('WA_PHONE_ID')),
);
// Registrar no container e injetar onde necessárioSe precisar controlar o transporte HTTP, injete um client compatível com HttpClientInterface:
use Sixtec\WBApi\Config\WBMetaConfig;
use Sixtec\WBApi\Tests\Fakes\FakeHttpClient;
use Sixtec\WBApi\WBMetaClient;
$config = new WBMetaConfig(accessToken: env('WA_TOKEN'), phoneNumberId: env('WA_PHONE_ID'));
$client = WBMetaClient::fromConfig($config, new FakeHttpClient());
// Registrar no container e injetar onde necessário# Todos os testes
./vendor/bin/phpunit
# Apenas unitários
./vendor/bin/phpunit --testsuite Unit
# Apenas integração
./vendor/bin/phpunit --testsuite IntegrationA suite usa FakeHttpClient — sem chamadas reais à API.
Para testar com sua própria lógica, injete um FakeHttpClient em WBMetaClient::fromConfig():
use Sixtec\WBApi\Tests\Fakes\FakeHttpClient;
use Sixtec\WBApi\WBMetaClient;
$fake = new FakeHttpClient();
$fake->addResponse(200, [
'contacts' => [['input' => '5511999999999']],
'messages' => [['id' => 'wamid.test01', 'message_status' => 'accepted']],
]);
$client = WBMetaClient::fromConfig($config, $fake);| Classe | Quando é lançada |
|---|---|
WBMetaException |
Base — erros gerais da biblioteca |
HttpException |
Resposta HTTP não-2xx ou falha de rede |
WebhookVerificationException |
Token ou mode inválido no desafio do webhook |
MIT © Mário Lucas