Recepção de eventos externos de Google Ads, Meta Ads e formulários próprios, com processamento assíncrono de leads e registro de conversões offline.
| Origem | Método | Rota |
|---|---|---|
| Google Ads | POST | /webhooks/google |
| Meta Ads (verificação) | GET | /webhooks/meta?hub.mode=subscribe&hub.verify_token=...&hub.challenge=... |
| Meta Ads (eventos) | POST | /webhooks/meta |
| Formulário próprio | POST | /integracoes/leads |
Esses endpoints públicos não usam CSRF. O formulário próprio autentica as requisições com HMAC; não é necessário nem suficiente cadastrar a URL da landing page em um canal Ads.
WEBHOOK_ALLOW_UNSIGNED=false)..env: WEBHOOK_GOOGLE_SECRET, WEBHOOK_META_SECRET.WEBHOOK_META_VERIFY_TOKEN usado na verificação inicial.FORMULARIO_SECRET (segredo HMAC compartilhado entre a plataforma e o backend da landing page) e FORMULARIO_TIMESTAMP_TOLERANCE (padrão 300 segundos).WEBHOOK_ALLOW_UNSIGNED deve permanecer false em produção. A assinatura do formulário é sha256=<hex> em X-Formulario-Signature, calculada sobre o corpo HTTP bruto com HMAC-SHA256.O endpoint POST /integracoes/leads recebe envios do backend da landing page e encaminha-os para o mesmo armazenamento de eventos e fila usados pelos webhooks. A implementação não busca a página a partir de uma URL informada no canal Ads. A landing page precisa ter código server-side (ou uma função serverless) que receba o formulário, gere o JSON, assine o corpo bruto com FORMULARIO_SECRET e envie o POST ao endpoint.
O JSON exige event_type, id, timestamp (Unix), campanha_id e nome. id deve ser único por envio. campanha_id precisa existir; durante o processamento, o sistema resolve o cliente e o usuário pela campanha e cria o lead com origem formulario_proprio. E-mail, telefone, UTMs, gclid, fbclid e mensagem são opcionais. A assinatura deve ser criada no servidor: nunca coloque o segredo no HTML ou JavaScript do navegador.
Respostas principais:
| HTTP | Significado |
|---|---|
202 |
Evento autenticado e enfileirado; o lead ainda será processado |
200 |
Evento anterior já está processando/processado (already_accepted) |
401 |
Assinatura ausente/inválida ou timestamp fora da janela |
422 |
Corpo vazio, JSON inválido ou campo obrigatório ausente |
Um reenvio feito enquanto o primeiro evento ainda está pendente pode receber 202 e enfileirar outro job; 200 already_accepted é retornado quando o evento existente já está processando ou foi processado. O job confere se o lead com aquele ID de formulário já existe antes de criar outro.
Um 202 confirma o recebimento/enfileiramento, não o sucesso final de criação do lead. Verifique o resultado em /admin/webhooks; o job pode falhar depois da resposta, por exemplo, se campanha_id não existir.
Veja CONECTOR-FORMULARIO-PROPRIO.md para configuração, payload e exemplos de código.
1. Google/Meta envia POST → /webhooks/{origem}; ou o backend do formulário envia POST → /integracoes/leads
2. Controller valida a requisição e a assinatura HMAC (o formulário também valida o `timestamp` do JSON)
3. Persiste em webhook_eventos (status=recebido)
4. Enfileira ProcessarWebhookJob na fila 'webhooks'
5. Worker (bin/console queue:work webhooks) processa
6. Job cria/atualiza lead conforme tipo de evento, incluindo formulário próprio
7. webhook_eventos.status vira 'processado' ou 'erro'
database (tabela fila_jobs).Uma vez (teste):
php bin/console queue:once webhooks
Contínuo (produção):
php bin/console queue:work webhooks
No cron (recomendado em hospedagem compartilhada sem systemd):
* * * * * cd /caminho && php bin/console queue:once webhooks >> logs/queue.log 2>&1
Como serviço systemd (VPS):
# /etc/systemd/system/gt-queue.service
[Unit]
Description=Gestão de Tráfego — Queue Worker
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/gestao-trafego
ExecStart=/usr/bin/php bin/console queue:work webhooks
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
/admin/fila — status em tempo real./admin/webhooks — eventos recebidos./health — inclui status da fila se configurado.php bin/console queue:status.# Enviar webhook de teste (dev, sem assinatura)
curl -X POST http://localhost/gestao-trafego/webhooks/google \
-H "Content-Type: application/json" \
-d '{"event_type":"conversion","gclid":"abc123","cliente_id":1,"full_name":"Teste"}'
Se WEBHOOK_ALLOW_UNSIGNED=true (dev), o evento é aceito.
php bin/console queue:prune 7 # remove jobs de +7 dias
# em produção, adicione em cron semanal