Pular para o conteúdo

Webhooks

Saiba na hora em que o dinheiro se move

Notificações em tempo real de pay-ins, payouts, contas virtuais, assinaturas, câmbio, Wire In e Wire Out, cada uma assinada com HMAC-SHA256 para você verificar cada requisição antes de agir.

  • Assinaturas HMAC-SHA256
  • Reentregas com backoff
  • Por país e por conta

Configuração

Configure as notificações no Business Portal

Cadastrar um endpoint não exige código. Cada entrada pode ser limitada por país e por conta.

  1. 01

    Abra as notificações

    No Business Portal, acesse Configurations → Clients → Notifications.

  2. 02

    Adicione sua URL HTTPS

    Cadastre o endpoint HTTPS público dos seus servidores que vai receber os eventos.

  3. 03

    Defina o escopo

    Escolha um país, ou All, e a conta à qual as notificações pertencem.

  4. 04

    Adicione cabeçalhos e teste

    Se quiser, inclua cabeçalhos personalizados para o seu gateway e depois faça uma transação de teste no Stage.

Payload

Um mesmo envelope para cada evento

Todas as notificações compartilham os mesmos campos raiz, então um único receptor pode roteá-las. O corpo é JSON e chega com Content-Type text/plain.

  • transactionType: PayIn, PayOut, VirtualAccount, Subscription, CurrencyExchange, WireIn ou WireOut
  • transactionFlow: o fluxo de processamento, útil para rotear
  • data: a transação em si, com externalId, internalId e status

Eventos

Sobre o que você será notificado

  • Pay-in com cartão

    transactionType
    PayIn
    O que indica
    Aprovado, concluído, rejeitado, reembolsado ou com chargeback
  • Pay-in por transferência ou meio local

    transactionType
    PayIn
    O que indica
    O status final depois que o pagador confirma no banco ou na carteira
  • Conta virtual

    transactionType
    VirtualAccount
    O que indica
    Conta criada e cada pagamento recebido
  • Assinatura

    transactionType
    Subscription
    O que indica
    Ciclo de vida da assinatura; cada cobrança também chega como evento PayIn
  • Payout

    transactionType
    PayOut
    O que indica
    Mudanças de status ao longo do fluxo de payout, até a conclusão
  • Câmbio

    transactionType
    CurrencyExchange
    O que indica
    Resultado de uma conversão entre seus saldos
  • Wire In

    transactionType
    WireIn
    O que indica
    Fundos creditados na sua conta, em moeda fiduciária ou USDC/USDT
  • Wire Out

    transactionType
    WireOut
    O que indica
    Saque enviado; os Wire Out em cripto incluem o hash da transação na blockchain

Os esquemas completos e os códigos de status de cada evento estão na referência de webhooks em docs.localpayment.com.

Segurança

Verifique cada assinatura

Cada requisição traz x-Signature: HMAC-SHA256 seguido do HMAC em hexadecimal do corpo bruto, calculado com o seu segredo de webhook. Recalcule e compare antes de confiar no payload.

  • Leia o corpo bruto: é JSON enviado como Content-Type text/plain
  • Remova o prefixo “HMAC-SHA256 ” e compare com uma função de tempo constante
  • Rejeite assinaturas divergentes com 401 e nunca registre o payload completo
webhook.js
// Receptor Express para webhooks da Localpayment (Node.js 18+).
import crypto from 'node:crypto';
import express from 'express';
import { jobQueue } from './queue.js'; // sua própria fila de jobs assíncronos

const app = express();

// Carregue o segredo do webhook do seu gerenciador de segredos na inicialização. Nunca o deixe no código.
const WEBHOOK_SECRET = process.env.LP_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) throw new Error('LP_WEBHOOK_SECRET is not configured');

function hasValidSignature(rawBody, header) {
  if (typeof rawBody !== 'string') return false;
  // Formato do header: "x-Signature: HMAC-SHA256 <hex>". Remova o prefixo antes.
  const received = String(header ?? '').replace(/^HMAC-SHA256\s+/i, '').trim();
  if (!/^[0-9a-f]{64}$/i.test(received)) return false;

  const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(rawBody, 'utf8').digest();
  return crypto.timingSafeEqual(Buffer.from(received, 'hex'), expected);
}

// O corpo é JSON enviado como Content-Type text/plain: mantenha o texto bruto para o HMAC.
app.post('/webhooks/localpayment', express.text({ type: '*/*', limit: '1mb' }), (req, res) => {
  if (!hasValidSignature(req.body, req.get('x-signature'))) return res.sendStatus(401);

  let event;
  try {
    event = JSON.parse(req.body);
  } catch {
    return res.sendStatus(400);
  }

  res.sendStatus(200); // responda rápido, antes de qualquer processamento pesado
  // Processe de forma assíncrona. Deduplique por internalId/externalId + status
  // para aplicar cada reentrega uma única vez. Nunca registre o payload completo.
  jobQueue.enqueue(event);
});

Processamento confiável

Feito para reentregas e redes do mundo real

  • Responda rápido

    Retorne 200 assim que a assinatura for validada e faça o processamento pesado em uma tarefa em segundo plano.

  • Espere reentregas

    Se não recebermos um 200, reenviamos com backoff exponencial. Seu receptor precisa poder rodar mais de uma vez sem efeitos duplicados.

  • Deduplique

    Processe por internalId ou externalId mais o status, para aplicar cada reentrega uma única vez.

  • Não presuma a ordem

    Compare o status recebido com o que você tem salvo antes de aplicá-lo e nunca faça uma transação retroceder.

  • Confirme pela API

    Na dúvida, consulte GET /api/transactions/{externalId}/status para saber o status atual de uma transação.

  • Registre com cuidado

    Guarde tipos de evento, IDs e status, nunca payloads completos, tokens ou dados pessoais.

Operações

Acompanhe cada entrega no Business Portal

Os relatórios de notificações mostram o que foi enviado aos seus endpoints, para que suporte e engenharia resolvam problemas sem vasculhar logs de servidor.

  • Registro das notificações enviadas aos seus endpoints
  • Busca de transações e relatórios de atividade detalhados
  • Webhooks, lista de IPs permitidos e usuários em um só lugar

Perguntas frequentes sobre webhooks

Como sei que um webhook veio mesmo da Localpayment?

Verifique o cabeçalho x-Signature: recalcule o HMAC-SHA256 do corpo bruto com o seu segredo de webhook e compare com uma função de tempo constante. Você também pode adicionar cabeçalhos personalizados a cada entrada para o seu gateway.

Com que frequência as entregas com falha são reenviadas?

Entregas que não recebem um 200 são reenviadas com backoff exponencial. Projete seu receptor para várias tentativas e consulte os relatórios de notificações do Business Portal para ver o histórico de entregas.

Por que o Content-Type é text/plain?

O corpo é JSON enviado como text/plain. Leia-o como texto bruto, verifique a assinatura sobre esses bytes exatos e só então interprete o JSON.

Posso enviar países diferentes para endpoints diferentes?

Sim. Cada entrada de notificação é limitada por país (ou All) e por conta, então você pode direcionar mercados ou unidades de negócio para URLs separadas.

O que meu endpoint deve retornar?

Um status 200, o mais rápido possível e antes de qualquer processamento longo. Sem um 200, a entrega é considerada com falha e reenviada.

Conecte seu primeiro webhook

Configure um endpoint no Stage, dispare um pay-in de teste e verifique a assinatura de ponta a ponta.

Como podemos ajudar?

Baixe o seu exemplar

Deixe seus dados e receba a edição completa na hora.