API de Dashboard Financeiro
Endpoints para o painel administrativo financeiro. Fornece KPIs, series temporais para graficos, lista de inadimplentes, analise de churn e exportacao Excel.
Prefixo: /api/v1/admin/finance
Autenticacao: Bearer token (JWT) + user_type='admin'
Endpoints
| Metodo |
Endpoint |
Descricao |
| GET |
/admin/finance/overview |
KPIs consolidados (MRR, ARR, inadimplentes, churn) |
| GET |
/admin/finance/revenue |
Serie temporal de receita (diario/mensal) |
| GET |
/admin/finance/overdue |
Lista de clientes inadimplentes |
| GET |
/admin/finance/churn |
Analise de churn mensal |
| GET |
/admin/finance/export |
Exportar dados financeiros em Excel (.xlsx) |
1. GET /admin/finance/overview
KPIs financeiros consolidados. Dados prontos para cards de dashboard.
GET /api/v1/admin/finance/overview
Authorization: Bearer <admin_token>
Response (200 OK)
{
"mrr": 384070,
"mrr_stripe": 246760,
"mrr_asaas": 137310,
"arr": 4608840,
"total_customers": 13797,
"active_subscriptions": 193,
"overdue_count": 34,
"churn_rate_30d": 4.0,
"total_refunded": 0,
"total_chargebacks": 0,
"net_revenue_30d": 384070,
"average_ticket": 1990,
"payment_success_rate": 19.7,
"by_payment_method": {
"credit_card": { "count": 112, "mrr": 222880 },
"pix": { "count": 81, "mrr": 161190 }
},
"by_plan": {
"plan-basic": { "count": 193, "mrr": 384070, "name": "Plano Mensal Basico" }
}
}
Campos
| Campo |
Tipo |
Descricao |
mrr |
int |
Monthly Recurring Revenue (centavos) |
mrr_stripe |
int |
MRR somente Stripe (centavos) |
mrr_asaas |
int |
MRR somente Asaas (centavos) |
arr |
int |
Annual Recurring Revenue = MRR * 12 (centavos) |
total_customers |
int |
Total de customers cadastrados |
active_subscriptions |
int |
Assinaturas com status active |
overdue_count |
int |
Assinaturas com payment_failures > 0 ou suspended |
churn_rate_30d |
float |
Taxa de churn ultimos 30 dias (%) |
total_refunded |
int |
Total reembolsado (centavos) |
total_chargebacks |
int |
Total de chargebacks |
net_revenue_30d |
int |
Receita liquida 30d = MRR - refunds (centavos) |
average_ticket |
int |
Ticket medio = MRR / ativos (centavos) |
payment_success_rate |
float |
Taxa de sucesso de pagamento (%) |
by_payment_method |
dict |
Breakdown por metodo (credit_card, pix, boleto) |
by_plan |
dict |
Breakdown por plano (count, mrr, name) |
Uso no frontend (cards)
// Converter centavos para reais
String formatBRL(int cents) => 'R\$ ${(cents / 100).toStringAsFixed(2)}';
// Cards sugeridos:
// - MRR: formatBRL(overview.mrr)
// - ARR: formatBRL(overview.arr)
// - Assinaturas ativas: overview.active_subscriptions
// - Inadimplentes: overview.overdue_count (vermelho se > 0)
// - Churn 30d: "${overview.churn_rate_30d}%"
// - Ticket medio: formatBRL(overview.average_ticket)
2. GET /admin/finance/revenue
Serie temporal de receita. Dados formatados para graficos de linha/barra.
GET /api/v1/admin/finance/revenue?period=monthly&start=2026-01&end=2026-03
Authorization: Bearer <admin_token>
Query Parameters
| Parametro |
Tipo |
Default |
Descricao |
period |
string |
monthly |
monthly ou daily |
start |
string |
6 meses atras |
Inicio (YYYY-MM mensal, YYYY-MM-DD diario) |
end |
string |
hoje |
Fim (mesmo formato) |
Response (200 OK)
{
"period": "monthly",
"data": [
{
"date": "2026-01",
"gross": 79600,
"refunds": 0,
"chargebacks": 0,
"net": 79600,
"new_subscriptions": 7,
"cancellations": 0,
"stripe": 45000,
"asaas": 34600
},
{
"date": "2026-02",
"gross": 246760,
"refunds": 0,
"chargebacks": 0,
"net": 246760,
"new_subscriptions": 85,
"cancellations": 3,
"stripe": 150000,
"asaas": 96760
},
{
"date": "2026-03",
"gross": 218900,
"refunds": 0,
"chargebacks": 0,
"net": 218900,
"new_subscriptions": 0,
"cancellations": 0,
"stripe": 130000,
"asaas": 88900
}
],
"totals": {
"gross": 545260,
"refunds": 0,
"chargebacks": 0,
"net": 545260,
"new_subscriptions": 92,
"cancellations": 3
}
}
Uso no frontend (graficos)
// Grafico de linha: Receita mensal
// X: data[].date
// Y1: data[].gross (receita bruta)
// Y2: data[].net (receita liquida)
// Grafico de barras empilhadas: Stripe vs Asaas
// X: data[].date
// Stack 1: data[].stripe
// Stack 2: data[].asaas
// Grafico de linha: Novas vs Cancelamentos
// X: data[].date
// Y1: data[].new_subscriptions (verde)
// Y2: data[].cancellations (vermelho)
3. GET /admin/finance/overdue
Lista paginada de clientes com pagamentos em atraso.
GET /api/v1/admin/finance/overdue?page=0&limit=20
Authorization: Bearer <admin_token>
Query Parameters
| Parametro |
Tipo |
Default |
Descricao |
page |
int |
0 |
Pagina (zero-indexed) |
limit |
int |
20 |
Itens por pagina (max 100) |
Response (200 OK)
{
"total_overdue": 34,
"total_amount_at_risk": 39800,
"customers": [
{
"customer_id": "69b1a2b3c4d5e6f7a8b9c0d1",
"username": "joao_silva",
"email": "joao@example.com",
"full_name": "Joao da Silva",
"plan_id": "plan-basic",
"plan_name": "Plano Mensal Basico",
"gateway": "asaas",
"payment_method": "credit_card",
"payment_failures": 3,
"last_payment_at": "2026-02-15T00:00:00+00:00",
"next_billing_date": "2026-03-15T00:00:00+00:00",
"days_overdue": 12,
"subscription_status": "suspended",
"amount": 1990
}
]
}
Campos do customer inadimplente
| Campo |
Tipo |
Descricao |
customer_id |
string |
ID do customer (MongoDB ObjectId) |
username |
string |
Username |
email |
string |
Email |
full_name |
string |
Nome completo |
plan_id |
string |
Slug do plano |
plan_name |
string |
Nome do plano |
gateway |
string |
Gateway (stripe/asaas) |
payment_method |
string |
Metodo (credit_card/pix/boleto) |
payment_failures |
int |
Falhas consecutivas de pagamento |
last_payment_at |
string |
Ultimo pagamento (ISO 8601) |
next_billing_date |
string |
Proxima cobranca (ISO 8601) |
days_overdue |
int |
Dias em atraso |
subscription_status |
string |
Status da assinatura |
amount |
int |
Valor do plano (centavos) |
Uso no frontend (tabela)
// Tabela com colunas:
// | Cliente | Email | Plano | Gateway | Falhas | Dias Atraso | Valor | Status |
//
// Ordenar por payment_failures DESC (mais criticos primeiro)
// Badge vermelho se days_overdue > 7
// Badge amarelo se payment_failures >= 3
4. GET /admin/finance/churn
Analise de churn mensal com breakdown por motivo.
GET /api/v1/admin/finance/churn?months=6
Authorization: Bearer <admin_token>
Query Parameters
| Parametro |
Tipo |
Default |
Descricao |
months |
int |
6 |
Meses de historico (1-24) |
Response (200 OK)
{
"monthly": [
{
"month": "2026-01",
"active_start": 33,
"new": 7,
"cancelled": 0,
"active_end": 40,
"churn_rate": 0.0
},
{
"month": "2026-02",
"active_start": 39,
"new": 85,
"cancelled": 3,
"active_end": 121,
"churn_rate": 7.7
},
{
"month": "2026-03",
"active_start": 110,
"new": 122,
"cancelled": 8,
"active_end": 224,
"churn_rate": 7.3
}
],
"by_reason": {
"payment_failure": 0,
"customer_cancelled": 11,
"admin_cancelled": 0,
"chargeback": 0,
"expired": 2
}
}
Uso no frontend (graficos)
// Grafico de area: Evolucao de assinantes
// X: monthly[].month
// Y: monthly[].active_end
// Grafico de barras: New vs Cancelled por mes
// X: monthly[].month
// Barra verde: monthly[].new
// Barra vermelha: monthly[].cancelled
// Grafico de pizza: Motivos de cancelamento
// Fatias: by_reason (payment_failure, customer_cancelled, admin_cancelled, chargeback, expired)
5. GET /admin/finance/export
Exporta dados financeiros consolidados em xlsx (default), csv (zip multi-aba) ou json. Padrão visual definido em ADR-059 (título + metadados + freeze + autofilter + zebra + auto-width + formatos numéricos nativos).
Duas formas de autenticacao (para compatibilidade com download no browser):
# Opcao 1: Header (padrao, via fetch/http)
GET /api/v1/admin/finance/export?start=2026-01-01&end=2026-03-27
Authorization: Bearer <admin_token>
# Opcao 2: Query param (para window.open / <a href> no browser)
GET /api/v1/admin/finance/export?token=<jwt>&start=2026-01-01&end=2026-03-27&format=xlsx
Query Parameters
| Parametro |
Tipo |
Default |
Descricao |
format |
string |
xlsx |
xlsx (multi-aba), csv (zip), json (aninhado) |
start |
string |
90 dias atras |
Data inicio (YYYY-MM-DD) |
end |
string |
hoje |
Data fim (YYYY-MM-DD) |
token |
string |
- |
JWT token (alternativa ao header, para download no browser) |
Response
format |
Content-Type |
Filename |
xlsx |
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
fdplay_financeiro_<start>_<end>.xlsx |
csv |
application/zip (multi-aba) |
fdplay_financeiro_<start>_<end>.zip |
json |
application/json; charset=utf-8 |
fdplay_financeiro_<start>_<end>.json |
Abas do Excel
| Aba |
Conteudo |
Colunas |
| Resumo |
KPIs do periodo |
Metrica, Valor |
| Assinaturas |
Todas as subscriptions no periodo |
ID, Cliente, Email, Plano, Gateway, Metodo, Status, Valor, Inicio, Expiracao, Ultimo Pagamento, Falhas, Reembolsado |
| Inadimplentes |
Customers com payment_failures > 0 |
Cliente, Email, Plano, Gateway, Metodo, Falhas, Ultimo Pagamento, Prox. Cobranca, Dias Atraso, Valor, Status |
| Cancelamentos |
Subscriptions canceladas no periodo |
Cliente, Email, Plano, Gateway, Data Cancelamento, Valor |
| Reembolsos |
Eventos de refund/chargeback no periodo |
Evento, Tipo, Subscription, Admin, Motivo, Data |
Uso no frontend
// Botao "Exportar Excel" que faz download direto
Future<void> exportFinance(String start, String end) async {
// Token via query param — funciona com window.open no browser
final url =
'$baseUrl/api/v1/admin/finance/export?token=$token&start=$start&end=$end';
// Web: window.open dispara download nativo do browser
if (kIsWeb) {
html.window.open(url, '_blank');
return;
}
// Mobile: salvar via dio + path_provider
final response = await dio.get(
url,
options: Options(responseType: ResponseType.bytes),
);
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/fdplay_financeiro.xlsx');
await file.writeAsBytes(response.data);
// Abrir com app nativo
OpenFile.open(file.path);
}
Integracao Flutter/Dart
Service completo
import 'dart:convert';
import 'package:http/http.dart' as http;
class FinanceService {
final String baseUrl;
final String token;
FinanceService({required this.baseUrl, required this.token});
Map<String, String> get _headers => {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
/// KPIs consolidados
Future<Map<String, dynamic>> getOverview() async {
final r = await http.get(
Uri.parse('$baseUrl/api/v1/admin/finance/overview'),
headers: _headers,
);
return jsonDecode(r.body);
}
/// Serie temporal de receita
Future<Map<String, dynamic>> getRevenue({
String period = 'monthly',
String? start,
String? end,
}) async {
final params = <String, String>{'period': period};
if (start != null) params['start'] = start;
if (end != null) params['end'] = end;
final uri = Uri.parse('$baseUrl/api/v1/admin/finance/revenue')
.replace(queryParameters: params);
final r = await http.get(uri, headers: _headers);
return jsonDecode(r.body);
}
/// Clientes inadimplentes
Future<Map<String, dynamic>> getOverdue({int page = 0, int limit = 20}) async {
final uri = Uri.parse(
'$baseUrl/api/v1/admin/finance/overdue?page=$page&limit=$limit',
);
final r = await http.get(uri, headers: _headers);
return jsonDecode(r.body);
}
/// Analise de churn
Future<Map<String, dynamic>> getChurn({int months = 6}) async {
final uri = Uri.parse(
'$baseUrl/api/v1/admin/finance/churn?months=$months',
);
final r = await http.get(uri, headers: _headers);
return jsonDecode(r.body);
}
/// URL para download do Excel (com token na query p/ browser)
String getExportUrl({String? start, String? end}) {
final params = <String, String>{'token': token};
if (start != null) params['start'] = start;
if (end != null) params['end'] = end;
return Uri.parse('$baseUrl/api/v1/admin/finance/export')
.replace(queryParameters: params)
.toString();
}
}
Sugestao de Layout do Dashboard
+---------------------------------------------+
| MRR | ARR | Ticket Medio |
| R$ 3.840,70 | R$ 46.088 | R$ 19,90 |
+---------------------------------------------+
| Ativos | Inadimpl. | Churn 30d |
| 193 | 34 (!) | 4.0% |
+---------------------------------------------+
[Grafico Linha: Receita Mensal - Bruto vs Liquido]
[Grafico Barras: Stripe vs Asaas por mes]
+---------------------------------------------+
| [Grafico Pizza: | [Grafico Barras: |
| Metodos Pagamento] | Novos vs Cancela] |
+---------------------------------------------+
[Tabela: Inadimplentes - ordenado por falhas]
| Cliente | Email | Falhas | Dias Atraso | Valor |
|---------|-------|--------|-------------|-------|
[Botao: Exportar Excel] [Filtro: Periodo]
Errors
| HTTP |
Code |
Quando |
| 401 |
UNAUTHORIZED |
Token JWT ausente ou invalido |
| 403 |
FORBIDDEN |
Usuario nao e admin |
| 422 |
VALIDATION_ERROR |
Parametros de query invalidos (datas, periodo) |
Notas Tecnicas
- Valores em centavos: Todos os campos monetarios (mrr, arr, amount, gross, net) sao em centavos. Frontend converte:
cents / 100 para exibir em reais.
- Churn rate: Calculado como
(cancelled_no_mes / ativos_inicio_mes) * 100.
- Overdue: Inclui subscriptions com
payment_failures > 0 OU status = 'suspended'.
- Export: Gera
.xlsx com openpyxl. Headers estilizados (fundo escuro, fonte branca). Colunas auto-dimensionadas.
- Performance: Overview faz ~5 queries MongoDB. Revenue itera subscriptions em memoria (ok para <10k subs). Para escala maior, migrar para aggregation pipeline.