Avistadocs
Webhooks V2

概述

什么是 V2 Webhooks?

V2 Webhooks 是当 PIX 交易中发生重要事件时发送到您应用程序的通知。V2 格式使用 {type, data} 信封结构,使处理更加简便,并提供每个事件的更多详细信息。

要接收 V2 Webhooks,您的账户必须将 Webhook 版本配置为 V2。请参阅如何激活

基本结构

所有 V2 Webhooks 遵循以下结构:

{
  "type": "RECEIVE" | "TRANSFER" | "REFUND",
  "data": {
    // 事件特定数据
  }
}

事件类型

类型描述V1 等价
RECEIVE收到 PIX(Cash-In)CashIn
TRANSFER发送 PIX(Cash-Out)CashOut
REFUND退款(入账或出账)CashInReversal / CashOutReversal

完整数据结构

interface WebhookV2Data {
  // 标识符
  id: number;                    // 交易 ID
  txId: string | null;           // 收款标识符(txid)
  endToEndId: string | null;     // PIX 交易的端到端 ID

  // PIX 密钥
  pixKey: string | null;         // 使用的 PIX 密钥

  // 状态
  status: 'PENDING' | 'LIQUIDATED' | 'REFUNDED' | 'ERROR';

  // 付款
  payment: {
    amount: string;              // 金额(带 2 位小数的字符串)
    currency: string;            // 货币(BRL)
  };

  // 退款
  refunds: RefundInfo[];         // 退款列表(如无则为空)

  // 日期
  createdAt: string;             // 创建日期(ISO 8601)

  // 错误
  errorCode: string | null;      // 错误码(如有)

  // 操作类型
  webhookType: 'RECEIVE' | 'TRANSFER' | 'REFUND';
  creditDebitType: 'CREDIT' | 'DEBIT';
  transactionType: 'PIX';
  localInstrument: 'DICT';

  // 账户
  debtorAccount: AccountInfo;    // 付款方/发送方
  creditorAccount: AccountInfo;  // 收款方/接收方

  // 幂等性
  idempotencyKey: string | null;

  // 附加数据
  ticketData: object;
  remittanceInformation: string | null;  // 交易描述
}

interface AccountInfo {
  ispb: string | null;           // 银行 ISPB 代码
  name: string | null;           // 银行名称
  issuer: string | null;         // 银行代码
  number: string | null;         // 账号
  document: string | null;       // CPF/CNPJ(已脱敏)
  accountType: string | null;    // 账户类型
}

interface RefundInfo {
  status: 'PENDING' | 'LIQUIDATED' | 'ERROR';
  payment: {
    amount: number;              // 退款金额(数值类型!)
    currency: string;
  };
  errorCode: string | null;
  eventDate: string;             // 退款日期
  endToEndId: string | null;     // 退款 E2E ID
  information: string | null;    // 退款描述
}

V1 与 V2 的区别

方面V1V2
格式字段在根层级信封结构 {type, data}
事件类型event: "CashIn"type: "RECEIVE"
方面V1V2
PIX 成功CONFIRMEDLIQUIDATED
退款成功CONFIRMEDREFUNDED
错误ERRORERROR
方面V1V2
字段counterpartdebtorAccount / creditorAccount
银行bank.bankNamename
ISPBbank.bankISPBispb
方面V1V2
类型numberstring
格式100.00"100.00"

账户映射

收到 PIX(RECEIVE)

debtorAccount = 付款方(交易对手)
creditorAccount = 您的账户(收款方)
creditDebitType = CREDIT

发送 PIX(TRANSFER)

debtorAccount = 您的账户(付款方)
creditorAccount = 收款方(交易对手)
creditDebitType = DEBIT

收款退款(REFUND - CashInReversal)

debtorAccount = 您的账户(退款方)
creditorAccount = 退款接收方(交易对手)
creditDebitType = DEBIT

转账退款(REFUND - CashOutReversal)

debtorAccount = 退款方(交易对手)
creditorAccount = 您的账户(退款接收方)
creditDebitType = CREDIT

端点配置

要求

  • 必须使用 HTTPS URL
  • 最大超时时间:10 秒
  • 期望响应:HTTP 2xx

身份认证

Webhooks 使用 Basic Auth 发送:

Authorization: Basic base64(username:password)

在控制面板中配置凭据或联系技术支持。

处理器示例

import express from 'express';

const app = express();
app.use(express.json());

interface WebhookV2 {
  type: 'RECEIVE' | 'TRANSFER' | 'REFUND';
  data: WebhookV2Data;
}

// 用于幂等性的集合
const processedIds = new Set<number>();

app.post('/webhooks/pix', (req, res) => {
  const webhook: WebhookV2 = req.body;

  // 立即响应
  res.status(200).json({ acknowledged: true });

  // 检查幂等性
  if (processedIds.has(webhook.data.id)) {
    console.log(`Webhook ${webhook.data.id} already processed`);
    return;
  }
  processedIds.add(webhook.data.id);

  // 按类型处理
  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 received: R$ ${amount}`);
    // 在系统中入账
  }
}

function handleTransfer(data: WebhookV2Data) {
  if (data.status === 'LIQUIDATED') {
    console.log(`PIX sent: ${data.endToEndId}`);
    // 确认转账
  } else if (data.status === 'ERROR') {
    console.log(`PIX failed: ${data.errorCode}`);
    // 回滚操作
  }
}

function handleRefund(data: WebhookV2Data) {
  if (data.status === 'REFUNDED') {
    const refund = data.refunds[0];
    console.log(`Refund: R$ ${refund.payment.amount}`);
    // 处理退款
  }
}

重试机制

如果您的端点未在 10 秒内返回 HTTP 2xx 响应:

尝试次数间隔累计
第 1 次立即0 分钟
第 2 次5 分钟5 分钟
第 3 次5 分钟10 分钟
第 4 次15 分钟25 分钟

4 次尝试失败后,Webhook 将不再自动重发。

请实现定期轮询作为后备方案,以确保不会遗漏任何交易。

后续步骤

本页目录