Integração de Webhooks
Receba notificações em tempo real quando eventos acontecem na sua conta stryhub. Cada webhook é assinado com HMAC-SHA256, entregue com tentativas automáticas e registrado para total visibilidade.
Visão Geral
A stryhub envia requisições HTTP POST para as URLs de endpoint configuradas sempre que eventos significativos ocorrem — como pagamentos concluídos, alterações de assinatura ou novos cadastros de clientes.
Diferente dos webhooks brutos do Stripe, a stryhub entrega payloads simplificados e consistentes que são fáceis de interpretar e processar. Você não precisa entender a estrutura de eventos do Stripe — apenas trate os eventos da stryhub.
Configuração
Para configurar um endpoint de webhook:
- Acesse Webhooks no seu painel administrativo
- Clique em New Integration
- Insira a URL onde deseja receber os eventos (deve ser HTTPS em produção)
- Adicione uma descrição opcional (ex.: "Servidor de Produção" ou "Ambiente de Staging")
- Selecione quais eventos deseja receber (todos são selecionados por padrão)
- Clique em Create Integration
Após a criação, você receberá uma chave de assinatura que começa com whsec_. Armazene-a com segurança — você precisará dela para verificar as assinaturas dos webhooks.
Formato do Payload
Cada entrega de webhook contém um payload JSON com esta estrutura:
{
"id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "payment.completed",
"created_at": "2026-02-16T14:30:00.000Z",
"data": {
"customer": {
"email": "john@example.com",
"name": "John Doe",
"stripe_customer_id": "cus_abc123"
},
"amount": 49.99,
"net_amount": 48.49,
"platform_fee": 1.50,
"currency": "usd",
"subscription_id": "sub_xyz789",
"mode": "subscription"
}
}
| Campo | Tipo | Descrição |
|---|---|---|
id |
string | Identificador único do evento (UUID v4) |
type |
string | Tipo do evento (ex.: payment.completed) |
created_at |
string | Timestamp ISO 8601 |
data |
object | Dados específicos do evento (varia conforme o tipo de evento) |
Headers HTTP
Cada requisição de webhook inclui estes headers:
| Header | Exemplo | Descrição |
|---|---|---|
Content-Type |
application/json |
Sempre JSON |
X-Stryhub-Signature |
t=1708100000,v1=abc123... |
Assinatura HMAC-SHA256 para verificação |
X-Stryhub-Event |
payment.completed |
Tipo do evento |
X-Stryhub-Delivery |
del_uuid |
ID único de entrega (para idempotência) |
User-Agent |
Stryhub-Webhooks/1.0 |
String do user agent |
Verificação de Assinatura
Cada entrega de webhook é assinada com a chave de assinatura única do seu endpoint usando HMAC-SHA256. Você deve sempre verificar a assinatura antes de processar o evento.
Como funciona
- Extraia o timestamp (
t) e a assinatura (v1) do headerX-Stryhub-Signature - Construa o conteúdo assinado:
{timestamp}.{raw_body} - Compute o HMAC-SHA256 usando sua chave de assinatura
- Compare a assinatura computada com o valor
v1 - Opcionalmente, verifique se o timestamp é recente (dentro de 5 minutos) para prevenir ataques de replay
Exemplos de Código
const crypto = require('crypto');
function verifyWebhook(rawBody, signatureHeader, secret) {
// Analisa o header de assinatura
const parts = {};
signatureHeader.split(',').forEach(part => {
const [key, value] = part.split('=');
parts[key] = value;
});
const timestamp = parts['t'];
const signature = parts['v1'];
// Constrói o conteúdo assinado
const signedContent = `${timestamp}.${rawBody}`;
// Computa a assinatura esperada
const expected = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');
// Compara as assinaturas (timing-safe)
const isValid = crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
// Verifica a validade do timestamp (tolerância de 5 min)
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
if (age > 300) {
throw new Error('Webhook timestamp too old');
}
return isValid;
}
// Exemplo com Express.js
app.post('/webhooks/stryhub', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-stryhub-signature'];
const secret = process.env.STRYHUB_WEBHOOK_SECRET;
try {
const isValid = verifyWebhook(req.body.toString(), signature, secret);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
console.log('Received event:', event.type, event.id);
// Trata o evento
switch (event.type) {
case 'payment.completed':
// Ativar assinatura, enviar e-mail de boas-vindas, etc.
break;
case 'subscription.canceled':
// Revogar acesso, enviar e-mail de retenção, etc.
break;
// ... tratar outros eventos
}
res.status(200).json({ received: true });
} catch (err) {
console.error('Webhook error:', err.message);
res.status(400).json({ error: err.message });
}
});
import hmac
import hashlib
import time
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
def verify_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
"""Verifica a assinatura do webhook da stryhub."""
# Analisa o header de assinatura
parts = {}
for part in signature_header.split(','):
key, value = part.split('=', 1)
parts[key] = value
timestamp = parts.get('t', '')
signature = parts.get('v1', '')
# Constrói o conteúdo assinado
signed_content = f"{timestamp}.{raw_body.decode('utf-8')}"
# Computa a assinatura esperada
expected = hmac.new(
secret.encode('utf-8'),
signed_content.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Compara as assinaturas (timing-safe)
is_valid = hmac.compare_digest(signature, expected)
# Verifica a validade do timestamp (tolerância de 5 min)
age = int(time.time()) - int(timestamp)
if age > 300:
raise ValueError('Webhook timestamp too old')
return is_valid
@app.route('/webhooks/stryhub', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Stryhub-Signature', '')
secret = os.environ['STRYHUB_WEBHOOK_SECRET']
try:
is_valid = verify_webhook(request.data, signature, secret)
if not is_valid:
return jsonify({'error': 'Invalid signature'}), 401
event = json.loads(request.data)
print(f"Received event: {event['type']} {event['id']}")
# Trata o evento
if event['type'] == 'payment.completed':
# Ativar assinatura, enviar e-mail de boas-vindas, etc.
pass
elif event['type'] == 'subscription.canceled':
# Revogar acesso, enviar e-mail de retenção, etc.
pass
return jsonify({'received': True}), 200
except Exception as e:
print(f"Webhook error: {e}")
return jsonify({'error': str(e)}), 400
<?php
// Handler de webhook em PHP
function verifyWebhook(string $rawBody, string $signatureHeader, string $secret): bool
{
// Analisa o header de assinatura
$parts = [];
foreach (explode(',', $signatureHeader) as $part) {
[$key, $value] = explode('=', $part, 2);
$parts[$key] = $value;
}
$timestamp = $parts['t'] ?? '';
$signature = $parts['v1'] ?? '';
// Constrói o conteúdo assinado
$signedContent = "{$timestamp}.{$rawBody}";
// Computa a assinatura esperada
$expected = hash_hmac('sha256', $signedContent, $secret);
// Compara as assinaturas (timing-safe)
$isValid = hash_equals($signature, $expected);
// Verifica a validade do timestamp (tolerância de 5 min)
$age = time() - intval($timestamp);
if ($age > 300) {
throw new Exception('Webhook timestamp too old');
}
return $isValid;
}
// Trata o webhook
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_STRYHUB_SIGNATURE'] ?? '';
$secret = getenv('STRYHUB_WEBHOOK_SECRET');
try {
$isValid = verifyWebhook($rawBody, $signature, $secret);
if (!$isValid) {
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit;
}
$event = json_decode($rawBody, true);
error_log("Received event: {$event['type']} {$event['id']}");
// Trata o evento
switch ($event['type']) {
case 'payment.completed':
// Ativar assinatura, enviar e-mail de boas-vindas, etc.
break;
case 'subscription.canceled':
// Revogar acesso, enviar e-mail de retenção, etc.
break;
}
http_response_code(200);
echo json_encode(['received' => true]);
} catch (Exception $e) {
error_log("Webhook error: " . $e->getMessage());
http_response_code(400);
echo json_encode(['error' => $e->getMessage()]);
}
?>
Eventos Suportados
A stryhub traduz eventos complexos do Stripe em notificações simples e acionáveis:
| Evento | Descrição | Quando é disparado |
|---|---|---|
checkout.completed |
Sessão de checkout concluída | O cliente finaliza um checkout (avulso ou assinatura) |
payment.completed |
Pagamento realizado com sucesso | Cobrança bem-sucedida (inicial ou recorrente) |
payment.failed |
Pagamento falhou | Cartão recusado ou erro no pagamento |
subscription.created |
Nova assinatura | O cliente assina um produto recorrente |
subscription.renewed |
Assinatura renovada | Pagamento recorrente realizado com sucesso |
subscription.canceled |
Assinatura cancelada | A assinatura é totalmente cancelada |
subscription.paused |
Assinatura pausada | A cobrança da assinatura é pausada |
subscription.resumed |
Assinatura retomada | A assinatura pausada é reativada |
subscription.past_due |
Pagamento em atraso | Pagamento recorrente falhou; assinatura em risco |
customer.created |
Novo cliente | Cliente registrado pela primeira vez |
Política de Tentativas
Se o seu endpoint retornar um código de status diferente de 2xx (ou expirar após 15 segundos), a stryhub automaticamente retenta a entrega:
| Tentativa | Intervalo | Acumulado |
|---|---|---|
| 1ª | Imediata | 0 |
| 2ª | 1 minuto | 1 minuto |
| 3ª | 5 minutos | 6 minutos |
| 4ª | 30 minutos | 36 minutos |
| 5ª | 2 horas | ~2,5 horas |
| 6ª (final) | 24 horas | ~26,5 horas |
Após 6 tentativas sem sucesso, a entrega é marcada como falha. Você pode retentar manualmente entregas com falha a partir do painel a qualquer momento.
Boas Práticas
1. Responda rapidamente
Retorne uma resposta 2xx o mais rápido possível (dentro de 15 segundos). Se precisar fazer processamento pesado, confirme o recebimento do webhook imediatamente e processe-o de forma assíncrona (ex.: usando uma fila de tarefas).
2. Verifique as assinaturas
Sempre verifique o header X-Stryhub-Signature antes de confiar no payload. Isso impede que atacantes enviem eventos falsos para o seu endpoint.
3. Trate duplicatas (idempotência)
Devido às tentativas automáticas, seu endpoint pode receber o mesmo evento mais de uma vez. Use o campo id no payload para detectar e ignorar eventos duplicados.
4. Use o ID de entrega
O header X-Stryhub-Delivery contém um ID único de entrega. Registre-o para depuração e use-o para rastrear tentativas individuais de entrega.
5. Monitore seus logs
Verifique regularmente os logs de entrega de webhooks no seu painel. Entregas com falha podem indicar problemas com seu endpoint, rede ou lógica da aplicação.
6. Use HTTPS
Sempre use HTTPS para a URL do seu endpoint em produção. Isso garante que o payload e a assinatura sejam criptografados em trânsito.