PayZuPayZu Docs
最佳实践

使用 virtualAccount 实现多租户

如果您在一个 PayZu 账户下运营多个品牌、门店、分支或合作伙伴,请在每次创建时传入 virtualAccount(最多 50 个字符)。该字段:

  • 每个 callback 都会返回,您可立即识别该交易属于哪个租户。
  • 可在 GET /user/transactionsGET /pix 中过滤,按租户隔离列表。
  • 无需创建子账户,一个 PayZu 账户即可服务 N 个租户。

命名约定

格式使用场景
tenant-{slug}多客户 SaaS 平台。
loja-{cidade}-{numero}拥有实体门店的连锁。
mkt-{partner}拥有多个卖家的市场平台。
filial-{codigo}同一公司的分支机构。
branch-{branchId}通用英文格式。

最大长度:50 个字符。请使用稳定且可读的格式。避免使用特殊字符和空格。

使用 virtualAccount 创建

{
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "callbackUrl": "https://seusite.com.br/webhooks/payzu"
}
{
  "id": "PAYZU20251123104518DF75D20A8F",
  "status": "PENDING",
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "qrCodeText": "00020126870014br.gov.bcb.pix..."
}
{
  "id": "PAYZU20251123104518DF75D20A8F",
  "type": "DEPOSIT",
  "status": "COMPLETED",
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "paidAt": "2025-11-23T10:46:26.986Z"
}

仅列出某个租户的交易

curl "https://api.payzu.processamento.com/v1/user/transactions?virtualAccount=loja-rj-01&dateFrom=2025-11-01" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"
const url = new URL('https://api.payzu.processamento.com/v1/user/transactions');
url.searchParams.set('virtualAccount', 'loja-rj-01');
url.searchParams.set('dateFrom', '2025-11-01');

const res = await fetch(url, {
  headers: {
    Authorization: `Bearer ${process.env.PAYZU_TOKEN}`,
    'Content-Type': 'application/json',
  },
});
res = requests.get(
    'https://api.payzu.processamento.com/v1/user/transactions',
    params={'virtualAccount': 'loja-rj-01', 'dateFrom': '2025-11-01'},
    headers={
        'Authorization': f'Bearer {os.environ["PAYZU_TOKEN"]}',
        'Content-Type': 'application/json',
    },
)

路由 callback

async function payzuWebhook(tx: PayzuCallback) {
  const tenantId = tx.virtualAccount;
  if (!tenantId) {
    log.warn('callback 缺少 virtualAccount', { id: tx.id });
    return;
  }

  const handler = tenantHandlers[tenantId];
  if (!handler) {
    log.error('未知租户', { tenantId, id: tx.id });
    return;
  }

  await handler.process(tx);
}

virtualAccount 对比 clientReference

两者是独立且互补的字段。请始终同时使用。

字段粒度用途
clientReference每笔交易幂等性 + 按订单查询。
virtualAccount每个租户路由 + 列表过滤。

组合使用示例:

{
  "amount": 99.90,
  "clientReference": "order-1234",
  "virtualAccount": "loja-rj-01",
  "callbackUrl": "https://seusite.com.br/webhooks/payzu"
}

常见陷阱

陷阱症状
使用 clientReference 来标识租户无法在列表中过滤,查询复杂化
virtualAccount 每次都变化(时间戳、可变 slug)列表碎片化
不处理缺少 virtualAccount 的 callback路由在遗留交易上崩溃
在处理器中硬编码租户每个新客户都需要手动接入

On this page