最佳实践
资金与精度
Magen Pix API 使用带小数位的雷亚尔,而非分。请注意:许多 PSP 以分为单位运作,因此如果您正在迁移或对比集成方案,这里的小数点不是可选项。
{ "amount": 99.90 }代码中的小数精度
在 JavaScript 中,0.1 + 0.2 !== 0.3。在 Python 中 Decimal 是安全的,但 float 不是。在 SQL 中,FLOAT 会损失精度。
| 语言 | 使用 |
|---|---|
| JavaScript | 以分为单位的整数,或 decimal.js 库。 |
| Python | 计算时使用 decimal.Decimal,仅在 API 处使用 float。 |
| Go | shopspring/decimal 或以分为单位的整数。 |
| Java | BigDecimal,绝不使用 double。 |
| PHP | bcmath,或以分为单位的整数。 |
| SQL/Postgres | NUMERIC(15,2),绝不使用 FLOAT 或 REAL。 |
推荐模式:内部以分存储
在数据库中以分为单位的整数存储,仅在 API 边界处转换:
function centsToReais(cents: number): number {
return cents / 100;
}
function reaisToCents(reais: number): number {
return Math.round(reais * 100);
}
await createPixCharge({
amount: centsToReais(order.totalCents),
clientReference: `order-${order.id}`,
});
const callbackAmountCents = reaisToCents(callback.amount);
if (callbackAmountCents !== order.totalCents) {
throw new Error('callback 与订单金额不一致');
}from decimal import Decimal
def cents_to_reais(cents: int) -> Decimal:
return Decimal(cents) / Decimal(100)
def reais_to_cents(reais: float | Decimal) -> int:
return int((Decimal(str(reais)) * Decimal(100)).quantize(Decimal('1')))
create_pix_charge({
'amount': float(cents_to_reais(order.total_cents)),
'clientReference': f'order-{order.id}',
})import "github.com/shopspring/decimal"
func CentsToReais(cents int64) decimal.Decimal {
return decimal.NewFromInt(cents).Div(decimal.NewFromInt(100))
}
func ReaisToCents(reais decimal.Decimal) int64 {
return reais.Mul(decimal.NewFromInt(100)).Round(0).IntPart()
}<?php
function centsToReais(int $cents): float {
return $cents / 100;
}
function reaisToCents(float $reais): int {
return (int) round($reais * 100);
}
createPixCharge([
'amount' => centsToReais($order->totalCents),
'clientReference' => 'order-' . $order->id,
]);
$callbackAmountCents = reaisToCents($callback['amount']);
if ($callbackAmountCents !== $order->totalCents) {
throw new RuntimeException('callback 与订单金额不一致');
}各操作最低限额
Magen 在服务端校验。低于最低限额的请求会返回 400 Bad Request。
| 操作 | amount 最低额 |
|---|---|
POST /pix(收款) | R$ 1,00 |
POST /withdraw(按密钥提现) | R$ 0,01 |
POST /withdraw/qrcode(支付 QR Code) | R$ 0,10 |
POST /internal-transfer | R$ 0,01 |
手续费
Magen 收取的手续费会通过 callback 在 serviceFeeCharged 字段中返回(单位为雷亚尔)。
{
"amount": 99.90,
"serviceFeeCharged": 0.99,
"status": "COMPLETED"
}财务对账时,请考虑:
| 数值 | 含义 |
|---|---|
amount | 客户支付或您提现的金额。 |
serviceFeeCharged | 该操作的 Magen 手续费。 |
| 净额 | amount - serviceFeeCharged(入账)或 amount + serviceFeeCharged(总支出)。 |
校验 callback 中收到的金额
务必确认 callback 与订单一致。客户可能支付不同的金额(在某些场景下 Pix 允许 QR Code 不固定金额)。
async function handleDepositCallback(tx: magenCallback, order: Order) {
const callbackCents = reaisToCents(tx.amount);
if (callbackCents !== order.totalCents) {
log.warn('金额不一致', {
pedido: order.totalCents,
recebido: callbackCents,
});
await flagForReview(order, tx);
return;
}
await markOrderPaid(order, tx);
}常见陷阱
| 陷阱 | 症状 |
|---|---|
误以为是分,发送 amount: 9990 | 向客户收取 R$ 9.990,00 |
在 Postgres 中将 amount 存为 FLOAT | 多行求和时丢失分 |
在 Python 中将 Decimal 与 float 相加 | 类型错误或精度丢失 |
直接使用 parseFloat(tx.amount) 而不四舍五入 | 99.90 变成 99.9000000000001 |
| 不校验收到金额与预期金额 | 部分支付被误判为已完成 |
Paginação
Endpoints de listagem (transações, callbacks, infrações) usam paginação clássica por page + limit com flag hasNextPage. Para janelas grandes prefira o relatório assíncrono em CSV.
Segurança
Token Bearer é a única credencial da Magen. Trate como senha. Esta página cobre armazenamento, rotação, mascaramento em log, proteção do webhook por IP e validação DICT antes de pagar.