Receber eventos no seu sistema (webhooks)
Receba no seu servidor um aviso assinado a cada venda, reembolso, assinatura ou cliente novo, com novas tentativas automáticas e histórico de entregas.
Um webhook é um endereço do seu sistema que o Vipter chama quando algo acontece na loja: uma venda aprovada, um reembolso, uma assinatura cancelada. Cada chamada é um POST com os dados em JSON e uma assinatura que prova que veio do Vipter. Esta página é para quem vai programar o recebimento, ou para passar a quem vai.
Antes de começar
- Um endereço
https://no seu sistema que aceitePOSTe responda com um código 2xx em até 10 segundos. - Papel Admin ou Dono no projeto do Vipter.
Passo 1: adicionar o endpoint
- No painel do Vipter, abra GeralIntegraçõesAutomaçõesWebhooks.
- Clique em Adicionar endpoint.
- Preencha os campos numerados:

| # | Campo | O que colar |
|---|---|---|
| 1 | URL do endpoint | O endereço que vai receber os eventos. Precisa começar com https://. |
| 2 | Descrição | Um lembrete para você, como "ERP da loja". Até 200 caracteres. |
| 3 | Tipos de evento | Marque só os eventos que o seu sistema usa. Sem nenhum marcado, o endpoint recebe todos, inclusive os que forem criados no futuro. |
- Clique em Criar endpoint.
Os eventos de um endpoint não podem ser editados depois. Para mudar a lista, crie um endpoint novo e remova o antigo.
Passo 2: guardar o segredo de assinatura
Logo depois de criar, o Vipter mostra Seu segredo de assinatura: um valor que começa com whsec_. Guarde agora. Só é exibido uma vez; você pode rotacionar depois.
Copie o segredo e guarde no seu sistema, por exemplo numa variável de ambiente. Depois de fechar a janela, o endpoint mostra só o final do segredo.
Trate o segredo como uma senha
Quem tem o segredo consegue forjar eventos que passam pela verificação. Não coloque o segredo no código-fonte nem o envie por e-mail. Se ele vazar, gere outro pelo botão Rotacionar segredo.
Passo 3: verificar a assinatura
Cada chamada traz estes cabeçalhos:
| Cabeçalho | Conteúdo |
|---|---|
Vipter-Signature | t=<data em segundos Unix>,v1=<assinatura>. Durante a troca de segredo, vem um segundo v1=. |
Vipter-Event-Id | O ID do evento, como evt_…. |
Vipter-Event-Type | O tipo do evento, como order.paid. |
User-Agent | Vipter-Webhooks/1.0 |
A assinatura é um HMAC SHA-256, em hexadecimal, calculado com o seu segredo sobre o texto <t>.<corpo>: o valor de t, um ponto e o corpo da requisição exatamente como chegou. Para conferir:
- Leia o corpo cru, antes de qualquer conversão para JSON. Um corpo reformatado gera outra assinatura.
- Recalcule o HMAC com o seu segredo e compare com cada
v1=do cabeçalho. Basta um igual. - Recuse chamadas com
tmuito antigo. O exemplo abaixo aceita até 5 minutos de diferença.
O mesmo código aparece no painel, em Trecho de verificação (Node.js):
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyVipterSignature(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return header.split(',').filter((p) => p.startsWith('v1=')).some((p) => {
const given = Buffer.from(p.slice(3), 'hex');
return given.length === expected.length / 2 && timingSafeEqual(given, Buffer.from(expected, 'hex'));
});
}Um exemplo com Express, que recebe o corpo cru e responde logo:
import express from 'express';
const app = express();
app.post('/webhooks/vipter', express.raw({ type: 'application/json' }), (req, res) => {
const raw = req.body.toString('utf8');
if (!verifyVipterSignature(raw, req.get('Vipter-Signature') ?? '', process.env.VIPTER_WEBHOOK_SECRET)) {
return res.status(400).send('assinatura inválida');
}
const event = JSON.parse(raw);
// Guarde event.id e ignore se ele já foi processado.
res.sendStatus(200);
// Processe event.type e event.data.object depois de responder.
});Passo 4: enviar um evento de teste
- Com o endpoint ativo, clique em Enviar evento de teste, no topo da página.
- Aparece Evento de teste enfileirado. e a entrega entra em Entregas recentes.
O teste é um evento customer.created com um cliente fictício e "test": true dentro de data.object. Ele só chega aos endpoints ativos que recebem customer.created ou todos os eventos.
Deu certo se
A entrega aparece em Entregas recentes com o status succeeded e o código HTTP que o seu sistema respondeu, e o seu sistema aceitou a assinatura.
O formato dos eventos
Todo evento tem o mesmo envelope. O objeto em data.object depende do tipo.
{
"id": "evt_4f1c2a9b0d3e5f6a7b8c9d0e",
"object": "event",
"type": "order.paid",
"created": 1790000000,
"livemode": true,
"api_version": "2026-09-01",
"data": {
"object": {
"object": "order",
"id": "…",
"status": "…",
"total_amount": 19700,
"currency": "BRL"
}
}
}idé único por evento. Use para ignorar repetições: uma entrega reenviada chega com o mesmoid.- Valores em dinheiro vêm em centavos, como
19700para R$ 197,00. - Pedido (
"object": "order"):id,customer_id,customer_email,subscription_id,status,total_amount,currency,refunded_amount,order_type,recurrence,offer_id,payment_method,provider_slug,paid_at,items,external_order_id,status_source,created_at,updated_at. Emorder.paid, tambémdownloads, com os links de download do comprador quando o produto tem arquivos. - Assinatura (
"object": "subscription"):id,customer_id,customer_email,customer_name,status,current_offer_id,offer_name,product_id,product_name,billing_cycle,currency,current_amount,current_period_start,current_period_end,next_billing_at,trial_start,trial_end,cycles_completed,cycle_limit,cancel_at_period_end,cancelled_at,cancellation_reason,payment_instrument_id,created_at,updated_at. - Cliente (
"object": "customer"):id,email,name,phone,document_type,metadata,created_at,updated_at. - Checkout abandonado (
"object": "checkout_abandonment"): o comprador (customer), a oferta, o produto, a quantidade e o valor.
Lista de eventos
| Evento | Quando é enviado |
|---|---|
order.paid | Um pagamento foi aprovado. Vale também para as renovações de assinatura, com recurrence: "subsequent", e para vendas marcadas como pagas pela equipe, com status_source: "manual". |
order.failed | Uma cobrança foi recusada. |
order.refunded | Um pedido foi reembolsado. |
order.partially_refunded | Parte de um pedido foi reembolsada. |
order.charged_back | O comprador contestou a compra no banco (chargeback). |
subscription.created | Uma assinatura foi criada. |
subscription.renewed | Uma assinatura foi renovada. |
subscription.dunning | A cobrança da renovação falhou e a assinatura está em recuperação. |
subscription.reactivated | Uma assinatura voltou a ficar ativa. |
subscription.upgraded | O assinante mudou para um plano mais caro. |
subscription.downgraded | O assinante mudou para um plano mais barato. |
subscription.payment_method_changed | O cartão da assinatura foi trocado. |
subscription.paused | A assinatura foi pausada. |
subscription.resumed | A assinatura pausada voltou. |
subscription.cancelled | A assinatura foi cancelada. |
subscription.expired | A assinatura terminou. |
customer.created | Um cliente novo foi cadastrado. |
customer.updated | Os dados de um cliente mudaram. |
checkout.abandoned | O comprador preencheu os dados no checkout e não pagou. É enviado depois de um tempo sem atividade, e só se ele não comprou nesse meio-tempo. |
checkout.recovered | Um checkout abandonado terminou em compra. |
Entregas e novas tentativas
Uma entrega dá certo quando o seu sistema responde com um código 2xx em até 10 segundos. Qualquer outra resposta, incluindo redirecionamentos, ou a falta de resposta conta como falha.
Depois de uma falha, o Vipter tenta de novo, até 8 tentativas no total. A espera entre elas é de 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas, 24 horas e 48 horas. Depois da oitava tentativa, a entrega fica com o status exhausted.
Em Entregas recentes, cada linha mostra o Evento, o Status com o código HTTP e o erro, as Tentativas e a Última tentativa. As entregas que não deram certo têm o botão Reenviar, que recomeça as tentativas do zero.
Endpoint desativado sozinho
Se 20 entregas seguidas esgotarem as tentativas, o Vipter desativa o endpoint e mostra o motivo no card dele. Uma entrega bem-sucedida zera essa contagem. Corrija o seu sistema e ligue o endpoint de novo pela chave Ativo.
Enquanto um endpoint está desativado, os eventos novos não são guardados para ele. Use a lista de pedidos e de assinaturas do painel para conferir o que aconteceu nesse período.
Trocar o segredo ou remover o endpoint
- Rotacionar segredo: gera um segredo novo e mostra uma única vez. Rotacionar o segredo? O anterior continua válido por 24 horas. Nesse período, cada chamada traz as duas assinaturas, e o seu sistema pode trocar o segredo sem perder eventos.
- Remover: Remover este endpoint e seu histórico de entregas?
Problemas comuns
-
Informe uma URL https://
O endereço não começa com
https://ou tem erro de digitação. Endereçoshttp://não são aceitos. -
A assinatura nunca confere
O corpo foi convertido para JSON antes da verificação, ou o segredo é de outro endpoint. Verifique o corpo cru e confira o final do segredo no card do endpoint.
-
As entregas falham com
timeoutO seu sistema demora mais de 10 segundos para responder. Responda 200 assim que validar a assinatura e processe o evento depois.
-
O botão de teste está desabilitado
Não há endpoint ativo. Ligue a chave Ativo de um endpoint.
O que fazer a seguir
- Para mandar vendas a uma plataforma de anúncios em vez do seu sistema, veja como funciona o rastreamento de conversões.
- Conecte uma área de membros pronta, como a MemberKit, sem programar.
Verificar o domínio de envio (SPF, DKIM e DMARC)
Entenda os registros de DNS que provam que os e-mails da loja são seus, onde conseguir cada um e como ler a verificação de domínio do Vipter.
Dados do projeto
Edite nome, país, moeda, fuso, contato, site, MCC, slug, página de sucesso padrão e endereço da loja, e saiba o que fica fixo depois do credenciamento nas bandeiras.