<h2>O que são Webhooks e Por Que Importam</h2>
<p>Webhooks são mecanismos de comunicação orientada por eventos que permitem que sistemas externos notifiquem sua aplicação sobre ocorrências específicas em tempo real. Diferente de APIs tradicionais onde você faz requisições e aguarda respostas, webhooks funcionam em modelo push: um serviço externo envia dados para você quando algo acontece. Na prática, é como assinar um boletim informativo — você não precisa verificar constantemente se há novidades; elas chegam automaticamente.</p>
<p>Em PHP, receber webhooks significa criar um endpoint HTTP que aceita POST requests de serviços como Stripe, GitHub, Shopify ou APIs customizadas. Seu servidor processa esses eventos de forma assíncrona, permitindo atualizações em tempo real sem polling. Isso é fundamental para pagamentos, notificações de deployment, sincronização de dados e automações.</p>
<h2>Criando Seu Primeiro Webhook em PHP</h2>
<h3>Estrutura Básica do Endpoint</h3>
<p>O primeiro passo é criar um arquivo PHP que ouça requisições HTTP. Vamos usar um exemplo prático de um webhook de pagamento do Stripe:</p>
<pre><code class="language-php"><?php
// webhook.php
// Define o tipo de conteúdo como JSON
header('Content-Type: application/json');
// Lê o corpo da requisição
$input = file_get_contents('php://input');
$event = json_decode($input, true);
// Verifica se o JSON é válido
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'JSON inválido']);
exit;
}
// Registra o evento recebido (importante para debug)
file_put_contents('logs/webhook.log', date('Y-m-d H:i:s') . ' - ' . $input . PHP_EOL, FILE_APPEND);
// Retorna sucesso imediatamente
http_response_code(200);
echo json_encode(['status' => 'received']);
?></code></pre>
<p>Este código simples recebe qualquer dados POST, valida o JSON e retorna um status 200. O cliente (serviço externo) precisa receber um status 2xx rapidamente—geralmente em menos de 5 segundos. Sempre processe eventos de forma assíncrona, nunca bloqueie a resposta com lógica pesada.</p>
<h3>Validação e Segurança</h3>
<p>Nunca confie cegamente em dados de webhooks. Serviços respeitáveis enviam assinaturas (signatures) para validar que a requisição é legítima:</p>
<pre><code class="language-php"><?php
// webhook-seguro.php
define('WEBHOOK_SECRET', 'seu_secret_do_stripe');
header('Content-Type: application/json');
$input = file_get_contents('php://input');
$signature = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '';
// Valida a assinatura (exemplo Stripe)
$timestamp = explode(',', $signature)[0];
$hash = explode('=', explode(',', $signature)[1])[1] ?? '';
$signed_content = $timestamp . '.' . $input;
$expected_hash = hash_hmac('sha256', $signed_content, WEBHOOK_SECRET);
if (!hash_equals($hash, $expected_hash)) {
http_response_code(403);
echo json_encode(['error' => 'Assinatura inválida']);
exit;
}
$event = json_decode($input, true);
http_response_code(200);
echo json_encode(['status' => 'verified']);
?></code></pre>
<p>Esta abordagem usa HMAC-SHA256 para validar que a requisição veio realmente do Stripe. Sempre implemente essa validação em produção—é a diferença entre um sistema seguro e vulnerável.</p>
<h2>Processando Eventos com Lógica Robusta</h2>
<h3>Roteamento de Eventos</h3>
<p>Webhooks geralmente contêm um campo <code>type</code> ou <code>event</code> que indica qual evento ocorreu. Você deve rotear e processar cada tipo diferentemente:</p>
<pre><code class="language-php"><?php
// webhook-router.php
class WebhookProcessor {
private $validatedEvent;
public function __construct($event) {
$this->validatedEvent = $event;
}
public function process() {
$eventType = $this->validatedEvent['type'] ?? null;
switch ($eventType) {
case 'payment.success':
return $this->handlePaymentSuccess($this->validatedEvent['data']);
case 'payment.failed':
return $this->handlePaymentFailed($this->validatedEvent['data']);
case 'user.created':
return $this->handleUserCreated($this->validatedEvent['data']);
default:
error_log("Evento desconhecido: $eventType");
return false;
}
}
private function handlePaymentSuccess($data) {
// Atualiza status do pedido no banco
$orderId = $data['order_id'];
$amount = $data['amount'];
// Simula atualização DB
error_log("Pagamento recebido: Order #$orderId - R$ $amount");
// Envia email de confirmação
mail($data['customer_email'],
'Pagamento Confirmado',
'Seu pagamento foi processado com sucesso.');
return true;
}
private function handlePaymentFailed($data) {
error_log("Pagamento falhou: " . $data['error_message']);
return false;
}
private function handleUserCreated($data) {
// Sincroniza com CRM, envia boas-vindas, etc
error_log("Novo usuário: " . $data['email']);
return true;
}
}
// No seu arquivo webhook principal
$input = file_get_contents('php://input');
$event = json_decode($input, true);
$processor = new WebhookProcessor($event);
$processor->process();
http_response_code(200);
?></code></pre>
<h3>Implementação com Fila (Recomendado)</h3>
<p>Para aplicações críticas, adicione eventos a uma fila (Redis, RabbitMQ, ou até banco de dados) em vez de processar sincronamente:</p>
<pre><code class="language-php"><?php
// webhook-com-fila.php
class WebhookQueue {
private $redisClient;
public function __construct() {
$this->redisClient = new Redis();
$this->redisClient->connect('localhost', 6379);
}
public function enqueue($event) {
$queueKey = 'webhooks:' . $event['type'];
$this->redisClient->lpush($queueKey, json_encode($event));
error_log("Evento enfileirado: " . $event['type']);
return true;
}
}
// Seu webhook endpoint
$input = file_get_contents('php://input');
$event = json_decode($input, true);
// Valida assinatura aqui (código omitido por brevidade)
$queue = new WebhookQueue();
$queue->enqueue($event);
http_response_code(202); // Accepted - será processado em breve
echo json_encode(['status' => 'queued']);
?></code></pre>
<p>Com filas, você retorna 202 imediatamente e processa o evento em background via worker scripts (rodados com cron ou supervisord), aumentando confiabilidade e performance.</p>
<h2>Testes e Monitoramento</h2>
<h3>Testando Webhooks Localmente</h3>
<p>Use ferramentas como <code>ngrok</code> para expor seu servidor local à internet:</p>
<pre><code class="language-bash"># Terminal 1: Inicia seu servidor PHP
php -S localhost:8000
Terminal 2: Expõe via ngrok
ngrok http 8000
Isso gera URL pública como https://abc123.ngrok.io</code></pre>
<p>Então configure o webhook em seu serviço de teste apontando para <code>https://abc123.ngrok.io/webhook.php</code>. Você verá todas as requisições no terminal do ngrok em tempo real, facilitando debug.</p>
<h3>Logging e Monitoramento</h3>
<p>Sempre registre webhooks recebidos, respostas processadas e erros:</p>
<pre><code class="language-php"><?php
// webhook-logger.php
class WebhookLogger {
private $logFile;
public function __construct($logPath = 'logs/webhooks.log') {
$this->logFile = $logPath;
}
public function log($event, $status, $error = null) {
$entry = json_encode([
'timestamp' => date('c'),
'event_type' => $event['type'] ?? 'unknown',
'status' => $status,
'error' => $error,
'ip_source' => $_SERVER['REMOTE_ADDR']
]) . PHP_EOL;
file_put_contents($this->logFile, $entry, FILE_APPEND);
}
}
// Uso
$logger = new WebhookLogger();
try {
$processor->process();
$logger->log($event, 'success');
} catch (Exception $e) {
$logger->log($event, 'failed', $e->getMessage());
http_response_code(500);
}
?></code></pre>
<p>Revisar logs regularmente ajuda identificar padrões de falha e comportamentos anormais antes que causem problemas maiores.</p>
<h2>Conclusão</h2>
<p>Webhooks são essenciais para integrar sua aplicação PHP com serviços externos modernos. Os três pontos principais a lembrar são: (1) <strong>sempre valide e autentique</strong> requisições via assinaturas HMAC; (2) <strong>responda rapidamente</strong> com status 2xx e processe eventos de forma assíncrona usando filas quando possível; (3) <strong>registre tudo</strong> em logs estruturados para debugging e monitoramento de produção. Com essas práticas, você construirá sistemas robustos que reagem a eventos em tempo real sem sacrificar performance ou segurança.</p>
<h2>Referências</h2>
<ul>
<li>https://stripe.com/docs/webhooks</li>
<li>https://docs.github.com/en/developers/webhooks-and-events/webhooks</li>
<li>https://www.php.net/manual/en/book.hash.php</li>
<li>https://redis.io/</li>
<li>https://www.php-fig.org/psr/psr-18/</li>
</ul>