Fire Bankingdocs
Webhooks V2

RECEIVE

Descripción general

El webhook RECEIVE se envia cuando un PIX es recibido en su cuenta. Este evento indica que alguien pago un QR Code generado por su aplicación o realizo una transferencia directa a su clave PIX.

Cuando se envia

  • Pago de QR Code (cobro) confirmado
  • Transferencia directa a la clave PIX de la cuenta

Estructura del Payload

{
  "type": "RECEIVE",
  "data": {
    "id": 123,
    "txId": "7978c0c97ea847e78e8849634473c1f1",
    "pixKey": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906",
    "status": "LIQUIDATED",
    "payment": {
      "amount": "100.00",
      "currency": "BRL"
    },
    "refunds": [],
    "createdAt": "2024-01-15T10:30:00.000Z",
    "errorCode": null,
    "endToEndId": "E12345678901234567890123456789012",
    "ticketData": {},
    "webhookType": "RECEIVE",
    "debtorAccount": {
      "ispb": "18236120",
      "name": "NU PAGAMENTOS S.A.",
      "issuer": "260",
      "number": "12345-6",
      "document": "123.xxx.xxx-xx",
      "accountType": null
    },
    "idempotencyKey": null,
    "creditDebitType": "CREDIT",
    "creditorAccount": {
      "ispb": null,
      "name": null,
      "issuer": null,
      "number": null,
      "document": null,
      "accountType": null
    },
    "localInstrument": "DICT",
    "transactionType": "PIX",
    "remittanceInformation": "Pagamento pedido #12345"
  }
}

Campos Importantes

typestring

Siempre "RECEIVE" para PIX recibidos.

data.idnumber

ID de la transacción. Use para idempotencia.

data.txIdstring

Identificador del cobro (txid del endpoint /cob). Puede ser null para transferencias directas.

data.endToEndIdstring

End to End ID - identificador único de la transacción PIX en el Banco Central.

data.statusstring

Estado de la transacción:

  • LIQUIDATED: Pago confirmado (exito)
  • ERROR: Falla en el procesamiento
data.paymentobject

data.debtorAccountobject

Datos de quien pago (el pagador/emisor).

data.creditDebitTypestring

Siempre "CREDIT" para recepciones.

data.refundsarray

Lista de devoluciones. Vacia para transacciones sin devoluciones.

data.remittanceInformationstring

Descripción de la transferencia (si fue proporcionada por el pagador).

Procesamiento del Webhook

Ejemplo en Node.js

interface ReceiveWebhook {
  type: 'RECEIVE';
  data: {
    id: number;
    txId: string | null;
    status: 'LIQUIDATED' | 'ERROR';
    payment: {
      amount: string;
      currency: string;
    };
    endToEndId: string;
    debtorAccount: {
      name: string | null;
      document: string | null;
    };
    remittanceInformation: string | null;
  };
}

async function handleReceive(webhook: ReceiveWebhook) {
  const { data } = webhook;

  if (data.status !== 'LIQUIDATED') {
    console.log(`PIX no confirmado: ${data.status}`);
    return;
  }

  // Convertir valor de string a numero
  const amount = parseFloat(data.payment.amount);

  // Buscar orden por txId (si es un cobro)
  if (data.txId) {
    const order = await findOrderByTxId(data.txId);
    if (order) {
      await markOrderAsPaid(order.id, {
        amount,
        endToEndId: data.endToEndId,
        payer: data.debtorAccount.name,
      });
      return;
    }
  }

  // Recepcion sin cobro asociado
  await createGenericCredit({
    amount,
    endToEndId: data.endToEndId,
    payer: data.debtorAccount.name,
    description: data.remittanceInformation,
  });
}

Ejemplo en Python

from decimal import Decimal

def handle_receive(webhook: dict):
    data = webhook['data']

    if data['status'] != 'LIQUIDATED':
        print(f"PIX no confirmado: {data['status']}")
        return

    # Convertir valor
    amount = Decimal(data['payment']['amount'])

    # Procesar por txId si existe
    if data.get('txId'):
        order = find_order_by_txid(data['txId'])
        if order:
            mark_order_as_paid(
                order_id=order.id,
                amount=amount,
                e2e_id=data['endToEndId'],
                payer=data['debtorAccount'].get('name')
            )
            return

    # Credito generico
    create_generic_credit(
        amount=amount,
        e2e_id=data['endToEndId'],
        payer=data['debtorAccount'].get('name'),
        description=data.get('remittanceInformation')
    )

Correlacion con el Cobro

Si el PIX fue pagado via un QR Code generado por el endpoint /cob/:txid, el campo txId contendra el identificador:

{
  "type": "RECEIVE",
  "data": {
    "txId": "7978c0c97ea847e78e8849634473c1f1",
    // ...
  }
}

Use este campo para correlacionar con sus registros internos:

// Crear cobro
const charge = await createCob('my-txid-123', { valor: '100.00' });

// Guardar asociacion
await saveOrder({
  orderId: 'order-456',
  txId: 'my-txid-123',
  status: 'PENDING'
});

// En el webhook RECEIVE
if (webhook.data.txId === 'my-txid-123') {
  await updateOrder('order-456', { status: 'PAID' });
}

Manejo de Errores

Si status === 'ERROR', verifique el campo errorCode:

if (data.status === 'ERROR') {
  console.error(`Error de PIX: ${data.errorCode}`);

  // Notificar sobre la falla
  await notifyPaymentError({
    txId: data.txId,
    errorCode: data.errorCode,
  });
}

Idempotencia

Use data.id para evitar el procesamiento duplicado:

const PROCESSED_KEY = 'processed_webhooks';

async function handleWebhook(webhook: ReceiveWebhook) {
  const webhookId = `receive:${webhook.data.id}`;

  // Verificar si ya fue procesado
  const isProcessed = await redis.sismember(PROCESSED_KEY, webhookId);
  if (isProcessed) {
    console.log(`Webhook ${webhookId} ya procesado`);
    return;
  }

  // Marcar como procesado ANTES de procesar
  await redis.sadd(PROCESSED_KEY, webhookId);

  // Procesar
  await handleReceive(webhook);
}

Mejores Prácticas

Próximos Pasos

En esta página