Desktop ATM simulator built with Java Swing — supports withdrawals, deposits, transfers between accounts, transaction history, and PIN changes. Data is persisted in SQLite.
Add screenshots here once you have them. Drop image files into a
screenshots/folder and reference them like: 
- Language: Java 26
- UI: Swing + FlatLaf 3.6 (IntelliJ theme)
- Build: Gradle 9.5 (Kotlin DSL, wrapper included)
- Database: SQLite 3.49 via JDBI 3 3.47 (lightweight SQL mapper)
- Persistence: JDBI
BeanMapper— no ORM, no bytecode enhancement
- JDK >= 26 (verify with
java -version) - Git (optional, for cloning)
# 1. Clone or navigate to the project
cd Cajero_Real
# 2. Build and run (Gradle wrapper auto-downloads Gradle)
./gradlew runOn Wayland (e.g., niri, Hyprland, Sway), the window may appear blank. Run with:
_JAVA_AWT_WM_NONREPARENTING=1 ./gradlew runFrom IntelliJ: open the Cajero_Real folder, then run CajeroApp.main() or the Gradle application > run task.
For IntelliJ on Wayland, add _JAVA_AWT_WM_NONREPARENTING=1 to the Run Configuration's environment variables.
| Card Number | PIN | Holder | Balance |
|---|---|---|---|
| 4821 | 1234 | Carlos Rodriguez | $15,000.00 |
| 9734 | 5678 | Maria Garcia | $25,000.00 |
Both accounts are created automatically on first run. Delete cajero_real.db to reset.
Login → Menu → Withdraw / Deposit / Transfer / History / Change PIN
| Screen | Description |
|---|---|
| Login | Teal screen, enter 4-digit PIN. 3 attempts before the card is locked. |
| Menu | Welcome message with balance. Six action buttons. |
| Withdraw | Preset amounts ($100, $200, $500, $1000) or custom. Confirmation dialog before processing. |
| Deposit | Enter amount, updates balance immediately. |
| Transfer | Destination account (16-digit number) and amount. Validates balance. |
| History | Scrollable log of all operations for the current session. |
| Change PIN | Enter current PIN, new PIN, confirm. Validates length, digits, and mismatch. |
Payment cards are hardcoded as "4821" — the login screen does not ask for a card number. This is a simplified demo.
com.cajero
├── CajeroApp.java Entry point — FlatLaf setup, DB init, launches UI
├── config/
│ └── DatabaseManager.java JDBI + SQLite connection, table creation, seed data
├── model/
│ ├── Cuenta.java Account POJO
│ ├── Transaccion.java Transaction POJO
│ └── TipoTransaccion.java Enum: WITHDRAW, DEPOSIT, TRANSFER
├── service/
│ ├── AuthService.java PIN validation, attempt counter
│ ├── CuentaService.java Account CRUD (JDBI queries)
│ └── TransaccionService.java Transaction registry + history
├── controller/
│ ├── LoginController.java
│ ├── MenuController.java
│ ├── RetiroController.java
│ ├── DepositoController.java
│ ├── TransferenciaController.java
│ ├── HistorialController.java
│ └── CambioPinController.java
└── ui/
├── MainFrame.java JFrame + CardLayout (7 screens)
├── component/
│ └── PanelAnimator.java Fade utility (currently instant)
└── panel/
├── LoginPanel.java
├── MenuPanel.java
├── RetiroPanel.java
├── DepositoPanel.java
├── TransferenciaPanel.java
├── HistorialPanel.java
└── CambioPinPanel.java
| Layer | Responsibility |
|---|---|
| Model | Plain Java objects (POJOs) with getters/setters |
| Service | Business logic + database access via JDBI |
| Controller | Mediates between panels and services |
| UI | Swing panels with null layout (absolute positioning) |
- Login limit: 3 failed PIN attempts → card blocked → app exits after 3 seconds.
- Withdraw: amount must be positive, must not exceed available balance.
- Transfer: destination must be exactly 16 numeric digits, amount must be positive and not exceed balance.
- Deposit: only validates positivity.
- PIN change: requires current PIN, then new PIN must be 4 digits, numeric, and different from the current one. Both entries must match.
- Persistence: every operation writes to SQLite immediately. Balance updates and transactions are written in the same service call.
CREATE TABLE cuentas (
id INTEGER PRIMARY KEY AUTOINCREMENT,
titular TEXT NOT NULL,
tarjeta TEXT NOT NULL UNIQUE,
pin_hash TEXT NOT NULL,
saldo REAL NOT NULL DEFAULT 0.0
);
CREATE TABLE transacciones (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tipo TEXT NOT NULL, -- RETIRO, DEPOSITO, TRANSFERENCIA
monto REAL NOT NULL,
fecha TEXT NOT NULL, -- ISO-8601
cuenta_id INTEGER NOT NULL,
cuenta_destino TEXT,
saldo_posterior REAL NOT NULL
);PINs are stored as Integer.toHexString(pin.hashCode()) — simple hashing, not cryptographic. Adequate for a school project.
./gradlew build # Assemble JAR
./gradlew compileJava # Compile only
./gradlew run # Build + run
./gradlew clean # Clean build artifacts (keeps DB)| Problem | Solution |
|---|---|
| Blank/white window on Linux | Your WM is Wayland. See "Setup & Running" for the environment variable fix. |
| DB corruption or wants fresh data | Delete cajero_real.db and re-run — seed data is inserted automatically. |
| Port conflicts | None — this is a desktop app, no server ports. |
| Slow UI or stutter | Run with _JAVA_AWT_WM_NONREPARENTING=1. The alpha compositing animation was removed for Wayland compatibility. |
All core features are implemented and compile cleanly. The UI uses absolute positioning (null layout) — functional but does not resize. Contributions welcome for:
- Layout migration to
GridBagLayoutorMigLayout - Adding a card number input to the login screen
- Withdrawal receipt simulation
- Dark theme toggle
- i18n (Spanish/English)