SDK oficial do painelzap: envio de mensagens e midia pelo WhatsApp, grupos, validacao de numero e verificacao de webhooks. Com integracao para Laravel.

Maintainers

Package info

github.com/wlfonseca/painelzap-sdk-php

pkg:composer/painelzap/sdk

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.0 2026-08-07 12:50 UTC

This package is auto-updated.

Last update: 2026-08-07 12:53:07 UTC


README

SDK oficial do painelzap para PHP: envio de mensagens e mídia pelo WhatsApp, grupos, validação de número e verificação de webhooks. Com integração pronta para Laravel.

composer require painelzap/sdk

Requer PHP 8.0.2+.

Uso direto (qualquer projeto PHP)

Pegue a appkey em Apps → Integração e a authkey no seu perfil.

use Painelzap\Painelzap;

$zap = new Painelzap([
    'authkey' => getenv('PAINELZAP_AUTHKEY'),
    'appkey'  => getenv('PAINELZAP_APPKEY'),
]);

$zap->sendMessage('5531988888888', 'Olá! Sua entrega chega hoje.');

Laravel

O provider e a facade são descobertos sozinhos. Publique a configuração:

php artisan vendor:publish --tag=painelzap-config

E preencha o .env:

PAINELZAP_AUTHKEY=...
PAINELZAP_APPKEY=...
PAINELZAP_WEBHOOK_SECRET=...
use Painelzap\Laravel\PainelzapFacade as Painelzap;

Painelzap::sendMessage('5531988888888', 'Olá!');

Ou injetando, se preferir testar com mock:

public function __construct(private \Painelzap\Painelzap $zap) {}

Mensagens

$zap->sendMessage('5531988888888', 'Olá!');

$zap->sendTemplate('5531988888888', 'uuid-do-template', [
    'nome'   => 'Maria',
    'pedido' => '1234',
]);

Mídia

Por URL pública (precisa ser http/https e resolver para um IP público):

$zap->sendMediaUrl('5531988888888', 'https://exemplo.com/nota.pdf', 'Segue sua nota fiscal');

Por upload:

$zap->sendMediaFile('5531988888888', storage_path('app/foto.jpg'), 'Olha a foto');

Áudio como mensagem de voz (com a onda sonora, não anexo). O WhatsApp espera ogg/opus — outros codecs podem não tocar em todos os aparelhos:

$zap->sendMediaUrl('5531988888888', 'https://exemplo.com/recado.ogg', ptt: true);

O tipo vem da extensão; passe mediaType (image, video, audio, document) para forçar.

Confirmar que a mensagem chegou

Todo envio devolve message_id. Guarde-o e acompanhe o evento message.status:

$r = $zap->sendMessage('5531988888888', 'Olá!');
$id = $r['message_id'];

sent não é entrega. Significa apenas que o servidor do WhatsApp aceitou. Entrega no aparelho é delivered. Mensagem parada em sent por muito tempo não chegou.

A progressão é sentdeliveredread (e played em áudio); error indica falha.

Histórico da conversa

$mensagens = $zap->history('5531988888888', 50);

foreach ($mensagens as $m) {
    echo $m['from_me'] ? 'eu: ' : 'ele: ', $m['message'], ' [', $m['status'] ?? '-', "]\n";
}

Vem das mais recentes para as mais antigas, incluindo o que você enviou (com o status de entrega; nas recebidas o status é null). Para paginar, passe before com o sent_at da última linha — aceita string ISO ou DateTimeInterface:

$anteriores = $zap->history('5531988888888', 50, end($mensagens)['sent_at']);

O histórico guarda os últimos 30 dias e começa a partir de quando o recurso foi ativado. O arquivo da mídia não fica guardado (só o tipo e o nome); para receber a mídia, use o webhook.

Grupos

$grupos = $zap->groups();

$grupo = $zap->group($grupos[0]['id']);
echo $grupo['name'], count($grupo['participants']);

$zap->sendGroupMessage($grupos[0]['id'], 'Bom dia, time!');

Sobre os participantes: o WhatsApp migrou os contatos para LID, então $participante['id'] vem como ...@lid, que não é telefone. O campo number traz o telefone quando o WhatsApp o expõe, e vem null quando não. Use id como chave estável.

Validar número

Não envia mensagem e não consome a cota do plano:

if (! $zap->numberExists('5531988888888')) {
    return;
}

// ou, com o retorno completo
['exists' => $existe, 'jid' => $jid] = $zap->checkNumber('5531988888888');

Webhooks

Configure o endereço em Dispositivos → editar dispositivo → Webhook. Cada entrega vem assinada no header X-Painelzap-Signature.

No Laravel, use o middleware — ele recusa a requisição antes de chegar no seu controller:

Route::post('/webhook', [WebhookController::class, 'handle'])
    ->middleware('painelzap.webhook');
public function handle(Request $request)
{
    $evento = $request->json()->all();

    // Responda rápido: fora da faixa 2xx a entrega é retentada
    // 5 vezes (10s, 1min, 5min, 15min).
    if ($evento['event'] === 'message.received' && ! empty($evento['data']['media']['url'])) {
        // A mídia fica disponível por 3 dias — baixe e guarde do seu lado.
        BaixarMidia::dispatch($evento['data']['media']['url']);
    }

    return response()->noContent();
}

Fora do Laravel, ou para verificar na mão:

use Painelzap\Webhooks;

$corpo = file_get_contents('php://input');
$assinatura = $_SERVER['HTTP_X_PAINELZAP_SIGNATURE'] ?? null;

// Lança se não conferir:
$evento = Webhooks::parse($corpo, $assinatura, $segredo);

// Ou, se preferir decidir você mesmo:
if (! Webhooks::verify($corpo, $assinatura, $segredo)) {
    http_response_code(401);
    exit;
}

Use o corpo cru. Se você re-serializar o array já decodificado, os bytes mudam e a assinatura nunca bate — por isso o middleware usa $request->getContent().

Eventos

event Quando dispara
message.received mensagem recebida na conversa individual
message.group mensagem em grupo — group_id e author preenchidos
message.status mudou o status do que você enviou — data traz message_id, chat_jid e status
session.status dispositivo conectou ou caiu (data.status)
webhook.test botão Send test event da tela do dispositivo

data.type é text, image, audio, video, document ou sticker. Mídia sem legenda chega com message: null.

Erros

Toda falha vira PainelzapException, com a mensagem que a API devolveu:

use Painelzap\PainelzapException;

try {
    $zap->sendMessage('5531988888888', 'Olá');
} catch (PainelzapException $e) {
    if ($e->naoAutorizado()) { /* chave inválida, app de outra conta, ou cota estourada */ }
    if ($e->invalido())      { /* parâmetro faltando — veja $e->body() */ }

    report($e);
}

Testes

composer install
composer test

Licença

MIT