Webhooks de Saque (Payout)
Eventos relacionados ao ciclo de vida das solicitações de saque para submerchants do Fast Connect.
Eventos Disponíveis
| Evento | Descrição |
|---|---|
payout.approved | Enviado quando uma solicitação de saque é aprovada pelo gateway |
payout.rejected | Enviado quando uma solicitação de saque é rejeitada pelo gateway |
Pré-requisitos
- Fast Connect habilitado no merchant master
- Submerchant com
parent_merchant_idconfigurado - Webhook endpoints configurados no submerchant
- Eventos habilitados nos
enabled_eventsdo webhook endpoint (ou lista vazia para habilitar todos)
Payload
Todos os webhooks de saque seguem a estrutura padrão:
{
"id": "evt_2RhQg9M7ZCg3X3nMb9W1kX8Q",
"event": "payout.approved" | "payout.rejected",
"data": {
"id": "2vorkDcXyvzifL63YX09S9VqcnI",
"merchantId": "36OO3bcGjhPjjF3tzrqOGcmQuKo",
"amount": 10000,
"currency": "BRL",
"status": "approved" | "rejected",
"payoutFee": 100,
"netAmount": 9900,
"rejectionReason": "..." // apenas se rejeitado
}
}
payout.approved
Enviado quando uma solicitação de saque é aprovada pelo gateway e as operações no ledger são concluídas com sucesso.
{
"id": "evt_2RhQg9M7ZCg3X3nMb9W1kX8Q",
"event": "payout.approved",
"data": {
"id": "2vorkDcXyvzifL63YX09S9VqcnI",
"merchantId": "36OO3bcGjhPjjF3tzrqOGcmQuKo",
"amount": 10000,
"currency": "BRL",
"status": "approved",
"payoutFee": 100,
"netAmount": 9900,
"externalAccountDetails": {
"type": "pix",
"pixKey": "12345678000199"
},
"processedAt": "2025-12-04T18:45:52.988Z",
"createdAt": "2025-12-04T18:30:00.000Z",
"updatedAt": "2025-12-04T18:45:52.988Z"
}
}
Campos importantes:
amount: Valor bruto solicitado (em centavos)payoutFee: Taxa de saque calculada (em centavos)netAmount: Valor líquido que será transferido após dedução das taxasprocessedAt: Data/hora em que o saque foi aprovado e processado
payout.rejected
Enviado quando uma solicitação de saque é rejeitada pelo gateway.
{
"id": "evt_2RhQg9M7ZCg3X3nMb9W1kX8Q",
"event": "payout.rejected",
"data": {
"id": "2vorkDcXyvzifL63YX09S9VqcnI",
"merchantId": "36OO3bcGjhPjjF3tzrqOGcmQuKo",
"amount": 10000,
"currency": "BRL",
"status": "rejected",
"payoutFee": 100,
"netAmount": 9900,
"rejectionReason": "Documentação incompleta",
"externalAccountDetails": {
"type": "pix",
"pixKey": "12345678000199"
},
"processedAt": "2025-12-04T18:45:52.988Z",
"createdAt": "2025-12-04T18:30:00.000Z",
"updatedAt": "2025-12-04T18:45:52.988Z"
}
}
Campos importantes:
rejectionReason: Motivo da rejeição informado pelo gatewayprocessedAt: Data/hora em que o saque foi rejeitado
Processo de Envio
-
Busca de endpoints:
- Sistema busca
webhook_endpointsdo submerchant - Filtra por eventos habilitados (
enabled_eventscontém o evento ou está vazio)
- Sistema busca
-
Envio:
- Envia para todos os endpoints configurados
- Usa
Promise.allSettled(não falha se algum endpoint falhar) - Registra logs de sucesso/falha
-
Timing:
- Webhooks são enviados após o commit da transação
- Garante que os dados já estão persistidos no banco
Exemplo de Implementação
app.post('/webhooks/fastpay', (req, res) => {
const { id, event, data } = req.body;
switch (event) {
case 'payout.approved':
console.log(`Saque aprovado: ${data.id}`);
console.log(`Valor líquido: R$ ${(data.netAmount / 100).toFixed(2)}`);
console.log(`Taxa: R$ ${(data.payoutFee / 100).toFixed(2)}`);
// Atualizar status na sua base de dados
// Notificar o submerchant
// Registrar no log de transações
break;
case 'payout.rejected':
console.log(`Saque rejeitado: ${data.id}`);
console.log(`Motivo: ${data.rejectionReason}`);
// Atualizar status na sua base de dados
// Notificar o submerchant sobre a rejeição
// Registrar motivo da rejeição
break;
default:
console.log(`Evento desconhecido: ${event}`);
}
res.status(200).send('OK');
});
Fluxo Completo
Valores em Centavos
Todos os valores monetários são enviados em centavos:
amount: 10000 = R$ 100,00payoutFee: 100 = R$ 1,00netAmount: 9900 = R$ 99,00
Para converter para reais, divida por 100:
const amountInReais = data.amount / 100;
const feeInReais = data.payoutFee / 100;
const netAmountInReais = data.netAmount / 100;
Idempotência
Webhooks podem ser enviados mais de uma vez. Use o campo id para evitar processamento duplicado:
const processedEvents = new Set();
app.post('/webhooks/fastpay', (req, res) => {
const { id, event, data } = req.body;
// Verificar se já processamos este evento
if (processedEvents.has(id)) {
return res.status(200).send('Already processed');
}
// Processar evento...
processPayoutEvent(event, data);
// Marcar como processado
processedEvents.add(id);
res.status(200).send('OK');
});
Tratamento de Erros
Se seu endpoint retornar um erro (status diferente de 2xx), a FastPay tentará reenviar:
- Tentativa 1: Imediatamente
- Tentativa 2: Após 1 minuto
- Tentativa 3: Após 5 minutos
- Tentativa 4: Após 15 minutos
- Tentativa 5: Após 1 hora
Após 5 tentativas falhadas, o webhook é marcado como "failed" no painel.
Configuração de Webhooks
Os webhooks de saque podem ser configurados:
-
Via Painel da FastPay:
- Navegue até FastConnect > Gestão de Subcontas > Webhooks
- Configure os endpoints para o submerchant
-
Via API:
- Use o endpoint de configuração de webhooks do submerchant
- Informe os eventos desejados em
enabled_eventsou deixe vazio para todos
Notas Importantes
- Webhooks são enviados apenas para submerchants (com
parent_merchant_id) - O sistema respeita
enabled_eventsconfigurados no webhook endpoint - Falhas no envio não interrompem o fluxo de aprovação/rejeição
- Todos os valores monetários são em centavos
- A moeda é sempre BRL para submerchants
- O saque é realizado via PIX usando o CNPJ do submerchant como chave