Skip to content

Latest commit

 

History

59 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Banking API

A RESTful banking API built with Spring Boot and Java 21, simulating the core operations of a digital bank: customer onboarding, account management, deposits, withdrawals, transfers and JWT-based authentication.

Table of Contents

Overview

Banking API exposes a set of REST resources that let a client register, authenticate, open bank accounts and move money between them, while keeping every sensitive rule — balance checks, account status, ownership — enforced consistently across the codebase.

Two roles are supported: CUSTOMER, who can only manage their own data and accounts, and ADMIN, who has full visibility across the system.

Features

  • Customer registration, lookup, update and removal
  • Account creation, balance lookup and blocking
  • Deposits, withdrawals and transfers between accounts
  • Paginated transaction history per account
  • JWT login and self-service registration
  • Role-based and ownership-based authorization on every protected endpoint

Tech Stack

  • Java 21
  • Spring Boot 4.1 — Web, Data JPA, Security, Validation, Cache
  • PostgreSQL for persistence
  • Flyway for schema versioning
  • JJWT for token generation and validation
  • Lombok
  • JUnit 5, Mockito and AssertJ for testing
  • Docker Compose for the local database
  • Maven

Architecture

The codebase is organized by business domain rather than by technical layer — each feature (accounts, customers, transactions, authentication) owns its entity, repository, service, controller and DTOs, instead of being scattered across generic controller/service/repository packages.

Business rules live inside the domain entities themselves (Account, Customer) rather than in the services: an account knows how to validate its own deposit, withdrawal and status transitions, which keeps that logic unit-testable in complete isolation from Spring and the database.

Ownership checks are centralized in a single CurrentCustomerProvider, so the "is this the resource owner, or an admin?" rule is written once and reused everywhere, instead of being duplicated across every service method.

Security

  • Stateless authentication via JWT, verified by a custom filter placed before Spring Security's default authentication filter
  • Passwords hashed with BCrypt
  • /api/auth/** is public; every other endpoint requires a valid token
  • Two layers of authorization: role-based (@PreAuthorize("hasRole('ADMIN')")) for admin-only actions, and ownership-based for everything a customer does with their own data
  • Custom 401/403 handlers returning a consistent JSON error body instead of the default servlet error page

API Endpoints

Authentication

POST   /api/auth/login
POST   /api/auth/register

Customers

# ADMIN only
POST   /api/customers
GET    /api/customers

# CUSTOMER only
GET    /api/customers/{id}
PUT    /api/customers/{id}
DELETE /api/customers/{id}

Accounts

POST   /api/v1/accounts
GET    /api/v1/accounts/{id}
POST   /api/v1/accounts/{id}/deposit
POST   /api/v1/accounts/{id}/withdraw
POST   /api/v1/accounts/{id}/block

Transactions

POST   /api/accounts/{accountId}/transactions/deposit
POST   /api/accounts/{accountId}/transactions/withdraw
POST   /api/accounts/{accountId}/transactions/transfer
GET    /api/accounts/{accountId}/transactions

Error Handling

A global @RestControllerAdvice converts every domain exception into a standardized ProblemDetail (RFC 7807) response:

Exception HTTP Status
AccountNotFoundException 404 Not Found
ResourceNotFoundException 404 Not Found
InsufficientBalanceException 422 Unprocessable Entity
AccountBlockedException 403 Forbidden
AccessDeniedException 403 Forbidden
AuthenticationException 401 Unauthorized
IllegalArgumentException 400 Bad Request
MethodArgumentNotValidException 400 Bad Request
Any other exception 500 Internal Server Error

Getting Started

Requirements: Java 21, Maven 3.9+, Docker.

  1. Start the database:

    docker compose up -d
  2. Optionally override the local defaults with environment variables:

    Variable Default (dev)
    JWT_SECRET local-dev-only-insecure-default-secret-please-override
    JWT_EXPIRATION 3600000 (1 hour, in ms)
    DB_URL jdbc:postgresql://localhost:5433/banking
    DB_USERNAME banking
    DB_PASSWORD banking

    JWT_SECRET should always be overridden with a strong, private value outside local development.

  3. Run the application:

    ./mvnw spring-boot:run

    Flyway migrations run automatically on startup.

  4. The API is available at http://localhost:8080.

Running Tests

./mvnw test

The suite covers domain rules on Account and Customer, the service layer (including ownership and permission scenarios), JWT generation/validation, and the authentication flow, using JUnit 5, Mockito and AssertJ.

About

A Spring Boot banking API with accounts, transfers and JWT authentication

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages