Skip to content

Latest commit

 

History

105 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Carteira Financeira 💳

Status PHP Laravel PostgreSQL Redis Docker Tests License

API REST para gerenciamento seguro de carteiras em BRL, com depósitos, transferências, reversões administrativas, ledger imutável, idempotência e proteção contra concorrência.

🛠 Tecnologias Utilizadas

Tecnologia Uso no projeto
PHP 8.4 Runtime usado nas imagens da aplicação e do worker
Laravel 13 Framework HTTP, validação, Eloquent, eventos e comandos de console
Laravel Sanctum Autenticação por Bearer Token e abilities por operação
PostgreSQL 17 Persistência transacional, constraints, locks e triggers do ledger
Redis 7.4 Cache, sessões e fila
Nginx Servidor HTTP e encaminhamento para PHP-FPM
Docker Compose Orquestração de aplicação, Nginx, PostgreSQL, Redis e worker
Scribe Documentação HTML, OpenAPI e collection Postman
PHPUnit Testes unitários, Feature, integração e concorrência real
Laravel Pint Padronização de código PHP

✨ Funcionalidades

  • Cadastro com criação atômica de usuário e carteira.
  • Login e logout com Laravel Sanctum.
  • Tokens com abilities diferentes para usuários e administradores.
  • Consulta de saldo e histórico paginado da própria carteira.
  • Depósitos em BRL com registro contábil.
  • Transferências atômicas entre carteiras.
  • Bloqueio de saldo insuficiente e de transferência para si mesmo.
  • Reversões operacionais exclusivas para administradores.
  • Reversões capazes de produzir saldo negativo quando o valor original já foi gasto.
  • Idempotência para depósito, transferência e reversão.
  • Ledger imutável protegido também por triggers no PostgreSQL.
  • Locks pessimistas e ordem determinística de aquisição para concorrência.
  • Promoção e remoção operacional do papel de administrador.
  • Reconciliação de saldos em modo somente leitura ou correção explícita.
  • Eventos de domínio, logs estruturados e correlação por X-Request-ID.
  • Health check de aplicação, PostgreSQL e Redis.
  • OpenAPI e collection Postman versionados e reproduzíveis.
  • Factories financeiras e cenário local de demonstração idempotente.

🚀 Melhorias e Diferenciais

  • Valores financeiros representados como inteiros, evitando erros de ponto flutuante.
  • Regras críticas protegidas em mais de uma camada: aplicação, transação e banco.
  • Ledger append-only, sem atualização ou exclusão de lançamentos.
  • Respostas idempotentes preservadas para replay fiel da primeira operação.
  • Eventos financeiros publicados somente após o commit da transação.
  • Testes de concorrência com processos e conexões PostgreSQL independentes.
  • Policies combinadas com abilities do token para operações administrativas.
  • Artefatos de contrato gerados a partir de rotas, Requests, Resources e anotações.
  • Seed demonstrativo construído pelas Actions reais, sem um caminho financeiro paralelo.

🧱 Arquitetura

O projeto separa transporte HTTP, validação, dados de entrada e regras de aplicação:

Request HTTP
    │
    ├── Middleware: autenticação, abilities e X-Request-ID
    │
    └── Controller invocável
            │
            ├── Form Request → DTO
            │
            └── Action de aplicação
                    │
                    ├── transação + locks no PostgreSQL
                    ├── Models + ledger + idempotência
                    └── evento pós-commit → listener → log estruturado

Principais diretórios:

app/
├── Actions/             # Casos de uso de autenticação, saúde e carteira
├── Console/Commands/    # Gestão de papéis e reconciliação
├── DTOs/                # Dados validados que entram nas Actions
├── Enums/               # Tipos, estados, papéis e lançamentos
├── Events/              # Eventos financeiros pós-commit
├── Http/                # Controllers, Requests, Resources e middleware
├── Listeners/           # Auditoria estruturada dos eventos
├── Models/              # Entidades Eloquent e relacionamentos
├── Policies/            # Autorização administrativa
└── Support/             # Idempotência e correlação HTTP
database/
├── factories/           # Factories válidas para os quatro models centrais
├── migrations/          # Schema, constraints e triggers
└── seeders/              # Cenário financeiro local reproduzível
docs/api/                 # OpenAPI e collection Postman versionados
tests/                    # Testes Unit, Feature, integração e concorrência

Modelagem do banco

Schema da Carteira Financeira

O arquivo-fonte do diagrama está em schema.dbml.

🧭 Rotas da API

Método Rota Acesso Descrição
GET /api/health Público Saúde da aplicação e dependências
POST /api/v1/auth/register Público Cadastra usuário e carteira
POST /api/v1/auth/login Público Emite token Sanctum
POST /api/v1/auth/logout Autenticado Revoga o token atual
GET /api/v1/auth/me wallet:read Retorna perfil e carteira
GET /api/v1/wallet wallet:read Consulta saldo da própria carteira
GET /api/v1/wallet/transactions wallet:read Lista o histórico paginado
POST /api/v1/deposits wallet:transact Realiza depósito idempotente
POST /api/v1/transfers wallet:transact Realiza transferência idempotente
POST /api/v1/transactions/{transaction}/reversals Admin + wallet:reverse Reverte depósito ou transferência

A autenticação usa Authorization: Bearer {TOKEN}. Depósito, transferência e reversão também exigem um header Idempotency-Key não vazio, com no máximo 255 caracteres.

🚀 Pré-requisitos

Fluxo recomendado

  • Docker Engine com Docker Compose v2.
  • GNU Make, opcional, para os atalhos operacionais.

Execução sem Docker

  • PHP 8.3 ou superior.
  • Composer 2.
  • PostgreSQL.
  • Redis.
  • Extensões PHP: bcmath, intl, pcntl, pdo_pgsql, redis e zip.

▶️ Como Rodar com Docker

Na raiz do repositório, execute:

make setup

O comando:

  1. cria .env a partir de .env.example, quando necessário;
  2. constrói e inicia os cinco serviços;
  3. instala dependências PHP;
  4. gera APP_KEY somente quando ainda estiver vazia;
  5. executa migrations;
  6. carrega os dados de demonstração.

Sem Make:

cp .env.example .env
docker compose up -d --build
docker compose exec app composer install --no-interaction
docker compose exec app php artisan key:generate
docker compose exec app php artisan migrate --seed

A API estará disponível em:

http://localhost:18080

Verifique o ambiente:

make status
curl http://localhost:18080/api/health

Para interromper os containers sem remover os volumes:

make down

💻 Como Rodar sem Docker

  1. Instale as dependências:
composer install
  1. Crie e ajuste o ambiente:
cp .env.example .env
php artisan key:generate

Para serviços executados no host, ajuste pelo menos:

DB_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
  1. Crie os bancos wallet e wallet_test no PostgreSQL e execute:
php artisan migrate --seed
  1. Inicie a API e o worker em terminais separados:
php artisan serve --host=127.0.0.1 --port=18080
php artisan queue:work --sleep=3 --tries=3 --timeout=90

O phpunit.xml usa o hostname postgres, apropriado ao Docker. Para executar os testes inteiramente no host, forneça uma configuração de teste equivalente apontando para 127.0.0.1.

🌍 Variáveis de Ambiente

O arquivo .env.example contém a configuração local completa. As variáveis mais relevantes são:

Variável Padrão local Descrição
APP_NAME Carteira API Nome da aplicação e da documentação
APP_URL http://localhost:18080 URL base usada pela aplicação e pelo Scribe
APP_LOCALE pt_BR Idioma padrão das validações
APP_DEBUG true Debug local; deve ser false fora do desenvolvimento
AUTH_TOKEN_NAME api-token Nome dos tokens Sanctum
AUTH_TOKEN_EXPIRATION_MINUTES 60 Validade dos tokens em minutos
DB_HOST postgres Host PostgreSQL dentro do Compose
DB_DATABASE wallet Banco principal local
DB_USERNAME wallet Usuário local do PostgreSQL
DB_PASSWORD wallet_local_password Senha somente para ambiente local
REDIS_HOST redis Host Redis dentro do Compose
CACHE_STORE redis Backend de cache
QUEUE_CONNECTION redis Backend da fila
APP_PORT 18080 Porta HTTP publicada pelo Nginx
APP_UID / APP_GID 1000 IDs usados pelo usuário não-root da imagem

Nunca versione um .env real ou reutilize as credenciais locais em produção.

🌱 Dados de Demonstração

Carregue o cenário separadamente com:

make seed

Todas as contas abaixo usam a senha local password:

Papel E-mail Saldo final
Administrador admin@carteira.local R$ 0,00
Usuário ana@carteira.local R$ 750,00
Usuário bruno@carteira.local R$ 750,00

O cenário cria um depósito de R$ 1.000,00 para Ana, um depósito de R$ 500,00 para Bruno e uma transferência de R$ 250,00 de Ana para Bruno. O seeder pode ser executado novamente sem duplicar recursos ou saldos.

🔌 Exemplos de Uso da API

Defina a URL base:

BASE_URL=http://localhost:18080

Saúde

curl --request GET "$BASE_URL/api/health" \
  --header 'Accept: application/json'

Cadastro

curl --request POST "$BASE_URL/api/v1/auth/register" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Maria Silva",
    "email": "maria@example.com",
    "password": "StrongPassword123!",
    "password_confirmation": "StrongPassword123!"
  }'

Login

curl --request POST "$BASE_URL/api/v1/auth/login" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
    "email": "ana@carteira.local",
    "password": "password"
  }'

Copie data.access_token da resposta:

TOKEN='cole-o-token-aqui'

Consultar carteira

curl --request GET "$BASE_URL/api/v1/wallet" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN"

Depositar R$ 150,00

curl --request POST "$BASE_URL/api/v1/deposits" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Idempotency-Key: deposit-example-001' \
  --data '{
    "amount": 15000,
    "currency": "BRL"
  }'

Transferir R$ 50,00

curl --request POST "$BASE_URL/api/v1/transfers" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Idempotency-Key: transfer-example-001' \
  --data '{
    "destination_user_id": 3,
    "amount": 5000,
    "currency": "BRL"
  }'

Consultar histórico

curl --request GET "$BASE_URL/api/v1/wallet/transactions?page=1&per_page=15" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN"

Reverter uma transação

Faça login como admin@carteira.local, copie o token para ADMIN_TOKEN e informe o ULID da transação:

ADMIN_TOKEN='cole-o-token-administrativo-aqui'
TRANSACTION_ID='01K0M8W4Z8H7Y2K5Q9R3C6D1EF'

curl --request POST \
  "$BASE_URL/api/v1/transactions/$TRANSACTION_ID/reversals" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header "Authorization: Bearer $ADMIN_TOKEN" \
  --header 'Idempotency-Key: reversal-example-001' \
  --data '{
    "reason": "Solicitação confirmada pelo atendimento."
  }'

Logout

curl --request POST "$BASE_URL/api/v1/auth/logout" \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $TOKEN"

💰 Decisões Financeiras

Valores em centavos

Todos os valores monetários em centavos são inteiros. 15000 representa R$ 150,00. A API aceita apenas BRL e não recebe números decimais ou strings numéricas para amount.

Ledger

Cada alteração de saldo produz lançamento de crédito ou débito com balance_before e balance_after. Constraints verificam a transição matemática, e triggers impedem UPDATE, DELETE e TRUNCATE no ledger.

Idempotência

Depósitos, transferências e reversões exigem Idempotency-Key. Repetir a mesma chave e o mesmo payload devolve a resposta original sem duplicar dinheiro. Reutilizar a chave com outro payload retorna conflito HTTP 409.

Locks e concorrência

As Actions usam transações PostgreSQL e lockForUpdate. Transferências bloqueiam as carteiras em ordem determinística, reduzindo risco de deadlock e impedindo gasto duplo do mesmo saldo.

Reversões e saldo negativo

Uma reversão cria uma nova transação e lançamentos opostos; o histórico original não é apagado. Se o destinatário já gastou o valor recebido, a reversão ainda preserva a verdade contábil e pode deixar sua carteira negativa.

Autorização

Usuários comuns recebem abilities de leitura e transação. Apenas administradores recebem wallet:reverse, e a Policy também confirma o papel persistido no banco.

Não existe endpoint público para elevar papéis. A gestão é operacional:

docker compose exec app php artisan user:promote-admin usuario@example.com
docker compose exec app php artisan user:demote-admin usuario@example.com

O rebaixamento revoga todos os tokens do usuário. Após uma promoção, faça novo login para emitir um token com as abilities administrativas.

🧰 Operação e Manutenção

Comandos Make

Comando Descrição
make help Lista os atalhos disponíveis
make setup Prepara ambiente, containers, dependências, migrations e seed
make up Inicia os serviços
make down Interrompe os serviços sem apagar volumes
make migrate Executa migrations pendentes
make seed Carrega ou atualiza o cenário demonstrativo
make test Executa a suíte completa
make lint Verifica o padrão Laravel Pint
make format Formata o código com Pint
make docs Regenera OpenAPI e Postman versionados
make reconcile Compara os saldos com o ledger
make logs Acompanha logs do Compose
make shell Abre um shell no container da aplicação
make status Exibe o estado dos serviços

Reconciliação

O modo padrão é somente leitura e retorna falha quando encontra divergências:

make reconcile

A correção precisa ser explícita:

docker compose exec app php artisan wallet:reconcile --fix

O modo --fix bloqueia a carteira, recalcula o saldo pelo ledger, incrementa a versão e registra a correção em log.

📚 Documentação da API

Com os serviços ativos:

  • HTML interativo: http://localhost:18080/docs;
  • OpenAPI servido: http://localhost:18080/docs.openapi;
  • Postman servido: http://localhost:18080/docs.postman.

Artefatos versionados:

Regere os dois arquivos depois de qualquer alteração no contrato HTTP:

composer docs:generate

No fluxo Docker, use make docs. O exporter normaliza variáveis, autenticação e identificadores da collection para que a geração seja determinística.

🧪 Testes e Qualidade

Suíte completa

make test

Verificar formatação

make lint

Aplicar formatação

make format

Teste focal

docker compose exec app php artisan test \
  tests/Feature/Api/V1/Transfer/CreateTransferTest.php

A suíte cobre autenticação, autorização, saldo, idempotência, rollback, constraints, eventos, reconciliação e concorrência real. Os testes usam wallet_test, criado pelo container PostgreSQL.

⚠️ Limitações Conhecidas

  • Apenas BRL é suportado.
  • Não existe integração com banco, PIX, cartão ou outro provedor externo.
  • A API não oferece refresh token; tokens expiram conforme configuração.
  • Não há interface web para operação financeira.
  • A gestão de administradores é feita somente por console.
  • O ledger é interno; não há exportação de extrato contábil.
  • O ambiente local possui credenciais deliberadamente simples e não serve como configuração de produção.
  • PHPStan/Larastan permanece como evolução futura opcional de qualidade.

🧭 Evoluções Futuras

  • Adicionar PHPStan/Larastan e integrar ao fluxo de qualidade.
  • Criar pipeline de integração contínua.
  • Publicar métricas operacionais e tracing distribuído.
  • Adicionar filtros por tipo e intervalo de datas ao histórico.
  • Implementar notificações de operações financeiras.
  • Integrar um provedor externo de pagamentos em ambiente isolado.
  • Planejar suporte a múltiplas moedas com regras explícitas de câmbio.

📄 Licença e Autoria

O projeto está configurado sob a licença MIT no composer.json.

A autoria e a evolução técnica são preservadas no histórico Git do repositório.

About

API REST para gerenciamento seguro de carteiras em BRL, com depósitos, transferências, reversões administrativas, ledger imutável, idempotência e proteção contra concorrência

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages