Pagamentos · modules/billing
Adapter pronto para produçãoMercadoPago,
conectado do jeito certo.
A maioria dos starters de SaaS em Next.js é Stripe-first. Quando você precisa cobrar em reais ou pesos, colar o MercadoPago no Next.js na mão é onde moram os bugs clássicos — webhooks sem verificação, handlers não idempotentes e um sandbox que briga com você. A UseDeploy entrega o adapter para você herdar o encanamento, não a dor.
A dor que a gente já comeu
Integrar o MercadoPago tem fama, e os fóruns de devs merecem: "de longe essa é a mais complexa e difícil de testar." O webhook assíncrono é onde ele morde — a verdade sobre um pagamento chega depois do redirect, em uma request assinada à parte que você tem que verificar e reconciliar contra o seu próprio banco de dados. O adapter da UseDeploy é uma implementação real e funcional desse fluxo: preferências de Checkout Pro para o redirect, uma checagem HMAC em tempo constante em cada notificação que chega, e um fetch do status do pagamento que mapeia o resultado no mesmo BillingEvent normalizado que qualquer outro provider emite. Você conecta seu access token; o trilho já está construído.
Webhooks assinados, verificados antes de serem confiados
Cada notificação do MercadoPago é verificada contra o manifest x-signature / x-request-id com HMAC-SHA256 e uma comparação em tempo constante antes de buscar o pagamento e mapeá-lo em um BillingEvent. Uma chamada sem assinatura ou adulterada é rejeitada com um 400 — as classes de cobrança dupla e de webhook falsificado ficam fechadas por padrão.
1 const manifest = `id:${dataId};request-id:${xRequestId};ts:${ts};`;
2 const expected = crypto
3 .createHmac('sha256', this.cfg.webhookSecret)
4 .update(manifest)
5 .digest('hex');
6
7 // constant-time comparison — never a naive === on the digest
8 return crypto.timingSafeEqual(hashBuf, expectedBuf);
O que o adapter faz hoje
Redirect de Checkout Pro
createCheckoutSession faz um POST na API /checkout/preferences e retorna a URL init_point. O modo de pagamento único está ativo; currency_id cobre ARS, BRL, MXN, USD e EUR.
Webhooks assíncronos assinados
parseWebhook verifica a assinatura HMAC, busca /v1/payments/:id, e emite invoice.paid ou invoice.payment_failed no event bus de billing.
Um único contrato tipado
O MercadoPago implementa o mesmo port IPaymentProvider que o Stripe e o Polar — trocar de provider é uma mudança de um único arquivo no composition root.
Event bus durável
Os eventos de billing fluem pelo outbox durável, então um webhook que chega duas vezes ou fora de ordem se reconcilia contra o seu DB em vez de aplicar em dobro.
Catálogo de planos vindo do Prisma
listPlans lê a tabela BillingPlan, então seu pricing vive no seu banco de dados — não hardcoded por provider.
Docs de produto trilíngues
O site Fumadocs entrega páginas reais EN / ES / PT (convenção dot-parser, EN canônico), não só roteamento de locale sobre copy em inglês.
Respostas diretas
Suporta assinaturas recorrentes (preapproval)?+
Como eu testo sem tocar em produção?+
Dá para rodar MercadoPago e Stripe ao mesmo tempo?+
Referência de configuração
Variáveis de ambiente
PAYMENT_PROVIDERobrigatóriaSet to mercado-pago
MERCADO_PAGO_ACCESS_TOKENobrigatóriaMERCADO_PAGO_WEBHOOK_SECRETobrigatóriaHMAC secret for x-signature verification
Onde ele vive
apps/server/src/modules/billing/infrastructure/providers/mercado-pago-payment-provider.tsapps/server/src/modules/billing/infrastructure/providers/index.ts
Pare de colar o MercadoPago no Next.js na mão.
Comece sobre uma base onde o checkout, os webhooks assinados e o contrato de billing já estão conectados — e localizados em EN, ES e PT.