Desenvolvedores

Webhooks

Receba os eventos da loja no seu sistema, verifique a assinatura e trate as novas tentativas.

Um webhook avisa o seu sistema quando algo acontece na loja: um pedido pago, um reembolso, um acesso revogado, uma entrega que falhou. A MinePag envia um POST com o evento em JSON para a URL que você cadastrar, assinado no padrão Standard Webhooks.

A lista de eventos e os dados de cada um estão em Eventos de webhook.

Cadastrar um endpoint

Só Proprietário e Administrador da loja gerenciam webhooks.

Prepare a URL

O endereço precisa ser HTTPS e público. Endereços locais (localhost, .local, .internal) e IPs privados ou reservados são recusados. Para testar na sua máquina, exponha a porta local com um túnel HTTPS.

Crie o endpoint

Abra Desenvolvedores › Webhooks e clique em Adicionar endpoint. Preencha:

  • Tipo: Webhook de eventos, para integrar com seus sistemas, ou Serviço externo de entrega, para quem entrega produtos e confirma pela API (precisa receber delivery.requested; veja Serviços externos).
  • Descrição (opcional): para reconhecer o endpoint no painel.
  • URL: o endereço que vai receber os eventos. Cada URL só pode ser cadastrada uma vez por loja.
  • Eventos: marque os eventos, organizados em Pedidos, Pagamentos, Acessos liberados, Entregas, Assinaturas e Clientes.

Clique em Criar endpoint.

Guarde o segredo

O painel mostra o segredo de assinatura, que começa com whsec_. Guarde em uma variável de ambiente do seu sistema, como MINEPAG_WEBHOOK_SECRET, e clique em Já guardei o segredo. Depois, o botão Mostrar segredo na página do endpoint exibe o valor de novo.

Teste

Na página do endpoint, use Enviar evento de teste. A MinePag envia um evento webhook.test assinado e mostra a resposta, como "O endpoint respondeu HTTP 200 em 85 ms.". O teste não entra no histórico nem é reenviado.

Gerenciar pela API

Os endpoints também podem ser gerenciados pela API, com uma chave secreta com os escopos webhooks:read e webhooks:write. Isso é útil para um app que se cadastra sozinho ao ser instalado na loja.

RotaO que faz
GET /webhook-endpointsLista os endpoints, sem o segredo.
POST /webhook-endpointsCria o endpoint. O segredo (secret) aparece só nesta resposta.
GET /webhook-endpoints/{endpointId}Consulta um endpoint.
PATCH /webhook-endpoints/{endpointId}Altera URL, descrição, tipo ou eventos. enabled: false pausa os envios e enabled: true reativa.
DELETE /webhook-endpoints/{endpointId}Remove o endpoint.
POST /webhook-endpoints/{endpointId}/rotate-secretGera um segredo novo, devolvido só nesta resposta. O anterior assina junto por 24 horas.
POST /webhook-endpoints/{endpointId}/testEnvia um webhook.test e devolve a resposta do endpoint.

As alterações feitas pela API ficam nos logs de auditoria com a chave de API como autora. Veja os detalhes na referência da API.

O que a MinePag envia

POST /webhooks/minepag HTTP/1.1
Content-Type: application/json
User-Agent: MinePag-Webhooks/1.0 (+https://minepag.com.br)
webhook-id: 0193b7c3-1001-7abc-8def-100000000001
webhook-timestamp: 1791815473
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{"id":"0193b7c3-1001-7abc-8def-100000000001","type":"order.paid","apiVersion":"2026-10-01","createdAt":"2026-10-12T14:31:13.000Z","projectId":"…","data":{…}}
CabeçalhoConteúdo
webhook-idIdentificador do evento. É igual ao id do corpo e se repete em todas as tentativas do mesmo evento.
webhook-timestampMomento do envio, em segundos desde 1970 (Unix). Muda a cada tentativa.
webhook-signatureUma ou mais assinaturas v1,<base64> separadas por espaço. Durante a troca de segredo vêm duas.

Resposta esperada

  • Responda 2xx em até 10 segundos. Qualquer outro status, falha de conexão ou demora conta como falha.
  • Redirecionamentos (3xx) não são seguidos e contam como falha.
  • O corpo da sua resposta é ignorado. O primeiro 1 KB fica no histórico do endpoint para ajudar a investigar falhas.

Verificar a assinatura

Verifique toda requisição antes de confiar nela. A assinatura é um HMAC-SHA256 de {webhook-id}.{webhook-timestamp}.{corpo} com o segredo do endpoint (o trecho depois de whsec_, decodificado de base64), codificado em base64.

Use o corpo bruto

Calcule a assinatura sobre os bytes exatos recebidos, antes de qualquer JSON.parse. Reserializar o JSON muda espaços e a ordem das chaves, e a assinatura deixa de bater.

Os passos:

  1. Leia webhook-id, webhook-timestamp e webhook-signature. Se faltar algum, recuse.
  2. Recuse se o horário estiver a mais de 5 minutos do relógio do seu servidor. Isso impede que alguém reenvie uma requisição antiga capturada.
  3. Calcule o HMAC esperado e compare, em tempo constante, com cada assinatura v1 do cabeçalho. Basta uma bater.
  4. Só então leia o JSON e processe.
Linguagem
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const TOLERANCE_SECONDS = 5 * 60;

export function verifyMinePagWebhook(rawBody, headers, secret) {
  const id = headers['webhook-id'];
  const timestamp = headers['webhook-timestamp'];
  const signatures = headers['webhook-signature'];
  if (!id || !timestamp || !signatures) return false;

  const now = Math.floor(Date.now() / 1000);
  if (!/^\d+$/.test(timestamp) || Math.abs(now - Number(timestamp)) > TOLERANCE_SECONDS) return false;

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

  return signatures.split(' ').some(entry => {
    const [version, signature] = entry.split(',');
    if (version !== 'v1' || !signature) return false;
    const received = Buffer.from(signature, 'base64');
    return received.length === expected.length && timingSafeEqual(received, expected);
  });
}

const app = express();

// express.raw mantém o corpo como Buffer, sem reserializar o JSON
app.post('/webhooks/minepag', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifyMinePagWebhook(req.body, req.headers, process.env.MINEPAG_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString('utf8'));
  res.status(200).end();
  queueEventProcessing(event);
});

queueEventProcessing representa a sua fila ou rotina de processamento. Se preferir, as bibliotecas oficiais do padrão Standard Webhooks, disponíveis para várias linguagens, também verificam os envios da MinePag com o mesmo segredo whsec_.

Novas tentativas

Quando o envio falha, a MinePag tenta de novo com estes intervalos:

TentativaQuando
1ªNa hora
2ª1 minuto depois da 1ª
3ª5 minutos depois da 2ª
4ª30 minutos depois da 3ª
5ª2 horas depois da 4ª
6ª6 horas depois da 5ª
7ª24 horas depois da 6ª

São 7 tentativas no total; a última acontece cerca de 32 horas e meia depois da primeira. Todas usam o mesmo webhook-id. Se a 7ª também falhar, o envio fica como Tentativas esgotadas e só volta a ser enviado se alguém usar Reenviar evento no painel.

Situação do endpoint

SituaçãoQuando
AtivoRecebendo normalmente.
Com falhas3 falhas seguidas. Proprietários e administradores recebem uma notificação. Os envios continuam. Volta a Ativo no primeiro sucesso.
Desativado20 falhas seguidas ao longo de mais de 3 dias, ou desativado à mão. A MinePag para de enviar, avisa por notificação e registra nos logs de auditoria.

Para voltar a receber, corrija o destino e use Reativar endpoint (ou PATCH /webhook-endpoints/{endpointId} com enabled: true). Ao desativar, as tentativas que estavam agendadas ficam como Tentativas esgotadas; depois de reativar, você pode reenviá-las pelo histórico. Eventos que aconteceram enquanto o endpoint estava desativado não são enviados nem aparecem no histórico: busque esses dados pela API (veja Conciliação diária).

Histórico e reenvio

A página do endpoint lista cada tentativa com o evento, a situação (Pendente, Entregue, Nova tentativa agendada ou Tentativas esgotadas), o status HTTP e o tempo de resposta. Abra uma tentativa para ver os dados enviados e o trecho da resposta do seu servidor.

Use Reenviar evento para mandar o mesmo evento de novo, com o mesmo webhook-id. O reenvio manual é uma tentativa avulsa: se falhar, não agenda outras. O reenvio fica nos logs de auditoria.

Trocar o segredo

Troque o segredo se ele vazar ou periodicamente:

  1. Na página do endpoint, clique em Gerar novo segredo e confirme. Pela API, use POST /webhook-endpoints/{endpointId}/rotate-secret.
  2. Copie o segredo novo. Por 24 horas, cada envio leva duas assinaturas no webhook-signature: uma com o segredo novo e outra com o anterior.
  3. Atualize o segredo no seu sistema dentro desse prazo. Os exemplos acima já aceitam qualquer uma das assinaturas, então a troca não perde eventos.
  4. Depois de 24 horas, só o segredo novo assina.

Boas práticas

  • Responda rápido e processe depois. Valide a assinatura, coloque o evento numa fila e responda 2xx. Processamentos longos estouram os 10 segundos e geram reenvios.
  • Deduplique pelo webhook-id. O mesmo evento pode chegar mais de uma vez (nova tentativa, reenvio manual, sua resposta perdida no caminho). Guarde os ids processados e ignore repetidos.
  • Não dependa da ordem. Eventos podem chegar fora de ordem. Use createdAt do envelope ou consulte o recurso pela API antes de sobrescrever um estado.
  • Ignore tipos que você não conhece. Responda 2xx e siga em frente: novos eventos podem surgir.
  • Confirme o que é crítico. Antes de liberar algo de alto valor, você pode consultar o pedido com GET /orders/{orderId}.
  • Um endpoint por finalidade. Separe o que é ERP, o que é suporte e o que é serviço de entrega: cada um com seu segredo e seus eventos.
  • Proteja o segredo. Guarde em variável de ambiente ou cofre de segredos, nunca no código.

Problemas comuns

SintomaCausa provável
Assinatura nunca bateCorpo reserializado (use o corpo bruto), segredo sem decodificar de base64, ou segredo de outro endpoint.
Assinatura bate no teste e falha depoisO relógio do servidor está atrasado ou adiantado mais de 5 minutos. Sincronize com NTP.
Envios com "O endpoint respondeu HTTP 301" ou 302A URL redireciona (ex.: http para https ou barra no fim). Cadastre a URL final.
Erro "Não foi possível conectar ao endpoint."Firewall bloqueando, certificado inválido ou servidor fora do ar.
Eventos duplicados no seu sistemaFalta deduplicar pelo webhook-id.

Nesta página