Fase 55: Monetização Open-Core — Billing, Carteira Aratá, Split e Payouts
Duração: 6 semanas (45 dias úteis)
Prioridade: 🔴 P0
Onda: S1 — Fundação de receita
Depende de: FASE6, FASE7, FASE15, FASE54
Estimativa Total: 180 horas
Status: 🟡 Em progresso (quote/receipt + gate comercial v0)
Spec SDD: docs/specs/phases/FASE55-monetization.spec.yaml
Handoff: Adendo Monetização
Objetivo
Implementar o modelo de receita open-core: morador nunca paga; comerciantes assinam plano comercial; transações geram taxa com split transparente; payouts automáticos via Carteira Aratá.
Amplia: FASE15 (reposicionada como billing comercial), FASE6/7 (checkout + payout), FASE22 (moeda/carteira).
Princípios
- Morador nunca paga pelo app comunitário
- Núcleo aberto — receita por operação, não paywall
- Transparência: taxa e split antes e depois do pagamento
- Split prioriza implementador local e fundo do território
Domínios novos/estendidos
| Entidade | Descrição |
|---|---|
CommercialPlan |
Planos Loja/Pro por território |
Subscription |
Assinatura ativa do comércio |
Wallet (Aratá) |
Saldo, titular, payoutMethod |
Transaction |
grossAmount, feeAmount, splitId |
FeeSplitRule |
Percentuais versionados por revenueType |
Payout |
Repasse liquidado por período |
ConsumptionMeter |
IA, mídia, notificações medidos |
Regras de billing
- Toda transação chama
POST /transactions/{id}/quoteantes do pagamento feeAmount = grossAmount × feeRate(banker's rounding)- Vendedor recebe
grossAmount − feeAmount; fee dividido pelaFeeSplitRule - Custos PSP/PIX fora do split, linha separada
- Alteração de taxa exige nova
FeeSplitRulecomeffectiveFrom— nunca edita vigente
Endpoints principais
| Método | Rota | Status FASE55 |
|---|---|---|
GET |
/api/v1/territories/{id}/plans |
✅ v0 — planos comerciais (MarketplaceAdvanced) |
POST |
/api/v1/subscriptions |
✅ existente — assinatura por usuário (mock Stripe sem secret) |
POST |
/api/v1/transactions/{id}/quote |
✅ v0 — checkout.Id como transactionId |
GET |
/api/v1/transactions/{id}/receipt |
✅ v0 — requer CheckoutStatus.Paid |
POST |
/api/v1/transactions/{id}/refund |
✅ v0 — estorno idempotente; reverte fee/split (AC-55-6) |
GET |
/api/v1/territories/{id}/payouts/consolidated |
✅ v0 — payout consolidado por período (AC-55-5) |
POST |
/merchants/{id}/subscription |
⏳ — usar /subscriptions até alias merchant |
GET |
/wallets/{id} |
⏳ FASE22 + FASE55 |
GET |
/implementers/{id}/payouts |
⏳ FASE57 |
GET |
/merchants/{id}/consumption |
⏳ |
Quote (resposta v0)
{
"transactionId": "uuid",
"grossAmount": 100.00,
"feeAmount": 10.00,
"netToSeller": 90.00,
"split": [
{ "recipient": "implementer", "amount": 4.00 },
{ "recipient": "territory_fund", "amount": 3.00 },
{ "recipient": "platform", "amount": 3.00 }
],
"feeSplitRuleId": "uuid"
}
Implementação v0 (código)
| Componente | Caminho |
|---|---|
FeeSplitRule |
backend/Arah.Domain/Financial/FeeSplitRule.cs |
| Quote / receipt | TransactionQuoteService, TransactionsController |
| Gate comercial | CommercialStoreGateService → StoreService.SetPaymentsEnabled |
| Seed split | FeeSplitRuleBootstrapHostedService (40/30/30 por território) |
| Plano LOJA (InMemory) | InMemoryDataStore — território B |
Telas (app)
- Ativar loja — seleção de plano + PIX
- Pagamento transparente — checkout com split visível
- Minha loja — bloqueada sem assinatura ativa
Critérios de aceite
- Comércio não vende sem plano ativo — gate em
SetPaymentsEnabled(v0) - Quote exibe taxa e split antes do PIX —
POST /transactions/{id}/quote(v0) - Comprovante auditável com split aplicado —
GET /transactions/{id}/receipt(v0, requer Paid) - Payout consolidado por período —
GET /territories/{id}/payouts/consolidated?from=&to=(v0, read-model sobre checkouts pagos) - Estorno reverte fee e splits proporcionalmente —
POST /transactions/{id}/refund(ledger append-only viaReversePaidCheckoutAsync; idempotente) - Morador usa feed/mapa/eventos sem cobrança — plano FREE inalterado