Status: Approved · v2 · SPEC-DOMAIN-WALLET-001
depends_on: SPEC-DOMAIN-TEAM-001, SPEC-DOMAIN-TORCIDA-001
used_by: IS-MVP-09.1, IS-MVP-09.3, IS-MVP-09.4
Carteiras e créditos
Saldo interno não sacável de times e torcidas, formado por lotes rastreáveis e usado em ações da plataforma.
Objetivo e limites
Esta especificação define o comportamento canônico do domínio Carteiras e créditos no RaizFC. Ela deve ser consultada antes de alterar contratos, mocks, endpoints, telas, permissões ou Increment Specifications relacionados.
Incluído no MVP
- Wallet para time e torcida.
- Créditos equivalentes a centavos de uso interno.
- Lotes com origem e expiração de 90 dias.
- Apoio 95/5.
- Patrocínio 80/20.
- Transferência sem taxa time↔torcida aprovada.
- Consumo FIFO.
Fora do MVP
- Saque.
- Cashback.
- Rendimento.
- Criptomoeda.
- Carteira de campeonato/campo/serviço.
Atores e responsabilidades
| Ator | Responsabilidade |
|---|---|
| Apoiador | Realiza contribuição anônima ou identificada. |
| Gestor financeiro | Consulta saldo/extrato, transfere e usa créditos. |
| Responsável principal | Possui acesso total financeiro. |
| Sistema | Credita, debita, expira e aplica FIFO. |
| Operação | Audita e corrige apenas por fluxo autorizado. |
Conceitos canônicos
Wallet
Conta interna de créditos, não bancária.
CreditLot
Lote com saldo remanescente, origem e expiresAt.
LedgerEntry
Movimentação imutável.
Available Balance
Soma de lotes válidos menos reservas/consumos.
Reserved Balance
Crédito temporariamente comprometido por operação.
Invariantes
- Saldo disponível nunca é negativo.
- Toda alteração de saldo produz ledger entry e referência de origem.
- Créditos expirados não podem ser usados.
- Consumo usa FIFO por expiração.
- Lotes são imutáveis em origem e valor inicial.
- Wallet pertence apenas a team ou torcida no MVP.
- Transferência só ocorre entre torcida approved e seu time.
- Crédito não é sacável nem transferível a usuário.
- Operações financeiras críticas exigem Idempotency-Key.
Estados
| Estado | Significado | Visibilidade/efeito |
|---|---|---|
| active | Carteira operacional | Recebe e usa créditos. |
| restricted | Uso limitado | Pode receber, mas não gastar/transferir conforme motivo. |
| closed | Entidade encerrada | Sem novas operações; histórico preservado. |
Transições permitidas
| Origem | Ação | Destino | Autoridade | Efeitos |
|---|---|---|---|---|
| — | create_with_entity | active | Sistema | Saldo zero. |
| active | restrict | restricted | Operação/status da entidade | Bloqueia saídas. |
| restricted | restore | active | Operação | Libera. |
| active/restricted | close | closed | Sistema/operação | Preserva extrato. |
Permissões
| Permission key | Quem recebe por padrão | Ação protegida | Auditoria |
|---|---|---|---|
| wallet.view | Financial/Admin/Owner | Saldo e extrato | Sim |
| wallet.transfer | Financial/Admin/Owner | Transferir time↔torcida | Sim |
| wallet.spend | Financial/Admin/Owner | Ações pagas | Sim |
| wallet.export | Financial/Admin/Owner | Exportar extrato | Sim |
Fluxos funcionais
Crédito por apoio
- Pagamento confirmado dispara efeito idempotente.
- Sistema calcula 95% para entidade e 5% plataforma.
- Cria lote de créditos com expiresAt +90 dias.
- Cria ledger entry credit/support.
- Atualiza saldo derivado e notifica gestores.
Crédito por patrocínio
- Pagamento confirmado calcula 80/20.
- Lote referencia sponsorshipId.
- Patrocínio ativa em transação lógica consistente.
Consumo
- Gestor escolhe ação e visualiza preço fixo.
- Backend valida permissão, status e saldo.
- Reserva/consome lotes FIFO.
- Cria entries por lote consumido e referência da ação.
- Falha posterior deve liberar reserva ou compensar de forma auditada.
Transferência
- Gestor escolhe torcida/time vinculado e valor.
- Valida approved link e saldo.
- Consome FIFO na origem.
- Cria lote na destinatária com política de expiração preservada ou reiniciada conforme decisão canônica: preservar menor expiração remanescente.
- Cria entries espelhadas sem taxa.
Expiração
- Job encontra saldo remanescente em lotes vencidos.
- Cria debit/expiration.
- Atualiza saldo e envia resumo aos gestores.
Casos-limite e erros
| Cenário | Comportamento esperado | Código/estado |
|---|---|---|
| Saldo insuficiente | Não alterar lotes | INSUFFICIENT_CREDITS |
| Idempotency repetida | Retornar operação original | IDEMPOTENCY_REPLAY |
| Wallet restrita | Bloquear saída | WALLET_RESTRICTED |
| Transferência inválida | Rejeitar | INVALID_WALLET_TRANSFER |
| Lote expira durante reserva | Usar regra transacional e impedir saldo inconsistente | CREDIT_LOT_EXPIRED |
| Valor não positivo | Rejeitar | INVALID_MONEY_AMOUNT |
Privacidade e exposição pública
- Saldo, lotes e extrato são privados.
- Página pública pode exibir apoiadores sem valores.
- A origem agregada pode ser usada em métricas sem identificar doadores anônimos.
- Logs não registram payload Pix completo.
Notificações e auditoria
- Crédito recebido.
- Transferência enviada/recebida.
- Saldo insuficiente para ação.
- Expiração próxima/realizada.
- Restrição/restauração.
- Toda movimentação possui auditoria e requestId.
API relacionada
| Método | Rota | Acesso | Finalidade |
|---|---|---|---|
| GET | /api/v1/wallets/:ownerType/:ownerId | wallet.view | Resumo |
| GET | /api/v1/wallets/:ownerType/:ownerId/ledger | wallet.view | Extrato |
| POST | /api/v1/wallets/:ownerType/:ownerId/transfers | wallet.transfer | Transferir |
| POST | /api/v1/wallets/:ownerType/:ownerId/spend | wallet.spend | Consumir |
| GET | /api/v1/wallets/:ownerType/:ownerId/credit-lots | wallet.view | Lotes |
Contratos compartilhados
- WalletEntity
- WalletSummaryDto
- CreditLotDto
- LedgerEntryDto
- WalletEntryType
- CreateWalletTransferRequest
- SpendCreditsRequest
- MoneyAmount
Requisitos de UX
- Carteira mostra saldo disponível, créditos a expirar, entradas/saídas e ações.
- Nunca usar símbolo que sugira saque ou investimento.
- Confirmação de gasto mostra custo, saldo antes/depois e finalidade.
- Extrato tem filtros por tipo e período.
- Apoiadores públicos ficam fora da tela privada de extrato.
Comportamento dos mocks
- Wallets com saldo zero, suficiente, insuficiente, lotes próximos de expirar e restricted.
- Consumo realmente altera lotes in-memory.
- Replay idempotente retorna mesma resposta.
- Job de expiração controlável.
Critérios de aceite
- Saldo derivado não fica negativo.
- Toda operação possui ledger.
- FIFO aplicado.
- Expiração em 90 dias.
- Transferência limitada ao vínculo time-torcida.
- Valores em centavos.
Testes obrigatórios
- Splits 95/5 e 80/20.
- FIFO.
- Expiração.
- Idempotência.
- Restrição.
- Transferência e expiração preservada.
- Concorrência de consumo.
Pós-MVP
- Previsão de caixa.
- Orçamentos por categoria.
- Mais tipos de entidade, se comprovado.
- Integrações contábeis.
Decisões registradas
- Créditos não são sacáveis.
- 1 crédito corresponde a R$1 de capacidade interna, armazenado em centavos.
- Somente time e torcida possuem wallet.
- Transferência sem taxa.
Machine summary
spec: SPEC-DOMAIN-WALLET-001
domain: Carteiras e créditos
must_preserve:
- Saldo disponível nunca é negativo.
- Toda alteração de saldo produz ledger entry e referência de origem.
- Créditos expirados não podem ser usados.
- Consumo usa FIFO por expiração.
- Lotes são imutáveis em origem e valor inicial.
- Wallet pertence apenas a team ou torcida no MVP.
- Transferência só ocorre entre torcida approved e seu time.
- Crédito não é sacável nem transferível a usuário.
touches:
- contracts
- mocks
- api
- frontend
- backend
- tests
- documentation