MagenMagen Docs
Best practices

Money and precision

The Magen Pix API uses reais with decimal places, not cents. Heads up: many PSPs work in cents, so if you're migrating or comparing integrations, the decimal point here is not optional.

{ "amount": 99.90 }

Decimal precision in code

In JavaScript, 0.1 + 0.2 !== 0.3. In Python Decimal is safe but float is not. In SQL, FLOAT loses precision.

LanguageUse
JavaScriptInteger in cents, or decimal.js library.
Pythondecimal.Decimal when calculating, float only at the API.
Goshopspring/decimal or integer in cents.
JavaBigDecimal, never double.
PHPbcmath, or integer in cents.
SQL/PostgresNUMERIC(15,2), never FLOAT or REAL.

Store as an integer in cents in your DB and convert only at the API edge:

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('Amount mismatch between callback and order');
}
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('Amount mismatch between callback and order');
}

Minimum limits per operation

Magen validates on the server. A request below the minimum returns 400 Bad Request.

OperationMinimum amount
POST /pix (charge)R$ 1.00
POST /withdraw (withdrawal by key)R$ 0.01
POST /withdraw/qrcode (pay QR)R$ 0.10
POST /internal-transferR$ 0.01

Fee

The fee charged by Magen arrives in the callback in the serviceFeeCharged field (in reais).

{
  "amount": 99.90,
  "serviceFeeCharged": 0.99,
  "status": "COMPLETED"
}

For financial reconciliation, consider:

ValueMeaning
amountWhat the customer paid or you withdrew.
serviceFeeChargedMagen fee on the operation.
Netamount - serviceFeeCharged (incoming) or amount + serviceFeeCharged (total outgoing).

Validate received amount in the callback

Always check that the callback matches the order. The customer may pay a different amount (Pix allows QR without a fixed amount in some cases).

async function handleDepositCallback(tx: magenCallback, order: Order) {
  const callbackCents = reaisToCents(tx.amount);
  if (callbackCents !== order.totalCents) {
    log.warn('Amount mismatch', {
      order: order.totalCents,
      received: callbackCents,
    });
    await flagForReview(order, tx);
    return;
  }
  await markOrderPaid(order, tx);
}

Common pitfalls

PitfallSymptom
Sending amount: 9990 thinking it's centsCharges R$ 9,990.00 from the customer
Storing amount as FLOAT in PostgresLoss of cents when summing many rows
Adding Decimal with float in PythonType error or lost precision
Trusting parseFloat(tx.amount) without rounding99.90 becomes 99.9000000000001
Not checking received amount vs expected amountPartial payment goes through as completed

On this page