sixtec/wbapi

PHP 8.1+ library for WhatsApp Business Cloud API (Meta) integration

Maintainers

Package info

github.com/sixtec/wbapi

pkg:composer/sixtec/wbapi

Statistics

Installs: 25

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

v1.0.0-alpha 2026-04-30 21:52 UTC

This package is not auto-updated.

Last update: 2026-05-01 22:03:17 UTC


README

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

Requisitos

Requisito Versão
PHP ^8.1
Guzzle ^7.0

Instalação

composer require sixtec/wbapi

Configuração

Cliente instanciável

Use 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();

Facade estática

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
));

Envio de Mensagens

Texto simples

WBMeta::to('+5511999999999')
    ->text('Olá, tudo bem?')
    ->send();

Texto com preview de URL

WBMeta::to('+5511999999999')
    ->text('Acesse: https://example.com', previewUrl: true)
    ->send();

Imagem

WBMeta::to('+5511999999999')
    ->image('https://example.com/foto.jpg', 'Legenda opcional')
    ->send();

Vídeo

WBMeta::to('+5511999999999')
    ->video('https://example.com/video.mp4', 'Legenda opcional')
    ->send();

Áudio

WBMeta::to('+5511999999999')
    ->audio('https://example.com/audio.ogg')
    ->send();

Documento

WBMeta::to('+5511999999999')
    ->document('https://example.com/relatorio.pdf', 'relatorio.pdf', 'Relatório Q1')
    ->send();

Sticker

WBMeta::to('+5511999999999')
    ->sticker('https://example.com/sticker.webp')
    ->send();

Responder uma mensagem

WBMeta::to('+5511999999999')
    ->replyTo('wamid.HBgLNTUx...')
    ->text('Resposta vinculada à mensagem original')
    ->send();

Reação

WBMeta::to('+5511999999999')
    ->reaction('wamid.HBgLNTUx...', '👍')
    ->send();

WBMeta::to('+5511999999999')
    ->removeReaction('wamid.HBgLNTUx...')
    ->send();

Localização

WBMeta::to('+5511999999999')
    ->location(-8.0476, -34.8770, 'Recife', 'Recife, PE')
    ->send();

Contato

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();

Botões interativos

WBMeta::to('+5511999999999')
    ->buttons('Escolha uma opção', [
        ['id' => 'confirm', 'title' => 'Confirmar'],
        ['id' => 'cancel', 'title' => 'Cancelar'],
    ])
    ->send();

Lista interativa

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();

Produto de catálogo

WBMeta::to('+5511999999999')
    ->product('Veja este produto', '1234567890', 'sku-1')
    ->send();

Lista de produtos de catálogo

WBMeta::to('+5511999999999')
    ->productList('Veja estes produtos', '1234567890', [
        [
            'title' => 'Produtos',
            'product_items' => [
                ['product_retailer_id' => 'sku-1'],
                ['product_retailer_id' => 'sku-2'],
            ],
        ],
    ])
    ->send();

Payload interativo avançado

WBMeta::to('+5511999999999')
    ->interactive([
        'type' => 'button',
        'body' => ['text' => 'Escolha uma opção'],
        'action' => [
            'buttons' => [
                [
                    'type' => 'reply',
                    'reply' => ['id' => 'yes', 'title' => 'Sim'],
                ],
            ],
        ],
    ])
    ->send();

Marcar mensagem como lida

WBMeta::markAsRead('wamid.HBgLNTUx...');

Template

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();

Retorno

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

Webhooks

1. Verificação do Desafio (GET)

$handler   = WBMeta::webhook();
$challenge = $handler->verify(
    $_GET['hub_mode'],
    $_GET['hub_verify_token'],
    $_GET['hub_challenge'],
);

http_response_code(200);
echo $challenge;

2. Recebimento de Eventos (POST)

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
}

Arquitetura

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ípios aplicados

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/

Injeção de Dependências / Frameworks

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ário

Se 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

Testes

# Todos os testes
./vendor/bin/phpunit

# Apenas unitários
./vendor/bin/phpunit --testsuite Unit

# Apenas integração
./vendor/bin/phpunit --testsuite Integration

A 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);

Exceções

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

Licença

MIT © Mário Lucas