Pix Automático: como integrar recorrência segura com webhooks

Pix Automático: como integrar recorrência segura com webhooks

A real é simples: pagamentos recorrentes via Pix Automático são ótimos pro usuário final e um pequeno inferno pra quem precisa manter controle financeiro sem virar refém de notificação de banco. Na minha experiência construindo integrações de pagamento, aprendi que “invisível” só funciona se o backend for obsessivamente visível.

Segundo o Olhardigital.com.br, a popularização do Pix abriu caminho para uma nova etapa em que pagamentos recorrentes deixam de exigir ação do usuário. O texto é curto — boa parte do conteúdo é exclusivo para assinantes do Clube Olhar Digital, da jornalista Flávia Correia — mas o tema rende bastante, especialmente quando olhamos pelo lado técnico. E é por aí que eu vou.

O que é o Pix Automático (e por que devs precisam ligar o radar)

Lançado pelo Banco Central em meados de 2024, o Pix Automático é uma evolução do Pix agendado. Ele permite que um recebedor autorizado cobre valores recorrentes — mensalidades, assinaturas, contas de consumo — sem que o pagador precise confirmar cada transação. O fluxo depende de um consentimento explícito: o usuário autoriza uma vez, define limites (valor máximo, periodicidade, número de cobranças), e o sistema dispara os débitos automaticamente na data combinada.

Diferente do débito automático em conta (que usa o arrasto do banco) e diferente do cartão de crédito (que passa por um gateway global tipo Stripe ou Adyen), o Pix Automático roda na infraestrutura do Open Finance + SPI (Sistema de Pagamentos Instantâneos). Isso muda o jogo:

  • Sem intermediário internacional cobrando 4% + R$ 0,40 por transação.
  • Liquidação em segundos, não em D+30.
  • Mas também: o stack é todo BACEN/DICT, então SDKs e documentação são menos maduros do que os do mercado de cartões.

A arquitetura por trás do “pagar sem pensar”

Três peças conversam nesse fluxo:

  1. DICT — Diretório de Identificadores de Contas Transacionais. Onde ficam as chaves Pix (CPF, e-mail, celular, chave aleatória). Quando o usuário autoriza um recebedor, é uma chave Pix que entra no mandato.
  2. SPI — Sistema de Pagamentos Instantâneos. O mensageiro central do BACEN, responsável por processar as liquidações em tempo real.
  3. PSPs — Prestadores de Serviço de Pagamento. Banco, fintech, gateway. É com quem você, dev, vai falar via API.

O fluxo resumido: seu app pede o consentimento do usuário (escopo, valor máximo, periodicidade); o PSP do pagador cria um mandato e registra no BACEN; na data de vencimento, o recebedor dispara a cobrança; o PSP valida, debita, liquida via SPI, e dispara webhook pra você.

Na Prática: integrando Pix Automático num produto real

Vou montar o cenário que eu já implementei. Você tem um SaaS de R$ 49/mês e quer cobrar via Pix Automático. Aqui está o passo a passo que eu seguiria:

  1. Escolha um PSP com suporte: Efí, Mercado Pago, PagSeguro, Pagar.me, ou direto um dos grandes bancos via Open Finance. Eu uso Efí no meu projeto pessoal — sandbox decente e documentação honesta.
  2. Implemente o fluxo de consentimento. O usuário entra no seu app, clica em “Autorizar cobrança via Pix”, é redirecionado para o ambiente do banco, aprova limites e volta com um redirect_uri contendo um código de autorização.
  3. Troque o código por um mandate_id via API do PSP e salve no seu banco relacional, associado ao customer_id.
  4. Antes de cada cobrança, valide: o mandato está ativo? o valor está dentro do limite aprovado? o cliente não cancelou?
  5. Dispare a cobrança via API: POST /pix/recurring com o mandate_id, valor e uma external_reference própria (idempotency key).
  6. Receba o webhook payment.confirmed ou payment.failed e atualize o status da assinatura.

Aqui um exemplo de handler em Node.js que eu já usei em produção. Cuidado com cada detalhe — esse é o coração da segurança:

import crypto from 'crypto';
import express from 'express';

const PSP_SECRET = process.env['PSP_WEBHOOK_SECRET'];
const processedIds = new Map(); // em produção: Redis com TTL de 24h

function verifySignature(rawBody, signature) {
  const expected = crypto
    .createHmac('sha256', PSP_SECRET)
    .update(rawBody)
    .digest('hex');

  // timingSafeEqual evita ataque de timing
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

const app = express();

// raw body é OBRIGATÓRIO para verificação de assinatura
app.post(
  '/webhooks/pix',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const sig = req.headers['x-psp-signature'];
    if (!sig || !verifySignature(req.body, sig)) {
      return res.status(401).end();
    }

    const event = JSON.parse(req.body);
    const eventId = event.id;

    // idempotência: nunca processar o mesmo evento duas vezes
    if (processedIds.has(eventId)) return res.status(200).end();
    processedIds.set(eventId, Date.now());

    switch (event.type) {
      case 'mandate.created':
        db.customers.update(event.data.customer_ref, {
          mandate_id: event.data.mandate_id,
          status: 'active'
        });
        break;

      case 'payment.confirmed':
        db.subscriptions.update({
          where: { customer_ref: event.data.customer_ref },
          set: {
            status: 'paid',
            last_paid_at: new Date(),
            next_due: addOneMonth(new Date())
          }
        });
        break;

      case 'payment.failed':
        notifyCustomer(
          event.data.customer_ref,
          'Não rolou o Pix automático. Resolve aí antes do bloqueio.'
        );
        break;
    }

    res.status(200).end();
  }
);

Três pilares que eu não negoceo: HMAC, idempotência e raw body. Tire qualquer um deles e você está vendendo entrada grátis pra fraudador.

Segurança que devs ignoram até levar golpe

Pix Automático tem uma particularidade cruel: o estorno é mais burocrático que cartão de crédito. Se você liberar acesso a um webhook sem validar origem, um atacante pode disparar payment.confirmed falsos e seu sistema vai marcar a assinatura como paga — sem um centavo ter entrado.

Os três pilares que eu repito como mantra:

  • Verificação de assinatura HMAC em todo webhook, sempre com raw body. Se você usa express.json(), o parse altera a string e a assinatura quebra. Já vi gente perder 3 dias debugando isso.
  • Idempotência via event_id. O PSP pode reentregar o mesmo webhook em caso de timeout — sem deduplicação, você cobra 2x.
  • Validação de mandato ativo antes de cada disparo. Nunca confie só no cliente; o estado de verdade está no PSP.

Erros Comuns que eu já vi em produção

  • Não salvar o ID do mandato — quando o webhook chega 3 dias depois e você não tem como reconciliar com o customer.
  • Confiar em return res.json() dentro do handler de webhook. O Express parseia o body, a assinatura quebra, e tudo vira 401 silencioso. Use express.raw().
  • Esquecer de tratar payment.failed — a assinatura fica “ativa” pra sempre, sem nunca ter pago de verdade.
  • Hardcodar limite de valor no frontend. Cliente altera via DevTools, seu sistema aceita e o BACEN recusa no fim do fluxo. Limite tem que ter dupla checagem: frontend e backend.
  • Não implementar retry exponencial no disparo da cobrança. Se o PSP estiver fora do ar às 3h da manhã, a cobrança falha e ninguém é notificado.
  • Logs sem mascaramento de PII. Salvar chave Pix crua em log expõe o usuário. Mascarar sempre: ***.123.456-**.
  • Não tratar mandate.revoked — cliente cancelou no banco, você continua achando que a assinatura está ativa. Spoiler: não está.

Controle financeiro pessoal: como devs se viram

Aqui sai do backend e vai pro lado pessoal. Se você assina 5 SaaS via Pix Automático, como saber o que está sendo cobrado sem checar app de banco cinco vezes por dia?

Na minha experiência, o que funciona de verdade:

  • Notificação push do banco ativada para todo Pix que sai. Nubank, Inter e C6 mandam em tempo real.
  • Planilha semanal automatizada via script que puxa extrato via Open Finance. Eu uso um Python simples que consome a API do meu banco e consolida:
import requests
from datetime import datetime, timedelta

TOKEN = "seu_access_token_open_finance"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}

r = requests.get(
    "https://api.banco.com.br/open-finance/transactions",
    headers=HEADERS,
    params={
        "from": (datetime.now() - timedelta(days=7)).isoformat()
    }
)

data = r.json()
total_recorrente = sum(
    tx["amount"]
    for tx in data["transactions"]
    if tx.get("is_pix_automatico") is True
)

print(
    f"Você gastou R$ {total_recorrente:.2f} "
    f"em Pix Automático nos últimos 7 dias."
)

# top 5 assinaturas por valor
top = sorted(
    (tx for tx in data["transactions"] if tx.get("is_pix_automatico")),
    key=lambda t: t["amount"],
    reverse=True
)[:5]
for tx in top:
    print(f"  - {tx['payee_name']}: R$ {tx['amount']:.2f}")
  • Auditoria mensal: reviso todos os mandatos ativos e cancelo o que não uso. É o equivalente tech do “cancela aquela assinatura de streaming que você esqueceu”. No meu caso, descobri que pagava R$ 29/mês num serviço de e-mail transacional que nem usava mais. Dois meses e já tinha “ganho” o ano.

FAQ — perguntas que um dev faria

Pix Automático é seguro?
Tão seguro quanto o Open Finance — que é razoavelmente seguro, com certificados mTLS e criptografia ponta a ponta. O ponto fraco nunca é o protocolo, é a implementação. Por isso HMAC e idempotência são inegociáveis no seu lado.

Posso cancelar um Pix Automático a qualquer momento?
Sim, pelo app do banco. O cancelamento é imediato. O recebedor é notificado e não pode mais disparar cobranças naquele mandato. Não precisa justificar nada.

Qual a diferença entre Pix Automático e cartão de crédito para recorrência?
Pix Automático tem custo por transação muito menor, liquida em segundos, e funciona pra quem não tem cartão. Cartão tem mais proteção ao consumidor (estorno facilitado) e cobertura internacional. Pra SaaS nacional BR-first, Pix Automático está ficando imbatível em custo. Pra produto global, cartão ainda reina.

Preciso de homologação do BACEN pra integrar?
Não. Você usa um PSP certificado. A homologação BACEN é dos PSPs, não dos merchants. Você só precisa cumprir as regras de UX e limites do Open Finance.

Como simulo cobranças em ambiente de sandbox?
Cada PSP tem seu sandbox. Efí e Mercado Pago permitem criar mandatos de teste com valores controlados e webhooks fake. Eu uso o webhook.site pra inspecionar payloads durante o desenvolvimento — economiza horas.

O que fica disso tudo

Pix Automático é UX brilhante pro usuário final, mas exige do dev um nível de disciplina que o cartão de crédito “esconde” atrás do gateway. Quem implementar HMAC, idempotência e validação de mandato vai dormir tranquilo. Quem não fizer, vai descobrir o problema na hora do chargeback — e com Pix, estorno é bem mais doloroso.

Do lado pessoal, a palavra-chave é visibilidade: notificações, auditoria mensal, script puxando extrato. “Pagamento invisível” só é confortável pra quem tem um backend bem visível cuidando das finanças.

Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.

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.