Docs
Webhooks

Webhooks

Receba no seu servidor cada venda, reembolso, disputa e assinatura, na hora em que acontece.

Um webhook é uma URL sua que a Rivoo chama (POST, JSON) sempre que algo acontece na loja: pedido pago, reembolso, disputa, renovação de assinatura. É o jeito certo de liberar uma compra no seu sistema, em vez de ficar consultando a API.

Cadastrar

No painel, abra Integrações › Webhooks e clique em Adicionar. Ou pela API: Criar webhook.

Informe a URL (HTTPS, pública) e os eventos que ela recebe. Sem eventos marcados, ela recebe todos.

Guarde o segredo de assinatura (whsec_...). Ele aparece só nesta hora e serve para conferir que o pedido veio da Rivoo.

O que chega

POST /webhooks/rivoo HTTP/1.1
Content-Type: application/json
User-Agent: Rivoo-Webhooks/1
webhook-id: evt_mhexample0000event00001
webhook-timestamp: 1793567091
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{
  "id": "evt_mhexample0000event00001",
  "type": "order.paid",
  "createdAt": "2026-10-31T21:04:51.000Z",
  "data": { "object": { "id": "ord_mhexample0000vip0000001", "source": "STOREFRONT", "totalCents": 2990, "paymentId": "pay_mhexample0000vip0000001" } }
}

data.object muda conforme o evento: veja a lista de eventos. Valores em dinheiro vêm em centavos (2990 = R$ 29,90).

Verificar a assinatura

O formato segue o padrão aberto Standard Webhooks. A assinatura é um HMAC-SHA256 de webhook-id.webhook-timestamp.corpo, com a chave sendo o segredo sem o prefixo whsec_, decodificado de base64. Compare com cada assinatura do header (separadas por espaço, cada uma com o prefixo v1,): durante a troca do segredo chegam duas.

Use o corpo cru, exatamente como chegou, antes de qualquer parse de JSON. E recuse timestamps com mais de 5 minutos de diferença, para ninguém reenviar um pedido antigo.
import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody: o corpo como string, antes do JSON.parse
export function verify(rawBody, headers, secret = process.env.RIVOO_WEBHOOK_SECRET) {
  const id = headers['webhook-id'];
  const ts = Number(headers['webhook-timestamp']);
  if (Math.abs(Date.now() / 1000 - ts) > 300) return false;

  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
  const expected = createHmac('sha256', key).update(`${id}.${ts}.${rawBody}`).digest();

  return headers['webhook-signature'].split(' ').some((sig) => {
    const got = Buffer.from(sig.replace(/^v1,/, ''), 'base64');
    return got.length === expected.length && timingSafeEqual(got, expected);
  });
}

Responder

Responda 2xx em até 10 segundos. Faça o trabalho pesado depois (fila), não antes de responder. Redirecionamentos não são seguidos.

Novas tentativas

Se a resposta não for 2xx (ou demorar mais de 10 s), a Rivoo tenta de novo: 8 tentativas no total, depois de 1 min, 5 min, 30 min, 2 h, 6 h, 12 h e 24 h. O webhook-id é o mesmo em todas: guarde os ids já processados e ignore repetidos.

Uma URL que falha seguido por mais de 3 dias é desativada, e você recebe um aviso no painel. Reative em Integrações › Webhooks depois de corrigir.

Testar

Use Enviar teste no webhook (ou Testar webhook) para receber um evento de exemplo. Em Envios do webhook você vê cada tentativa com o código de resposta.

Trocar o segredo

Trocar segredo do webhook gera um segredo novo. Por 24 horas as mensagens chegam assinadas com os dois (duas assinaturas no header), para você atualizar sem perder eventos.

Nesta página