Boas práticas
Separar lojas e filiais em uma conta
Explica como marcar transações com virtualAccount para separar lojas, filiais ou marcas em uma única conta.
Se você opera várias marcas, lojas, filiais ou parceiros em uma só conta PayZu, passe virtualAccount (até 50 caracteres) em cada criação. Esse campo:
- Volta em todo callback, você sabe imediatamente de qual tenant é a transação.
- Pode ser filtrado em
GET /user/transactionseGET /pix, listas isoladas por tenant. - Dispensa contas filhas, uma só conta PayZu serve N tenants.
Convenções de nome
| Padrão | Quando usar |
|---|---|
tenant-{slug} | Plataforma SaaS multi-cliente. |
loja-{cidade}-{numero} | Rede com lojas físicas. |
mkt-{partner} | Marketplace com vários vendedores. |
filial-{codigo} | Filiais de uma mesma empresa. |
branch-{branchId} | Genérico, em inglês. |
Tamanho máximo: 50 caracteres. Use formato estável e legível. Evite caracteres especiais e espaços.
Criar com virtualAccount
{
"amount": 99.90,
"clientReference": "order-1234",
"virtualAccount": "loja-rj-01",
"callbackUrl": "https://seusite.com.br/webhooks/payzu"
}{
"id": "PAYZU20260811K7M2X9QP4T000000",
"status": "PENDING",
"amount": 99.90,
"clientReference": "order-1234",
"virtualAccount": "loja-rj-01",
"qrCodeText": "00020126870014br.gov.bcb.pix..."
}{
"id": "PAYZU20260811K7M2X9QP4T000000",
"type": "DEPOSIT",
"status": "COMPLETED",
"amount": 99.90,
"clientReference": "order-1234",
"virtualAccount": "loja-rj-01",
"paidAt": "2026-08-11T10:46:26.986Z"
}Listar só de um tenant
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',
},
});Rotear o callback
async function payzuWebhook(tx: PayzuCallback) {
const tenantId = tx.virtualAccount;
if (!tenantId) {
log.warn('callback sem virtualAccount', { id: tx.id });
return;
}
const handler = tenantHandlers[tenantId];
if (!handler) {
log.error('tenant desconhecido', { tenantId, id: tx.id });
return;
}
await handler.process(tx);
}virtualAccount vs clientReference
Os dois são campos independentes e complementares. Use os dois sempre.
| Campo | Granularidade | Propósito |
|---|---|---|
clientReference | Por transação | Idempotência + lookup por pedido. |
virtualAccount | Por tenant | Roteamento + filtro de listagens. |
Basta enviar os dois campos no mesmo payload de criação, como no exemplo acima.
Armadilhas comuns
| Armadilha | Sintoma |
|---|---|
Usar clientReference pra identificar tenant | Não filtra em listagens, complica lookup |
virtualAccount muda toda vez (timestamp, slug variável) | Listagem fica fragmentada |
Não tratar callback sem virtualAccount | Roteamento crasha em transações legadas |
| Hardcode de tenants no handler | Onboarding manual a cada novo cliente |