Webhooks V2
Descripción General
Que son los Webhooks V2?
Los Webhooks V2 son notificaciones enviadas a su aplicación cuando ocurren eventos importantes en sus transacciones PIX. El formato V2 usa una estructura de envelope {type, data} que facilita el procesamiento y proporciona mas detalle sobre cada evento.
Para recibir webhooks V2, su cuenta debe tener la versión de webhook configurada a V2. Vea como activar.
Estructura Base
Todos los webhooks V2 siguen esta estructura:
{
"type": "RECEIVE" | "TRANSFER" | "REFUND",
"data": {
// Datos especificos del evento
}
}Tipos de Eventos
| Tipo | Descripción | Equivalente V1 |
|---|---|---|
RECEIVE | PIX recibido (Cash-In) | CashIn |
TRANSFER | PIX enviado (Cash-Out) | CashOut |
REFUND | Devolución (entrada o salida) | CashInReversal / CashOutReversal |
Estructura de Datos Completa
interface WebhookV2Data {
// Identificadores
id: number; // ID de la transacción
txId: string | null; // Identificador del cobro (txid)
endToEndId: string | null; // End to End ID de la transacción PIX
// Clave PIX
pixKey: string | null; // Clave PIX utilizada
// Estado
status: 'PENDING' | 'LIQUIDATED' | 'REFUNDED' | 'ERROR';
// Pago
payment: {
amount: string; // Monto (string con 2 decimales)
currency: string; // Moneda (BRL)
};
// Devoluciones
refunds: RefundInfo[]; // Lista de devoluciones (vacia si no hay)
// Fechas
createdAt: string; // Fecha de creación (ISO 8601)
// Error
errorCode: string | null; // Codigo de error (si hay)
// Tipo de operación
webhookType: 'RECEIVE' | 'TRANSFER' | 'REFUND';
creditDebitType: 'CREDIT' | 'DEBIT';
transactionType: 'PIX';
localInstrument: 'DICT';
// Cuentas
debtorAccount: AccountInfo; // Pagador/Emisor
creditorAccount: AccountInfo; // Receptor/Destinatario
// Idempotencia
idempotencyKey: string | null;
// Datos adicionales
ticketData: object;
remittanceInformation: string | null; // Descripción de la transacción
}
interface AccountInfo {
ispb: string | null; // Codigo ISPB del banco
name: string | null; // Nombre del banco
issuer: string | null; // Codigo del banco
number: string | null; // Numero de cuenta
document: string | null; // CPF/CNPJ (enmascarado)
accountType: string | null; // Tipo de cuenta
}
interface RefundInfo {
status: 'PENDING' | 'LIQUIDATED' | 'ERROR';
payment: {
amount: number; // Monto de devolución (numero!)
currency: string;
};
errorCode: string | null;
eventDate: string; // Fecha de devolución
endToEndId: string | null; // E2E ID de la devolución
information: string | null; // Descripción de la devolución
}Diferencias V1 vs V2
| Aspecto | V1 | V2 |
|---|---|---|
| Formato | Campos en la raiz | Envelope {type, data} |
| Tipo de evento | event: "CashIn" | type: "RECEIVE" |
| Aspecto | V1 | V2 |
|---|---|---|
| PIX exitoso | CONFIRMED | LIQUIDATED |
| Devolución exitosa | CONFIRMED | REFUNDED |
| Error | ERROR | ERROR |
| Aspecto | V1 | V2 |
|---|---|---|
| Campo | counterpart | debtorAccount / creditorAccount |
| Banco | bank.bankName | name |
| ISPB | bank.bankISPB | ispb |
| Aspecto | V1 | V2 |
|---|---|---|
| Tipo | number | string |
| Formato | 100.00 | "100.00" |
Mapeo de Cuentas
PIX Recibido (RECEIVE)
debtorAccount = Quien pago (contraparte)
creditorAccount = Su cuenta (receptor)
creditDebitType = CREDITPIX Enviado (TRANSFER)
debtorAccount = Su cuenta (pagador)
creditorAccount = Quien recibio (contraparte)
creditDebitType = DEBITDevolución de Recepción (REFUND - CashInReversal)
debtorAccount = Su cuenta (devolviendo)
creditorAccount = Quien recibira de vuelta (contraparte)
creditDebitType = DEBITDevolución de Transferencia (REFUND - CashOutReversal)
debtorAccount = Quien esta devolviendo (contraparte)
creditorAccount = Su cuenta (recibiendo de vuelta)
creditDebitType = CREDITConfiguración del Endpoint
Requisitos
- URL HTTPS requerida
- Timeout máximo: 10 segundos
- Respuesta esperada: HTTP 2xx
Autenticación
Los webhooks se envian con Basic Auth:
Authorization: Basic base64(username:password)Configure las credenciales en el dashboard o contacte a soporte.
Ejemplo de Handler
import express from 'express';
const app = express();
app.use(express.json());
interface WebhookV2 {
type: 'RECEIVE' | 'TRANSFER' | 'REFUND';
data: WebhookV2Data;
}
// Set para idempotencia
const processedIds = new Set<number>();
app.post('/webhooks/pix', (req, res) => {
const webhook: WebhookV2 = req.body;
// Responder rapidamente
res.status(200).json({ acknowledged: true });
// Verificar idempotencia
if (processedIds.has(webhook.data.id)) {
console.log(`Webhook ${webhook.data.id} ya procesado`);
return;
}
processedIds.add(webhook.data.id);
// Procesar por tipo
switch (webhook.type) {
case 'RECEIVE':
handleReceive(webhook.data);
break;
case 'TRANSFER':
handleTransfer(webhook.data);
break;
case 'REFUND':
handleRefund(webhook.data);
break;
}
});
function handleReceive(data: WebhookV2Data) {
if (data.status === 'LIQUIDATED') {
const amount = parseFloat(data.payment.amount);
console.log(`PIX recibido: R$ ${amount}`);
// Acreditar en el sistema
}
}
function handleTransfer(data: WebhookV2Data) {
if (data.status === 'LIQUIDATED') {
console.log(`PIX enviado: ${data.endToEndId}`);
// Confirmar transferencia
} else if (data.status === 'ERROR') {
console.log(`PIX fallido: ${data.errorCode}`);
// Revertir operación
}
}
function handleRefund(data: WebhookV2Data) {
if (data.status === 'REFUNDED') {
const refund = data.refunds[0];
console.log(`Devolución: R$ ${refund.payment.amount}`);
// Procesar devolución
}
}Reintentos
Si su endpoint no responde con HTTP 2xx dentro de 10 segundos:
| Intento | Intervalo | Acumulado |
|---|---|---|
| 1ro | Inmediato | 0 min |
| 2do | 5 minutos | 5 min |
| 3ro | 5 minutos | 10 min |
| 4to | 15 minutos | 25 min |
Después de 4 intentos fallidos, el webhook ya no sera reenviado automáticamente.
Implemente un sondeo periodico como respaldo para asegurar que no se pierdan transacciones.