Fire Bankingdocs
Webhooks V2

RECEIVE

概述

RECEIVE Webhook 在您的账户收到 PIX 时发送。此事件表示有人支付了您应用程序生成的二维码,或直接向您的 PIX 密钥发起了转账。

发送时机

  • 二维码(收款)付款确认
  • 直接向账户 PIX 密钥的转账

负载结构

{
  "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"
  }
}

重要字段

typestring

收到 PIX 时始终为 "RECEIVE"

data.idnumber

交易 ID。用于幂等性处理。

data.txIdstring

收款标识符(来自 /cob 端点的 txid)。直接转账时可为 null

data.endToEndIdstring

端到端 ID - PIX 交易在中央银行的唯一标识符。

data.statusstring

交易状态:

  • LIQUIDATED:付款已确认(成功)
  • ERROR:处理失败
data.paymentobject

data.debtorAccountobject

付款方(发送方)的数据。

data.creditDebitTypestring

收款时始终为 "CREDIT"

data.refundsarray

退款列表。无退款的交易为空数组。

data.remittanceInformationstring

转账描述(如付款方提供)。

处理 Webhook

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 not confirmed: ${data.status}`);
    return;
  }

  // 将金额从字符串转换为数值
  const amount = parseFloat(data.payment.amount);

  // 通过 txId 查找订单(如果是收款)
  if (data.txId) {
    const order = await findOrderByTxId(data.txId);
    if (order) {
      await markOrderAsPaid(order.id, {
        amount,
        endToEndId: data.endToEndId,
        payer: data.debtorAccount.name,
      });
      return;
    }
  }

  // 无关联收款的入账
  await createGenericCredit({
    amount,
    endToEndId: data.endToEndId,
    payer: data.debtorAccount.name,
    description: data.remittanceInformation,
  });
}

Python 示例

from decimal import Decimal

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

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

    # 转换金额
    amount = Decimal(data['payment']['amount'])

    # 如果存在 txId,按 txId 处理
    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

    # 通用入账
    create_generic_credit(
        amount=amount,
        e2e_id=data['endToEndId'],
        payer=data['debtorAccount'].get('name'),
        description=data.get('remittanceInformation')
    )

与收款的关联

如果 PIX 是通过 /cob/:txid 端点生成的二维码支付的,txId 字段将包含该标识符:

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

使用此字段与您的内部记录进行关联:

// 创建收款
const charge = await createCob('my-txid-123', { valor: '100.00' });

// 保存关联
await saveOrder({
  orderId: 'order-456',
  txId: 'my-txid-123',
  status: 'PENDING'
});

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

错误处理

如果 status === 'ERROR',请检查 errorCode 字段:

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

  // 通知付款失败
  await notifyPaymentError({
    txId: data.txId,
    errorCode: data.errorCode,
  });
}

幂等性

使用 data.id 避免重复处理:

const PROCESSED_KEY = 'processed_webhooks';

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

  // 检查是否已处理
  const isProcessed = await redis.sismember(PROCESSED_KEY, webhookId);
  if (isProcessed) {
    console.log(`Webhook ${webhookId} already processed`);
    return;
  }

  // 在处理前标记为已处理
  await redis.sadd(PROCESSED_KEY, webhookId);

  // 处理
  await handleReceive(webhook);
}

最佳实践

后续步骤

本页目录