最佳实践
分页
列出资源的端点(/user/transactions、/user/callbacks、/user/infractions)采用经典的 page + limit 分页方式,并带有 hasNextPage 标志。
循环模式
PAGE=1
LIMIT=100
while : ; do
RESP=$(curl -s "https://api.payzu.processamento.com/v1/user/transactions?dateFrom=2025-11-01&page=$PAGE&limit=$LIMIT" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json")
echo "$RESP" | jq -c '.data[]'
HAS_NEXT=$(echo "$RESP" | jq -r '.hasNextPage')
[ "$HAS_NEXT" != "true" ] && break
PAGE=$((PAGE+1))
doneasync function* iterateTransactions(filters: Record<string, string>) {
let page = 1;
const limit = 100;
while (true) {
const params = new URLSearchParams({ ...filters, page: String(page), limit: String(limit) });
const res = await fetch(`https://api.payzu.processamento.com/v1/user/transactions?${params}`, {
headers: {
Authorization: `Bearer ${process.env.PAYZU_TOKEN}`,
'Content-Type': 'application/json',
},
});
const { data, hasNextPage } = await res.json();
for (const tx of data) yield tx;
if (!hasNextPage) return;
page++;
}
}
for await (const tx of iterateTransactions({ dateFrom: '2025-11-01' })) {
await process(tx);
}def iterate_transactions(**filters):
page = 1
limit = 100
while True:
res = requests.get(
'https://api.payzu.processamento.com/v1/user/transactions',
params={**filters, 'page': page, 'limit': limit},
headers={
'Authorization': f'Bearer {os.environ["PAYZU_TOKEN"]}',
'Content-Type': 'application/json',
},
)
body = res.json()
for tx in body['data']:
yield tx
if not body.get('hasNextPage'):
return
page += 1func IterateTransactions(filters url.Values, fn func(tx Transaction) error) error {
page := 1
for {
filters.Set("page", strconv.Itoa(page))
filters.Set("limit", "100")
req, _ := http.NewRequest("GET",
"https://api.payzu.processamento.com/v1/user/transactions?"+filters.Encode(), nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PAYZU_TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil { return err }
var body struct {
Data []Transaction `json:"data"`
HasNextPage bool `json:"hasNextPage"`
}
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
res.Body.Close()
return err
}
res.Body.Close()
for _, tx := range body.Data {
if err := fn(tx); err != nil { return err }
}
if !body.HasNextPage { return nil }
page++
}
}可用过滤器
| 过滤器 | 使用场景 |
|---|---|
dateFrom / dateTo | 时间窗口(ISO 8601)。 |
clientReference | 查找与您的订单对应的交易。 |
virtualAccount | 按租户过滤(多店铺)。 |
status | CSV:COMPLETED,PENDING。接受多个值。 |
type | CSV:DEPOSIT,WITHDRAW,COMMISSION。 |
endToEndId | Bacen 唯一标识符。 |
document, name | 按付款方过滤。document 仅接受数字(11 位或 14 位)。 |
amount | 按精确金额过滤。 |
限制和页面大小
| 项目 | 值 |
|---|---|
limit 最大值 | 每页 100 条 |
| 默认值 | 20 |
页面过大会降低延迟性能。如果需要长时间段(月、年)或导出全部数据,建议使用异步报告。
何时使用异步报告替代分页
| 场景 | 建议 |
|---|---|
| 屏幕列表(dashboard) | 使用 GET /user/transactions 分页。 |
| 单次查询 | GET /user/transactions?clientReference=order-1234。 |
| 日对账(< 10k 笔交易) | 使用 GET /user/transactions 分页。 |
| 月度/年度对账 | POST /user/report(CSV 异步)。 |
| BI/数据仓库 | 每日运行 POST /user/report,通过 ETL 进行数据摄取。 |
异步报告生成带签名下载 URL 的 CSV 文件。无行数限制,后台运行。请查看对账教程。
常见陷阱
| 陷阱 | 症状 |
|---|---|
在大流量账户中未传 dateFrom 拉取全部数据 | 响应缓慢,可能 timeout |
limit=1000(超出允许范围) | API 拒绝或截断 |
用 data.length === 0 而不是 hasNextPage 进行迭代 | 最后一次迭代空页导致无限循环 |
固定页码(始终 page=1) | 只读取前 100 条,丢失其余数据 |
传递带标点符号的 document(123.456.789-00) | 错误 400,正则仅接受数字 |
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 PayZu é robusto, mas só funciona se o seu handler se comportar bem.
Dinheiro e precisão
A PayZu Pix API usa reais com casas decimais, não centavos. Como armazenar internamente sem perder precisão, converter na borda e respeitar os limites mínimos de cada operação.