A wallet management API built with ASP.NET Core 10.0, PostgreSQL and Clean Architecture patterns.
- JWT Authentication: Secure Bearer token-based authentication
- Role-Based Authorization: Admin-only operations like wallet deletion
- Wallet Management: Create, retrieve, and delete wallets
- Transaction Operations: Deposit, withdraw, and transfer funds
- Global Error Handling: Consistent error responses via middleware
- Docker Support: Full containerization with PostgreSQL
- Swagger Documentation: Interactive API documentation at
/swagger - Clean Architecture: Separation of concerns across Domain, Application, Infrastructure, and API layers
- Comprehensive Testing: Unit tests for domain logic, integration tests for API endpoints
src/
SimpleWallet.Api/ # API layer (controllers, DTOs, middleware)
SimpleWallet.Application/ # Application layer (services, interfaces)
SimpleWallet.Domain/ # Domain layer (entities, exceptions, validation)
SimpleWallet.Infrastructure/ # Infrastructure layer (repositories, database)
SimpleWallet.Tests/ # Test projects
Wallet.UnitTests/ # Domain logic tests
Wallet.IntegrationTests/ # API integration tests
- .NET 10.0 SDK
- Docker & Docker Compose (for containerized setup)
- PostgreSQL 16 (if running locally without Docker)
-
Start all services:
docker compose up -d
This will start:
- PostgreSQL 16 database on
localhost:5432 - API on
http://localhost:8080
- PostgreSQL 16 database on
-
Access Swagger UI:
http://localhost:8080/swagger -
Stop services:
docker compose down
-
Start only the database:
docker compose up -d db
-
Run the API:
cd src/SimpleWallet.Api dotnet runThe API will be available at
https://localhost:5071 -
Access Swagger UI:
https://localhost:5071/swagger
The database schema is automatically created on startup via EF Core's EnsureCreated() method.
The following data is automatically seeded on first run:
Users:
- User 1: ID
00000000-0000-0000-0000-000000000001 - User 2: ID
00000000-0000-0000-0000-000000000002
Wallets:
- Wallet 1: Balance = 1000, Owner = User 1
- Wallet 2: Balance = 500, Owner = User 2
Transactions:
- Sample transactions showing deposits and transfers between the wallets
The API includes two pre-configured users for development/testing:
Admin User:
- Username:
admin - Password:
admin123 - Role: Admin (can delete wallets)
Regular User:
- Username:
user - Password:
user123 - Role: User
curl -X POST http://localhost:8080/api/Authentication/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Include the token in the Authorization header:
Authorization: Bearer {token}
POST /api/Authentication/login- Obtain JWT token
GET /api/Wallet/{id}- Get wallet by IDGET /api/Wallet/user/{userId}- Get all wallets for a userPOST /api/Wallet- Create a new walletDELETE /api/Wallet/{id}- Delete wallet (Admin only) [Requires JWT]
PATCH /api/User/{id}- Update user name and/or email (Admin only) [Requires JWT]
POST /api/Transaction/deposit- Deposit funds to a walletPOST /api/Transaction/withdraw- Withdraw funds from a walletPOST /api/Transaction/transfer- Transfer funds between wallets
dotnet testdotnet test src/SimpleWallet.Tests/Wallet.UnitTests/Wallet.UnitTests.csproj# Requires API running on http://localhost:8080
dotnet test src/SimpleWallet.Tests/Wallet.IntegrationTests/Wallet.IntegrationTests.csprojUnit Tests (3):
- Wallet domain logic: insufficient funds validation, deposits, transfers
Integration Tests (3):
- Authentication flow with token validation
- Authorization enforcement on protected endpoints
- API endpoint functionality
Key settings in appsettings.json:
{
"ConnectionStrings": {
"PostgresConnection": "Host=localhost;Port=5432;Database=simplewallet;Username=postgres;Password=postgres"
},
"Jwt": {
"Issuer": "SimpleWalletAPI",
"Audience": "SimpleWalletClient",
"Key": "your-secret-key-must-be-at-least-32-characters-long!!",
"ExpirationMinutes": 60
}
}A pre-configured Postman collection is available: wallet.postman_collection.json
To import:
- Open Postman
- Click "Import" → "File"
- Select
wallet.postman_collection.json - Set the
baseUrlvariable tohttp://localhost:8080 - Use the "Login" endpoint to get a token and populate the
tokenvariable
The API returns consistent error responses:
400 Bad Request- Validation errors, invalid input, insufficient funds401 Unauthorized- Missing or invalid JWT token403 Forbidden- Insufficient permissions (e.g., non-admin trying to delete)404 Not Found- Resource does not exist500 Internal Server Error- Unexpected server error
All errors include a JSON response with status, title, and detail fields.
- Domain Layer: Business entities, validation, and domain-specific exceptions
- Application Layer: Services that orchestrate business logic
- Infrastructure Layer: Data access repositories and database context
- API Layer: Controllers, DTOs, middleware, and HTTP concerns
- Exception handling (catches and maps domain exceptions to HTTP status codes)
- Database initialization and seeding
- Authentication & Authorization
- Controller routing
dotnet publish -c Release -o ./publish