Skip to content

Pagar.me — Especificação para os apps Flutter (cliente e painel admin)

Data: 2026-10-21 | Status: Especificação — não implementada nos apps | Contrato da API: Pagar.me — Integração

A API já aceita o Pagar.me; os apps não. Esta página lista, arquivo por arquivo, o que muda em fdplay-front-end (cliente) e fdplay-front-end-admin (painel). Os caminhos foram lidos em 21/10/2026; confirme-os antes de editar.

Ordem importa: o cliente primeiro, o seletor depois

O app do cliente que não conhece um gateway cai em Stripe. Por isso a API recusa pagarme como padrão (409) até allow_as_default = true no credentials.toml. Ligue essa trava só depois de a nova versão do app do cliente estar publicada e a versão mínima (GET /config/client-version) atualizada.


1. App do cliente (fdplay-front-end)

1.1 Reconhecer o gateway — lib/config/payment_config.dart

  • enum PaymentGateway { stripe, asaas } → acrescentar pagarme.
  • fetchActiveGateway(): o switch tem default: → Stripe. Mapear 'pagarme' e tratar valor desconhecido como o último gateway válido (ou Asaas, o padrão da plataforma), nunca como Stripe.
  • Novo getter isPagarmeActive. isPixAvailable hoje é isAsaasActive: passa a incluir o Pagar.me (PIX avulso), sem Pix Automático.
  • PIX do Pagar.me não é recorrente (ver 1.4): a tela não pode prometer "débito automático".

1.2 Serviço — novo lib/services/pagarme_service.dart (par de asaas_service.dart)

Função Rota
getConfig() GET /pagarme/config → public_key
tokenizeCard(card) POST https://api.pagar.me/core/v5/tokens?appId={public_key} → token_xxx (uso único)
subscribe(planId, cardToken, billingAddress, promo?) POST /pagarme/subscribe
subscribePix(planId, promo?) POST /pagarme/subscribe/pix
updateCard(cardToken, billingAddress) PUT /pagarme/subscription/credit-card
cancel() DELETE /pagarme/subscription (ou o genérico DELETE /customers/me/subscription)
renew(cardToken + billingAddress ou payment_method:'pix') PUT /customers/me/subscription/renew

O número do cartão nunca vai para a API. Token usado não se reaproveita: erro → tokenizar de novo.

1.3 Telas de pagamento — ui/widgets/pages/sign_up/widgets/sign_up_payment.dart, ui/widgets/credit_card/payment.dart, ui/widgets/dialogs/account_dialog.dart

  • Escolher o fluxo por activePaymentGateway: no Pagar.me, tokenizar → subscribe; sem criptografia RSA, sem credit_card_holder_info.
  • Endereço de cobrança (CEP, rua, número, bairro, cidade, UF) no formulário do cartão, enviado em billing_address (ADR-0192). Sem ele o Pagar.me recusa todo cartão. 422 com field: billing_address = pedir o endereço.
  • Assinatura nasce pending: repolar GET /customers/me/subscription (já existe, a cada ~4 s) e mostrar "processando". Sair da espera quando o status deixar de ser pending: active entra; cancelled = cartão recusado ("Cartão recusado, tente outro"). Não escrever "Seu cartão foi aceito" enquanto pending.
  • Erros: 400 (falta CPF no perfil — pedir), 402 PAYMENT_DECLINED (recusado na hora: tokenizar de novo e oferecer outro cartão), 504 (não repetir: pode ter cobrado; aguardar e repolar), 502 (gateway indisponível).
  • Trocar cartão: updateCard (in-place), igual ao Asaas/Stripe.

1.4 PIX — ui/widgets/common/pix_expiration_timer.dart e telas de PIX

  • Resposta: info.pix.{qr_code, qr_code_image_url, expiration_date, charge_id} — a imagem vem como URL, não base64.
  • expiration_date é a janela de 10 min; reabrir o QR do mesmo pedido: GET /customers/me/pix-qr (410 = cobrança já não está aberta → gerar outro).
  • Vencido o período, o cliente renova (renew com payment_method:'pix') e recebe um QR novo. Texto da tela: "PIX do período", não "recorrente".

1.5 Ingresso — services/ticket_store_service.dart, ui/widgets/pages/ticket_store_page.dart, ui/widgets/dialogs/ticket_store_dialog.dart

  • POST /store/purchase/pagarme/card (card_token, billing_address, subscribe?) e …/pix; sem cadastro: /store/purchase/checkout/pagarme/{card,pix} + buyer.
  • PIX do ingresso: pix.payload / pix.image_url / pix.expiration_date; sem subscribe no PIX.
  • 504 na compra no cartão: o pedido fica reservado; mostrar "em processamento" e consultar GET /me/ticket-orders (não comprar de novo).

2. Painel admin (fdplay-front-end-admin)

2.1 Seletor de gateway — lib/presentation/views/finance/platform_config_view.dart

  • _gatewayOptions = ['stripe', 'asaas'] (linha ~30) e o rótulo gw == 'stripe' ? 'Stripe' : 'Asaas' (~119) → acrescentar pagarme / "Pagar.me (Stone)".
  • Tratar o 409 do PUT (sem chaves, ou trava allow_as_default desligada) mostrando detail; não "voltar" o seletor em silêncio.
  • Aviso na tela ao escolher Pagar.me: "apps antigos usam Stripe".

2.2 Menu e listagem — lib/config/router/routes_provider.dart, presentation/cubits/api_cubits_presentation.dart, views/asaas_subscriptions/*

  • Novo menu Pagar.me (par de asaasSubscriptions) e rota admin/pagarme/subscriptions; clonar asaas_subscriptions_view.dart e o modelo asaas_subscription_model_domain.dart (campos extras: pagarme_subscription_id, pagarme_customer_id, pagarme_order_id, pagarme_contract_*).
  • Ações da linha: cancelar (POST …/{id}/cancel), pagamentos (GET …/{id}/payments; value em reais, id = ch_xxx), estorno (POST …/{id}/refund, value opcional em reais) e histórico (GET …/{id}/refunds). Mesmo contrato docs + info do Asaas.
  • Estatísticas: GET /admin/pagarme/subscriptions/stats (MRR em centavos; PIX não entra).

2.3 Planos — views/plans/add_plan_view.dart

  • publishTo só liga asaas (e stripe). Aceitar pagarme no toggle; a API não cria nada no gateway (assinatura avulsa), só registra a preferência.
  • Listagem pública de planos para o app: GET /plans?gateway=pagarme devolve todo plano ativo.

2.4 Financeiro — views/finance/finance_view.dart (~1018), widgets/finance_report_pdf.dart (~505, ~879)

  • KPIs: acrescentar "MRR Pagar.me" (overview.mrr_pagarme); o PDF hoje imprime só mrr_asaas/mrr_stripe.
  • Receita por período: o item traz pagarme ao lado de asaas/stripe (centavos).
  • Filtro gateway: aceita pagarme. Dashboard de assinaturas: by_gateway.pagarme e gateway_distribution.pagarme_share.

2.5 Cliente 360 e estorno

  • domain/models/customer_360_model_domain.dart: ler pagarme_charges (mesmo Charge360), isPagarme, pagarme_customer_id, pagarme_subscription_id; somar no resumo. widgets/customer_payments_tab.dart e customer_subscriptions_tab.dart: listar o novo grupo e o rótulo PAGARME.
  • views/finance/refund_by_charge_view.dart: ['stripe', 'asaas'] (~155) → incluir pagarme (charge_id = ch_xxx). 404 = a cobrança não é de nenhuma assinatura/pedido nosso (a conta é compartilhada) — mostrar a mensagem.
  • Cancelar assinatura pela aba do cliente já usa a rota genérica, que detecta o gateway.

3. Critérios de aceite

  1. Com padrão asaas, nada muda em nenhum app.
  2. App novo + padrão pagarme (trava ligada): cartão com token, pending → active pelo polling; PIX com QR por URL; renovar PIX gera novo QR.
  3. App antigo nunca chega a ver pagarme (trava desligada até a versão mínima subir).
  4. Painel: menu Pagar.me lista, cancela e estorna; MRR e receita mostram o gateway; estorno por charge_id recusa cobrança alheia com 404.
  5. Nenhum log ou erro do app contém número de cartão.

Sem verificação local

Esta máquina não tem Flutter SDK: nenhuma dessas mudanças foi compilada nem testada. Validar no sandbox do Pagar.me com chaves de teste (DEBT-154) antes de ligar a trava.