DEV Community

José Gonçalves
José Gonçalves

Posted on

The Developer's Guide to Brazilian Fintech: Building Payment Systems With Pix, Boleto, and Cards

If you're building fintech products for Brazil, you're entering one of the most exciting — and complex — payment ecosystems in the world. Pix processed over R$17 trillion in 2023. Credit card installments are a cultural institution. Boleto bancário refuses to die. And the Central Bank keeps launching new features that reshape the landscape every few months.

At Mind Group Technologies, we've built payment systems for fintech clients across Brazil since 2016. From processing Pix payments in real-time to handling credit card installments across multiple acquirers, we've learned that Brazilian payments are a world unto themselves.

Here's the practical guide I wish someone had given me when we started.

Understanding the Brazilian Payment Landscape

Pix: The Game Changer

Pix launched in November 2020 and fundamentally changed how Brazilians move money. Within three years, it became the most-used payment method in the country. The numbers are staggering:

  • 150+ million registered Pix keys
  • R$17+ trillion processed in 2023
  • Average transaction time: 3-10 seconds
  • Available: 24/7/365, including holidays
  • Cost to merchants: Free for individuals, near-zero for businesses

For developers, Pix is accessed through the banking APIs of authorized Payment Service Providers (PSPs). The Central Bank defines the standard, but each PSP implements it slightly differently.

// Simplified Pix payment flow
async function createPixPayment(amount, description, tenantId) {
  // 1. Generate a unique transaction ID
  const txId = generateTxId(); // Max 35 chars, alphanumeric

  // 2. Create the charge via PSP API
  const charge = await pspClient.createCharge({
    calendario: { expiracao: 3600 }, // 1 hour expiration
    devedor: { cpf: customerCPF, nome: customerName },
    valor: { original: amount.toFixed(2) },
    chave: merchantPixKey, // Merchant's Pix key
    solicitacaoPagador: description,
    infoAdicionais: [
      { nome: 'tenant_id', valor: tenantId },
      { nome: 'order_id', valor: orderId }
    ]
  });

  // 3. Generate QR Code
  const qrCode = await pspClient.generateQRCode(charge.loc.id);

  return {
    txId,
    qrCodeImage: qrCode.imagemQrcode, // Base64 PNG
    qrCodePayload: qrCode.qrcode,      // Copy-paste string
    expiresAt: new Date(Date.now() + 3600000)
  };
}
Enter fullscreen mode Exit fullscreen mode

Critical implementation detail: Pix confirmations come via webhooks. You MUST implement idempotent webhook handlers because the Central Bank may send duplicate notifications.

// Idempotent Pix webhook handler
async function handlePixWebhook(payload) {
  const { txid, valor, horario } = payload.pix[0];

  // Check if already processed (idempotency)
  const existing = await db.query(
    'SELECT id FROM pix_transactions WHERE txid = $1',
    [txid]
  );

  if (existing.rows.length > 0) {
    return { status: 'already_processed' };
  }

  // Process within a transaction
  await db.transaction(async (trx) => {
    await trx.query(
      'INSERT INTO pix_transactions (txid, amount, timestamp) VALUES ($1, $2, $3)',
      [txid, valor.original, horario]
    );
    await trx.query(
      'UPDATE orders SET payment_status = $1 WHERE pix_txid = $2',
      ['confirmed', txid]
    );
  });

  // Notify customer in real-time
  await notifyPaymentConfirmed(txid);

  return { status: 'processed' };
}
Enter fullscreen mode Exit fullscreen mode

Credit Card Installments (Parcelamento)

This is where Brazilian payments get uniquely complex. In most countries, you charge a credit card once. In Brazil, customers expect to split purchases into monthly installments — and they get upset if you don't offer it.

There are two types:

  1. Parcelamento sem juros (interest-free installments): The merchant absorbs the cost. The customer pays R$100 in 3x of R$33.33.
  2. Parcelamento com juros (installments with interest): The customer pays interest. R$100 becomes 3x of R$35.50.
// Calculate installment options
function calculateInstallments(totalAmount, maxInstallments = 12) {
  const options = [];

  for (let i = 1; i <= maxInstallments; i++) {
    if (i === 1) {
      options.push({
        installments: 1,
        installmentAmount: totalAmount,
        totalAmount: totalAmount,
        interestFree: true,
        label: `1x de R$ ${totalAmount.toFixed(2)} (à vista)`
      });
    } else if (i <= 3) {
      // Interest-free up to 3x (merchant absorbs MDR)
      const installmentAmount = totalAmount / i;
      options.push({
        installments: i,
        installmentAmount: installmentAmount,
        totalAmount: totalAmount,
        interestFree: true,
        label: `${i}x de R$ ${installmentAmount.toFixed(2)} sem juros`
      });
    } else {
      // With interest (typically 1.99% per month)
      const monthlyRate = 0.0199;
      const installmentAmount = totalAmount * 
        (monthlyRate * Math.pow(1 + monthlyRate, i)) / 
        (Math.pow(1 + monthlyRate, i) - 1);
      const totalWithInterest = installmentAmount * i;
      options.push({
        installments: i,
        installmentAmount: installmentAmount,
        totalAmount: totalWithInterest,
        interestFree: false,
        label: `${i}x de R$ ${installmentAmount.toFixed(2)} (total: R$ ${totalWithInterest.toFixed(2)})`
      });
    }
  }

  return options;
}
Enter fullscreen mode Exit fullscreen mode

Business consideration: The number of interest-free installments you offer directly impacts your conversion rate. Offering 3x sem juros is nearly standard in Brazilian e-commerce. Offering 6x or 12x sem juros can increase conversion 20-40% but eats into your margins.

Boleto Bancário: The Undead Payment Method

Boleto is a uniquely Brazilian payment slip that can be paid at any bank, lottery house, or through banking apps. It's been "dying" for a decade but still processes billions in transactions because:

  • ~30 million Brazilians don't have credit cards
  • Businesses use it for B2B transactions
  • Some customers prefer it for large purchases
// Generate a boleto
async function generateBoleto(amount, customer, dueDate) {
  const boleto = await boletoProvider.create({
    amount: amount,
    dueDate: dueDate,
    customer: {
      name: customer.name,
      cpf: customer.cpf,
      address: customer.address
    },
    instructions: [
      'Não receber após o vencimento',
      'Multa de 2% após vencimento'
    ],
    fine: { percentage: 2.0 },  // Late fee
    interest: { percentage: 1.0 } // Monthly interest on late payment
  });

  return {
    barcode: boleto.barcode,
    digitableLine: boleto.digitableLine, // Typeable line
    pdfUrl: boleto.pdfUrl,
    dueDate: dueDate,
    // Important: boletos take 1-3 business days to confirm
    expectedConfirmation: addBusinessDays(new Date(), 3)
  };
}
Enter fullscreen mode Exit fullscreen mode

Key difference from other methods: Boleto confirmation is NOT real-time. It can take 1-3 business days. Your system needs to handle this async flow gracefully — hold the order, send reminders, and handle expiration.

Building a Multi-Method Payment Service

At Mind Group, we built a payment abstraction layer that handles all Brazilian payment methods through a unified interface. Here's the architecture:

┌──────────────────────────────────────────┐
│         Payment Gateway Service           │
│                                          │
│  ┌──────────────────────────────────┐    │
│  │     Unified Payment Interface     │    │
│  │  createPayment(method, amount)    │    │
│  │  getStatus(paymentId)             │    │
│  │  refund(paymentId, amount)        │    │
│  └──────────────────────────────────┘    │
│           │           │          │        │
│  ┌────────┤   ┌───────┤   ┌─────┤        │
│  ▼        ▼   ▼       ▼   ▼     ▼        │
│ ┌────┐ ┌────┐ ┌──────┐ ┌──────┐         │
│ │Pix │ │Card│ │Boleto│ │Wallet│         │
│ │Adpt│ │Adpt│ │Adapt │ │Adapt │         │
│ └────┘ └────┘ └──────┘ └──────┘         │
│   │      │       │        │              │
├───┼──────┼───────┼────────┼──────────────┤
│   ▼      ▼       ▼        ▼              │
│  PSP   Acquirer  Bank   Digital          │
│  API    API      API    Wallet           │
└──────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Each adapter implements the same interface but handles the specifics of each payment method. This means our application code doesn't need to know whether a customer is paying with Pix or credit card — the payment service handles the differences.

Handling Multiple Acquirers

In Brazil, it's common to work with multiple credit card acquirers (Cielo, Rede, Stone, PagSeguro) for redundancy and better rates. We implemented an intelligent routing system:

// Acquirer routing logic
async function routeCardPayment(payment) {
  const { amount, cardBrand, installments, tenantId } = payment;

  // Get tenant's acquirer configuration
  const config = await getTenantPaymentConfig(tenantId);

  // Score each available acquirer
  const scores = config.acquirers.map(acquirer => ({
    acquirer,
    score: calculateScore(acquirer, {
      // Lower MDR = better score
      mdr: acquirer.rates[cardBrand]?.[installments] || 999,
      // Higher uptime = better score
      uptime: acquirer.metrics.uptime30d,
      // Lower latency = better score  
      latency: acquirer.metrics.avgLatencyMs,
      // Current success rate matters
      successRate: acquirer.metrics.successRate24h
    })
  }));

  // Sort by score, try best first with fallback
  const sorted = scores.sort((a, b) => b.score - a.score);

  for (const { acquirer } of sorted) {
    try {
      const result = await acquirer.processPayment(payment);
      return result;
    } catch (error) {
      logger.warn(`Acquirer ${acquirer.name} failed, trying next`, error);
      continue;
    }
  }

  throw new PaymentError('All acquirers failed');
}
Enter fullscreen mode Exit fullscreen mode

Compliance: What You Can't Ignore

LGPD (Brazil's GDPR)

Financial data is the most sensitive category under LGPD. Requirements:

  • Explicit consent for data collection and processing
  • Data minimization — only collect what you need
  • Right to deletion — customers can request data removal
  • Breach notification — 72 hours to notify the authority (ANPD)
  • DPO requirement — you need a Data Protection Officer

Central Bank Regulations

If you're processing payments, you need to comply with Central Bank of Brazil (BCB) regulations:

  • Resolution 4,658: Cloud computing requirements for financial institutions
  • Circular 3,978: Anti-money laundering (PLD/FT) requirements
  • PIX regulations: Specific rules for Pix participants

PCI DSS

If you handle credit card data, PCI compliance is mandatory. Our recommendation: use tokenization through your acquirer's vault. Never store raw card numbers.

// NEVER do this
const payment = {
  cardNumber: '4111111111111111', // RAW CARD NUMBER - VIOLATION
  cvv: '123'
};

// DO this instead - tokenize through the acquirer
const token = await acquirer.tokenize({
  encryptedCard: rsaEncrypt(cardData, acquirerPublicKey)
});

// Store and reuse the token
const payment = {
  cardToken: token.id, // Safe to store
  acquirerId: 'cielo'
};
Enter fullscreen mode Exit fullscreen mode

Pix Innovations to Watch

The Central Bank keeps evolving Pix with new features that create opportunities for developers:

  • Pix Automático (automatic recurring Pix) — subscription billing without credit cards
  • Pix por Aproximação (contactless Pix) — NFC payments at physical stores
  • Pix Garantido (guaranteed Pix) — installment payments via Pix, potentially replacing credit card parcelamento
  • Pix Internacional — cross-border Pix payments

Each of these creates new integration requirements and business opportunities. At Mind Group, we're already building support for Pix Automático into our multi-tenant payment platform, as it could significantly reduce payment processing costs for our fintech clients.

Practical Tips From the Trenches

After 8+ years building payment systems in Brazil:

  1. Always have a fallback acquirer. Single points of failure in payments cost real money.

  2. Test with real money in sandbox. Brazilian payment sandbox environments are notoriously unreliable. Test with small real transactions before going live.

  3. Handle CPF validation properly. CPF (Brazilian tax ID) is required for most payment methods. Validate the check digits, don't just check length.

  4. Time zone matters. Brazil has multiple time zones and DST rules change. Payment deadlines (especially boleto) must account for this.

  5. Reconciliation is harder than processing. Build automated reconciliation from day one. When you process thousands of transactions across multiple methods and acquirers, manual reconciliation is impossible.

  6. Monitor chargebacks aggressively. Brazil has one of the highest chargeback rates in the world. Implement fraud detection (device fingerprinting, velocity checks, address verification) before you need it.

The Opportunity

Brazil's fintech sector attracted over $3 billion in investment in recent years. The Central Bank is one of the most innovation-friendly regulators in the world. And there are still massive gaps — 30+ million unbanked adults, SMBs struggling with payment infrastructure, and entire sectors that haven't digitized their payment flows.

For developers willing to navigate the complexity, Brazilian fintech is one of the most rewarding spaces to build in. The payment ecosystem is rich, the market is enormous, and the problems are real.


José Gonçalves is the Founder of Mind Group Technologies, a software company in Sorocaba, Brazil, specializing in fintech, healthcare, and SaaS platforms. We've been building payment systems and multi-tenant platforms since 2016. Learn more at mindconsulting.com.br.

Top comments (0)