RECEIVE
Visão Geral
O webhook RECEIVE é enviado quando um PIX é recebido na sua conta. Este evento indica que alguém pagou um QR Code gerado pela sua aplicação ou fez uma transferência direta para sua chave PIX.
Quando é enviado
- Pagamento de QR Code (cobrança) confirmado
- Transferência direta para chave PIX da conta
Estrutura do 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
typestringSempre "RECEIVE" para PIX recebido.
data.idnumberID da transação. Use para idempotência.
data.txIdstringIdentificador da cobrança (txid do endpoint /cob). Pode ser null para transferências diretas.
data.endToEndIdstringEnd to End ID - identificador único da transação PIX no Banco Central.
data.statusstringStatus da transação:
LIQUIDATED: Pagamento confirmado (sucesso)ERROR: Falha no processamento
data.paymentobjectdata.debtorAccountobjectDados de quem pagou (o pagador/remetente).
data.creditDebitTypestringSempre "CREDIT" para recebimentos.
data.refundsarrayLista de devoluções. Vazio para transações sem devolução.
data.remittanceInformationstringDescrição da transferência (se informada pelo pagador).
Processando o Webhook
Exemplo 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 não confirmado: ${data.status}`);
return;
}
// Converter valor de string para number
const amount = parseFloat(data.payment.amount);
// Encontrar pedido pelo txId (se for cobrança)
if (data.txId) {
const order = await findOrderByTxId(data.txId);
if (order) {
await markOrderAsPaid(order.id, {
amount,
endToEndId: data.endToEndId,
payer: data.debtorAccount.name,
});
return;
}
}
// Recebimento sem cobrança associada
await createGenericCredit({
amount,
endToEndId: data.endToEndId,
payer: data.debtorAccount.name,
description: data.remittanceInformation,
});
}Exemplo Python
from decimal import Decimal
def handle_receive(webhook: dict):
data = webhook['data']
if data['status'] != 'LIQUIDATED':
print(f"PIX não confirmado: {data['status']}")
return
# Converter valor
amount = Decimal(data['payment']['amount'])
# Processar por txId se existir
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
# Crédito genérico
create_generic_credit(
amount=amount,
e2e_id=data['endToEndId'],
payer=data['debtorAccount'].get('name'),
description=data.get('remittanceInformation')
)Correlação com Cobrança
Se o PIX foi pago via QR Code gerado pelo endpoint /cob/:txid, o campo txId conterá o identificador:
{
"type": "RECEIVE",
"data": {
"txId": "7978c0c97ea847e78e8849634473c1f1",
// ...
}
}Use este campo para correlacionar com seus registros internos:
// Criar cobrança
const cobrança = await createCob('meu-txid-123', { valor: '100.00' });
// Salvar associação
await saveOrder({
orderId: 'pedido-456',
txId: 'meu-txid-123',
status: 'PENDING'
});
// No webhook RECEIVE
if (webhook.data.txId === 'meu-txid-123') {
await updateOrder('pedido-456', { status: 'PAID' });
}Tratamento de Erros
Se status === 'ERROR', verifique o campo errorCode:
if (data.status === 'ERROR') {
console.error(`Erro no PIX: ${data.errorCode}`);
// Notificar sobre falha
await notifyPaymentError({
txId: data.txId,
errorCode: data.errorCode,
});
}Idempotência
Use data.id para evitar processamento duplicado:
const PROCESSED_KEY = 'processed_webhooks';
async function handleWebhook(webhook: ReceiveWebhook) {
const webhookId = `receive:${webhook.data.id}`;
// Verificar se já processou
const isProcessed = await redis.sismember(PROCESSED_KEY, webhookId);
if (isProcessed) {
console.log(`Webhook ${webhookId} já processado`);
return;
}
// Marcar como processado ANTES de processar
await redis.sadd(PROCESSED_KEY, webhookId);
// Processar
await handleReceive(webhook);
}