最佳实践
使用 virtualAccount 实现多租户
如果您在一个 Magen 账户下运营多个品牌、门店、分支或合作伙伴,请在每次创建时传入 virtualAccount(最多 50 个字符)。该字段:
- 每个 callback 都会返回,您可立即识别该交易属于哪个租户。
- 可在
GET /user/transactions和GET /pix中过滤,按租户隔离列表。 - 无需创建子账户,一个 Magen 账户即可服务 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/magen"
}{
"id": "MAGEN20251123104518DF75D20A8F",
"status": "PENDING",
"amount": 99.90,
"clientReference": "order-1234",
"virtualAccount": "loja-rj-01",
"qrCodeText": "00020126870014br.gov.bcb.pix..."
}{
"id": "MAGEN20251123104518DF75D20A8F",
"type": "DEPOSIT",
"status": "COMPLETED",
"amount": 99.90,
"clientReference": "order-1234",
"virtualAccount": "loja-rj-01",
"paidAt": "2025-11-23T10:46:26.986Z"
}仅列出某个租户的交易
curl "https://api.magen.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.magen.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.MAGEN_TOKEN}`,
'Content-Type': 'application/json',
},
});res = requests.get(
'https://api.magen.processamento.com/v1/user/transactions',
params={'virtualAccount': 'loja-rj-01', 'dateFrom': '2025-11-01'},
headers={
'Authorization': f'Bearer {os.environ["MAGEN_TOKEN"]}',
'Content-Type': 'application/json',
},
)路由 callback
async function magenWebhook(tx: magenCallback) {
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/magen"
}常见陷阱
| 陷阱 | 症状 |
|---|---|
使用 clientReference 来标识租户 | 无法在列表中过滤,查询复杂化 |
virtualAccount 每次都变化(时间戳、可变 slug) | 列表碎片化 |
不处理缺少 virtualAccount 的 callback | 路由在遗留交易上崩溃 |
| 在处理器中硬编码租户 | 每个新客户都需要手动接入 |
Idempotência
clientReference é a sua chave de idempotência e callbacks chegam mais de uma vez. Como combinar os dois para que retries não viram cobranças duplicadas nem baixas em dobro.
Lidando com callbacks
Responda em menos de 5 segundos, enfileire o processamento pesado, deduplique por id + status e teste localmente com ngrok. O retry da Magen é robusto, mas só funciona se o seu handler se comportar bem.