Documentation Index
Fetch the complete documentation index at: https://docs.usealpa.com/llms.txt
Use this file to discover all available pages before exploring further.
Pense nos webhooks como “mensagens enviadas pela Alpa para o seu sistema”, sem que você precise ficar consultando a API o tempo todo.
Por que usar webhooks?
Sem webhooks, sua aplicação teria que perguntar para a API a cada segundo:
“Essa transação já foi confirmada?”
Isso é lento e ineficiente.
Com webhooks, a Alpa avisa você imediatamente:
“O pagamento foi confirmado. Aqui estão os dados.”
Assim você pode:
- atualizar o status de um pedido
- liberar acesso a um produto
- enviar e-mails automáticos de confirmação
- registrar movimentações financeiras
Tudo isso de forma automática, sem fazer seu cliente esperar.
Como funciona na prática?
-
Você cria um endpoint no seu sistema
Ex.:
https://meusite.com/webhooks/alpa
-
Você cadastra esse endpoint criando uma assinatura de webhook via API
-
Sempre que algo importante acontece, como um pagamento aprovado:
- A Alpa envia um
POST para a sua URL
- O body contém o evento (ex:
transaction.paid, withdrawal.completed)
- Seu sistema processa esse evento
É como receber uma notificação push — só que para servidores.
Ambientes (Desenvolvimento vs Produção)
Ambientes da Alpa
- Webhooks criados com chaves de Desenvolvimento recebem eventos simulados
- Webhooks criados com chaves de Produção recebem eventos reais
Dessa forma, você pode testar tudo antes de ir para produção.
Segurança dos webhooks
Webhooks precisam ser seguros — afinal, qualquer pessoa poderia tentar enviar requisições falsas para sua aplicação.
Por isso, recomendamos duas camadas de proteção:
🔐 1. Secret na URL
Cada assinatura de webhook tem um secret único, que pode ir na query string do seu endpoint.
Ex.: https://meusite.com/webhook/alpa?secret=SEU_SECRET
Seu backend confere:
if (req.query.secret !== process.env.WEBHOOK_SECRET) {
return res.status(401).json({ error: "Unauthorized" });
}
🛡️ 2. Assinatura HMAC (verificação do corpo)
Mesmo que alguém descubra sua URL, ainda não consegue falsificar o evento.
Por quê?
Porque cada webhook enviado pela Alpa inclui uma assinatura no header X-Webhook-Signature.
Essa assinatura é gerada usando HMAC-SHA256 e garante que:
- o corpo da requisição não foi alterado
- o evento realmente foi enviado pela Alpa
Seu backend deve recalcular a assinatura e comparar:
- Se for igual → ✅ evento é legítimo
- Se for diferente → ❌ evento rejeitado
Esse é o mesmo método usado por Stripe, PayPal, Shopify, GitHub, etc.
🔧 Exemplo de validação HMAC (Node.js)
Este exemplo mostra como validar a assinatura HMAC enviada no header X-Webhook-Signature.
import crypto from "node:crypto";
/**
* Verifica se a assinatura do webhook é válida.
* @param rawBody String com o body bruto da requisição.
* @param signatureFromHeader Assinatura recebida no header X-Webhook-Signature.
* @param secret Secret configurado na sua assinatura de webhook.
* @returns true se a assinatura for válida, false caso contrário.
*/
export function verifyAlpaSignature(
rawBody: string,
signatureFromHeader: string,
secret: string
) {
const bodyBuffer = Buffer.from(rawBody, "utf8");
const expectedSig = crypto
.createHmac("sha256", secret)
.update(bodyBuffer)
.digest("base64");
const A = Buffer.from(expectedSig);
const B = Buffer.from(signatureFromHeader);
return A.length === B.length && crypto.timingSafeEqual(A, B);
}
Criando uma assinatura de webhook
Defina seu endpoint
Crie um endpoint HTTPS público que receberá as notificações.Requisitos do endpoint
- Deve ser HTTPS (HTTP não é aceito)
- Deve responder 200 OK dentro de 10 segundos
- Deve aceitar requisições
POST com body JSON
Crie a assinatura via API
Envie uma requisição POST /api/v1/webhooks/subscriptions:curl -X POST "https://alpa-sistema-api.onrender.com/api/v1/webhooks/subscriptions" \
-H "Authorization: Bearer SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://meusite.com/webhooks/alpa",
"events": ["transaction.paid", "withdrawal.completed"]
}'
Valide os eventos recebidos
Implemente a validação HMAC no seu endpoint para garantir que os eventos são legítimos.
Eventos suportados
| Evento | Quando é disparado |
|---|
transaction.paid | Pagamento de uma transação foi confirmado |
transaction.refunded | Reembolso de uma transação foi concluído |
transaction.cancelled | Transação foi cancelada |
transaction.expired | Transação expirou sem pagamento |
payment_link.paid | Pagamento realizado via link de pagamento |
payment_link.expired | Link de pagamento expirou |
withdrawal.completed | Saque concluído com sucesso |
withdrawal.failed | Saque falhou |
affiliate.commission_created | Comissão de afiliado gerada |
Todos os webhooks compartilham o mesmo formato de payload:
{
"id": "evt_abc123xyz",
"event": "transaction.paid",
"devMode": false,
"data": { }
}
Dados sensíveis: O campo taxId (CPF/CNPJ) é mascarado nos payloads (ex: 123.***.***-**). Para pagamentos com cartão, apenas os últimos 4 dígitos e a bandeira são enviados.
Eventos de Transação
transaction.paid
transaction.refunded
transaction.cancelled
transaction.expired
Disparado quando o pagamento de uma transação é confirmado.{
"id": "evt_abc123xyz",
"event": "transaction.paid",
"devMode": false,
"data": {
"transaction": {
"id": "txn_abc123xyz",
"externalId": "pedido-123",
"amount": 10000,
"paidAmount": 10000,
"status": "PAID",
"method": "PIX",
"currency": "BRL",
"receiptUrl": "https://app.usealpa.com/receipt/...",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:05.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
},
"paymentLink": {
"id": "pl_abc123",
"slug": "meu-produto"
}
}
}
Disparado quando uma transação é reembolsada.{
"id": "evt_abc123xyz",
"event": "transaction.refunded",
"devMode": false,
"data": {
"transaction": {
"id": "txn_abc123xyz",
"externalId": "pedido-123",
"amount": 10000,
"paidAmount": 10000,
"status": "REFUNDED",
"method": "PIX",
"currency": "BRL",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T11:00:00.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
},
"reason": "requested_by_customer"
}
}
Disparado quando uma transação é cancelada.{
"id": "evt_abc123xyz",
"event": "transaction.cancelled",
"devMode": false,
"data": {
"transaction": {
"id": "txn_abc123xyz",
"externalId": "pedido-123",
"amount": 10000,
"paidAmount": 0,
"status": "CANCELLED",
"method": "PIX",
"currency": "BRL",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:45:00.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
}
}
}
Disparado quando uma transação expira sem pagamento.{
"id": "evt_abc123xyz",
"event": "transaction.expired",
"devMode": false,
"data": {
"transaction": {
"id": "txn_abc123xyz",
"externalId": "pedido-123",
"amount": 10000,
"paidAmount": 0,
"status": "EXPIRED",
"method": "PIX",
"currency": "BRL",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
}
}
}
Eventos de Link de Pagamento
payment_link.paid
payment_link.expired
Disparado quando um pagamento é realizado via link de pagamento.{
"id": "evt_abc123xyz",
"event": "payment_link.paid",
"devMode": false,
"data": {
"paymentLink": {
"id": "pl_abc123",
"slug": "meu-produto",
"name": "Meu Produto",
"amount": 10000,
"currency": "BRL",
"status": "ACTIVE"
},
"transaction": {
"id": "txn_abc123xyz",
"amount": 10000,
"paidAmount": 10000,
"status": "PAID",
"method": "PIX",
"receiptUrl": "https://app.usealpa.com/receipt/...",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:05.000Z"
},
"customer": {
"id": "cust_abc123",
"name": "João Silva",
"email": "joao@exemplo.com",
"taxId": "123.***.***-**"
}
}
}
Disparado quando um link de pagamento expira.{
"id": "evt_abc123xyz",
"event": "payment_link.expired",
"devMode": false,
"data": {
"paymentLink": {
"id": "pl_abc123",
"slug": "meu-produto",
"name": "Meu Produto",
"amount": 10000,
"currency": "BRL",
"status": "EXPIRED",
"expiredAt": "2026-01-16T00:00:00.000Z"
}
}
}
Eventos de Saque
withdrawal.completed
withdrawal.failed
Disparado quando um saque é concluído com sucesso.{
"id": "evt_abc123xyz",
"event": "withdrawal.completed",
"devMode": false,
"data": {
"withdrawal": {
"id": "wth_abc123xyz",
"amount": 50000,
"status": "COMPLETED",
"method": "PIX",
"pixKey": "joao@exemplo.com",
"receiptUrl": "https://app.usealpa.com/receipt/...",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:10.000Z"
}
}
}
Disparado quando um saque falha.{
"id": "evt_abc123xyz",
"event": "withdrawal.failed",
"devMode": false,
"data": {
"withdrawal": {
"id": "wth_abc123xyz",
"amount": 50000,
"status": "FAILED",
"method": "PIX",
"pixKey": "joao@exemplo.com",
"failureReason": "invalid_pix_key",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:05.000Z"
}
}
}
Boas práticas
Recomendações importantes
- Use HTTPS em todos os webhooks
- Valide a assinatura HMAC em cada requisição recebida
- Registre cada evento recebido e processe cada um uma única vez (idempotência)
- Responda 200 OK somente após concluir o processamento
- Implemente retentativas com idempotência caso o processamento falhe
- Não valide o payload inteiro com schemas rígidos (como Zod) — novos campos podem ser adicionados sem aviso prévio