Pix Automático: como integrar recorrência via API com Node.js

Pix Automático: como integrar recorrência via API com Node.js

Pix Automático é a evolução natural do débito em conta. Como dev, já vi muita gente — e muito código — tratando recorrência como se fosse 1995 e um boleto de papel. O Banco Central finalmente entregou uma API nativa, padronizada e auditável pra isso. Vou te mostrar o que muda na vida de quem programa, os riscos reais e como integrar de verdade.

O que é Pix Automático e por que devs deveriam prestar atenção

Segundo o Olhardigital.com.br, o Pix já consolidou o celular como meio de pagamento default do brasileiro. O próximo passo — o tal pagamento “invisível” — é justamente o Pix Automático: recorrência nativa do Sistema de Pagamentos Instantâneos (SPI), autorizada uma vez pelo usuário no app do banco e executada sem nova confirmação a cada ciclo.

Na minha experiência, isso muda o jogo pra três frentes de produto:

  • Fintechs e SaaS que cobram mensalidade (assinatura sem fricção de cartão)
  • Contas recorrentes físicas: energia, internet, academia
  • Split de pagamentos e marketplaces que dependiam de DDA legado

O resumo operacional é simples: o dev (credor) gera um identificador de recorrência e apresenta ao pagador; o pagador autoriza no app do banco, com biometria; a partir daí, o SPI processa o débito automaticamente nas datas combinadas. Tudo padronizado, tudo auditável.

Como funciona a arquitetura — visão de quem programa

Aqui é onde fica interessante pra quem escreve código. Pix Automático não é mágica: é REST, OAuth 2.0 e mTLS em cima do arranjo aberto do Banco Central, com Open Finance Brasil como tecido conectivo entre PSPs e instituições.

As cinco camadas que importam no seu código:

  1. Iniciação: o credor cria a recorrência via API no PSP e recebe um QR/cópia-cola dinâmico.
  2. Autorização: o pagador escaneia/cola no app do banco e autentica (biometria ou senha).
  3. Persistência: o PSP retorna um txid — a chave de identidade da recorrência. Salve no banco no mesmo segundo.
  4. Execução: nas datas combinadas, o SPI debita automaticamente.
  5. Notificação: o PSP envia um webhook assinado confirmando (ou falhando) a execução.

Diferente do antigo Débito Direto Autorizado (DDA), aqui não existe homologação individual, não tem D+1 ou D+2 — é instantâneo, tem revogação direta pelo app do banco e segue um padrão aberto. Isso, pra quem opera SaaS, é uma redução brutal de fricção operacional.

Segurança — o que mudou e o que devs ainda erram

Pix Automático não é “DDA 2.0”. As diferenças de segurança são estruturais e mexem com o código:

  • Autenticação forte no lado do banco: biometria e senha ficam no app do banco. Você, dev, nunca vê credencial sensível. Recebe só um token de autorização com escopo limitado e prazo curto.
  • Janela de revogação imediata: o pagador cancela quando quiser pelo app. Isso força você a entregar valor real todo ciclo — senão perde o contrato.
  • Limites definidos na origem: valor máximo, periodicidade e data são travados na autorização. O banco valida antes de cada débito.
  • Trilha de auditoria centralizada: tudo passa pelo SPI, com logs imutáveis no Banco Central.

O erro clássico que eu vejo em código real:

// NÃO faça isso
async function cobrarUsuario(userId, valor) {
  const userBank = await db.bankTokens.findOne({ userId });
  // ❌ Token salvo em claro, sem rotação, sem escopo
  await pixApi.debit(userBank.token, valor);
}

Esse padrão é furada em três dimensões: o token não é “cobrança”, é prova de consentimento com escopo e expiração; se você trata como credencial persistente, vira passivo de segurança; e nada disso te dá direito de “forçar” o débito — só de solicitar a recorrência no fluxo autorizado.

Na Prática — integrando Pix Automático com Node.js

Vou te mostrar um exemplo funcional, simplificado mas fiel ao que vai pra produção. Usei Express porque é o que entra em produtos reais; troque pelo seu framework sem mudar a lógica.

// server.js
import express from 'express';
import crypto from 'node:crypto';
import fs from 'node:fs';
import https from 'node:https';
import axios from 'axios';

const app = express();
app.use(express.json());

const PSP = {
  clientId: process.env.PSP_CLIENT_ID,
  clientSecret: process.env.PSP_CLIENT_SECRET,
  certPath: './certs/production.p12',
  certPass: process.env.PSP_CERT_PASS,
  webhookSecret: process.env.PSP_WEBHOOK_SECRET,
  baseUrl: 'https://api.pix.exemplo.com/v1',
};

function agent() {
  return new https.Agent({
    pfx: fs.readFileSync(PSP.certPath),
    passphrase: PSP.certPass,
  });
}

// 1. OAuth2 com mTLS (não funciona sem o certificado)
async function getToken() {
  const { data } = await axios.post(
    `${PSP.baseUrl}/oauth/token`,
    { grant_type: 'client_credentials' },
    {
      httpsAgent: agent(),
      auth: { username: PSP.clientId, password: PSP.clientSecret },
    }
  );
  return data.access_token;
}

// 2. Cria recorrência e devolve o QR pro usuário autorizar
app.post('/api/recorrencias', async (req, res) => {
  const { valor, descricao, usuarioId } = req.body;

  try {
    const token = await getToken();

    const { data } = await axios.post(
      `${PSP.baseUrl}/recorrencias`,
      {
        valor: { original: Number(valor).toFixed(2) },
        calendario: {
          dataInicio: new Date().toISOString().split('T')[0],
          periodicidade: 'MENSAL',
        },
        infoDevedor: 0, // o devedor é identificado pelo txid
        // nunca envie CPF, agência ou dado sensível aqui
      },
      {
        httpsAgent: agent(),
        headers: { Authorization: `Bearer ${token}` },
      }
    );

    await db.recorrencias.insert({
      usuarioId,
      txid: data.txid,
      valor,
      descricao,
      status: 'AGUARDANDO_AUTORIZACAO',
      criadaEm: new Date(),
    });

    return res.status(201).json({
      txid: data.txid,
      qrCode: data.qrcode,
      copiaECola: data.copiaECola,
    });
  } catch (err) {
    console.error('falha ao criar recorrencia', err.response?.data);
    return res.status(502).json({ erro: 'falha_ao_criar_recorrencia' });
  }
});

// 3. Webhook assinado — entrada do PSP
app.post('/webhooks/pix', (req, res) => {
  const signature = req.headers['x-signature'];
  const payload = JSON.stringify(req.body);
  const expected = crypto
    .createHmac('sha256', PSP.webhookSecret)
    .update(payload)
    .digest('hex');

  if (signature !== expected) {
    return res.status(401).send('assinatura invalida');
  }

  const { txid, status } = req.body;
  db.recorrencias.update({ txid }, { status, atualizadoEm: new Date() });

  return res.status(204).send();
});

app.listen(3000);

Pontos críticos que eu reforço em code review:

  • mTLS é obrigatório: sem o certificado mútuo (.p12) o PSP nem responde. Não é opcional.
  • Webhook sempre validado por assinatura: aceitar payload sem checar HMAC é abrir a porta do cofre.
  • txid é a identidade da verdade: tudo no Banco Central passa por ele. Perdeu o txid, perdeu o controle da recorrência.
  • Não persista dado sensível do devedor: você só precisa do txid e de um identificador interno seu (ex.: usuarioId).

Comparação real — Pix Automático vs alternativas

Solução Latência Custo típico (credor) Padronização Risco de inadimplência
Pix Automático Instantâneo (D+0) R$ 0,10 a R$ 0,50 / transação Banco Central Baixo (consentimento forte)
Cartão de crédito D+1 a D+30 2% a 4% + MDR Bandeiras Médio (chargeback)
Boleto D+1 a D+3 Tarifa bancária Febraban Alto (esquecimento)
DDA legado D+1 Tarifa bancária Bancária (fechada) Médio

Pix Automático vence em custo e padronização. Cartão ainda manda em dispute resolution internacional e em conversão fora do Brasil. Pra SaaS brasileiro vendendo pra PF/PJ nacional, a régua hoje é Pix Automático com cartão como fallback.

Erros Comuns — o que evitar quando você coloca isso em produção

1) Tratar recorrência como cobrança avulsa. Recorrência tem ciclo de vida próprio: AGUARDANDO_AUTORIZACAO → ATIVA → CANCELADA → CONCLUIDA → FALHADA. Modele todos os estados. Só salvar “pago/não pago” te deixa cego em renewal, churn e retries.

2) Não validar assinatura do webhook. Já vi sistema em produção processar webhook sem checar HMAC. Mesmo erro de sempre: “é interno, tá no VPC”. Webhook é superfície pública. Valide sempre.

3) Esquecer o caminho de falha. Saldo insuficiente, banco fora do ar, conta bloqueada — a recorrência pode falhar. Implemente backoff exponencial e notifique o pagador. Não trate execução como coisa garantida.

4) Misturar dados sensíveis no payload. Você só precisa do txid. Não peça CPF, não peça agência, não peça nada que o banco já tem. Quanto menos dado sensível trafegar, menos superfície de ataque você entrega.

5) Não reagir à revogação em tempo real. O usuário pode cancelar pelo app a qualquer momento. Use o webhook de cancelamento, não polling diário. Se você só descobre o cancelamento no próximo débito, já entregou valor de graça.

6) Versionar contrato e esquecimento. Quando o status do serviço muda (novo valor, nova periodicidade), crie uma nova recorrência — não tente editar a ativa. PSPs não tratam bem update de recorrência autorizada, e o usuário precisa reautorizar de qualquer forma.

FAQ — perguntas que devs reais fazem

Pix Automático é gratuito pra pessoa física?
Sim, pra pessoa física não há tarifa do Banco Central. Pra empresa credora, o PSP cobra por transação — normalmente entre R$ 0,10 e R$ 0,50, dependendo do volume e contrato.

Preciso de homologação no Banco Central?
Não diretamente. Você usa um PSP certificado (Stone, Mercado Pago, PagSeguro, Cielo, etc.). Eles já são homologados e expõem a API.

Funciona pra pessoa jurídica (CNPJ) como credor e devedor?
Sim, tanto PJ quanto PF podem figurar nos dois lados. É particularmente útil pra B2B recorrente.

Qual a diferença entre Pix Automático e Pix por QR dinâmico?
O QR dinâmico é pagamento único — o usuário confirma toda vez. O Pix Automático é recorrência autorizada uma vez, executada automaticamente nas datas combinadas.

Posso usar em marketplace com split de pagamento?
Sim, mas com split nativo no PSP. Você precisa separar o credor (marketplace) do recebedor final (vendedor). A maioria dos PSPs já suporta isso na API de recorrência.

O que acontece se o usuário cancelar a autorização?
O PSP dispara um webhook de cancelamento. Sua próxima execução não acontece, e o status da recorrência passa para CANCELADA. A partir daí, trate o devedor como novo lead — reofereça o serviço com nova autorização, não tente “renovar” a antiga.

Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto — especialmente se quiser ver um exemplo completo com retry, idempotência e modelagem de estados no Postgres.

Y

Yuri Sousa

Front-End Developer / Designer

Desenvolvedor apaixonado por criar experiências digitais acessíveis e visualmente perfeitas. Escrevo sobre desenvolvimento web, design e tecnologia.