Skip to content

🏗️ ATM / Banking System — High-Level Design

Target Level: Senior/Staff Engineer | Focus: Financial transactions, security, state machines, cash management


1. SYSTEM OVERVIEW

Purpose: ATM network providing cash withdrawal, deposit, balance inquiry, and fund transfers with high security and reliability.

Scale: 10K ATMs, 1M accounts, 50K transactions/hour peak, 99.999% uptime

Users: Bank customers, ATM maintenance staff, Bank operations, Fraud investigation team

Use Cases: Cash withdrawal, Balance inquiry, PIN change, Fund transfer, Mini statement, Cash/check deposit

Constraints: <2s transaction time, PCI DSS compliance, no double-dispense, offline fallback for cash withdrawals


2. HIGH-LEVEL ARCHITECTURE

ATM Terminal (C/C++ on embedded Linux)
  - Card reader, PIN pad, Dispenser, Receipt printer
      │
      │ TCP/SSL (ISO 8583 / HTTPS)
      │
┌─────▼─────────────────────┐
│     ATM Switch / Gateway   │
│  (Kong / Custom)           │
│  - Route to core banking   │
│  - Protocol translation    │
└─────┬─────────────────────┘
      │
┌─────▼────────────────────────────────┐
│     ATM Controller Service (Go)      │
│  - State machine per ATM session     │
│  - Cash management                   │
│  - Transaction logging               │
└─────┬────────────────────────────────┘
      │
┌─────▼──────────┐    ┌───────────────┐
│ Core Banking   │    │ HSM           │
│ System         │    │ (Hardware     │
│ (Mainframe/    │    │  Security     │
│  PostgreSQL)   │    │  Module)      │
└────────────────┘    │ - PIN verify  │
                      │ - Key mgmt    │
                      └───────────────┘

Figure: request path from the ATM terminal to the core banking system and HSM.

flowchart TB
  T["ATM terminal: card reader, PIN pad, dispenser"] -->|"TCP/SSL, ISO 8583"| S["ATM switch / gateway"]
  S --> C["ATM controller (Go): session state, cash, logging"]
  C --> B[("Core banking")]
  C --> H["HSM: PIN verify, keys"]

🎬 Animated Sequence Diagram


🎬 Animated ATM Banking Sequence — Insert Card → PIN → Select → Withdraw → Cash + Receipt. Click ▶ to play/pause. Created with Remotion.


3. KEY COMPONENTS & INTERVIEW Q&A

ATM Controller (Go)

  • Session state machine: Idle → CardInserted → PinEntered → Ready → TransactionComplete
  • Cash dispenser management (denomination optimization)
  • Transaction logging (immutable audit trail)

Figure: ATM session lifecycle.

stateDiagram-v2
  [*] --> Idle
  Idle --> CardInserted: card read
  CardInserted --> PinEntered: PIN submitted
  PinEntered --> Ready: PIN verified
  PinEntered --> Idle: blocked or retained
  Ready --> TransactionComplete: transaction done
  TransactionComplete --> Idle: card ejected

🔴 Interview Question: "How do you ensure a withdrawal is never double-dispensed?"

✅ Answer: Make every step either retryable or reversible, and decide the default for each crash point. 1. Plan: the ATM checks it can make the amount from its cassettes before asking the bank. 2. Authorize: the request carries a unique id (ATM id + sequence / STAN). The issuer holds or debits the funds; a retried request with the same id returns the original response instead of debiting again. 3. Dispense: sensors confirm notes presented and taken. 4. Complete or reverse: on success the ATM confirms; on a clean failure it sends a reversal (ISO 8583 0420 advice), stored on disk and retried until acknowledged, so a reboot cannot lose it. 5. Ambiguous outcome (jam mid-dispense, power loss): neither confirm nor reverse blindly. Flag for reconciliation; cassette counters and the reject/purge bin settle it, usually the same day.

In ISO 8583 networks this is typically one financial request (0200) plus a reversal on failure rather than a separate auth/capture, but the guarantees are the same: retries are idempotent and reversals are delivered at least once.

Figure: withdrawal with idempotent request id, confirm on success, reversal on clean failure.

sequenceDiagram
  participant A as ATM
  participant I as Issuer
  A->>A: Check cassettes can make amount
  A->>I: Withdraw request (unique id)
  I-->>A: Approved (hold or debit)
  A->>A: Dispense, sensors confirm
  alt notes taken
    A->>I: Confirm
  else clean failure
    A->>I: Reversal (stored, retried)
  end

ATM Switch (Gateway)

  • ISO 8583 protocol translation
  • Route to appropriate core banking system
  • Fraud detection (velocity, amount thresholds)

Hardware Security Module (HSM)

  • PIN encryption/decryption (never plaintext outside HSM)
  • Key management and rotation
  • MAC generation for message integrity

🔴 Interview Question: "How is PIN security handled end-to-end?"

✅ Answer: 1. At PIN pad: the encrypting PIN pad builds an ISO 9564 PIN block (format 0 or 4) and encrypts it with a symmetric key, typically DUKPT (unique key per transaction) or a terminal key loaded via remote key injection. 2. In transit: each hop (ATM switch, network, issuer) translates the PIN block from one zone key to the next inside an HSM; the clear PIN never exists in host memory. Messages also carry a MAC. 3. At the issuer HSM: the PIN is verified against a PIN offset (IBM 3624) or PVV (Visa). The database stores the offset/PVV, never the PIN or a plain hash of it (10,000 possible PINs makes any hash trivially reversible). 4. Attempts: a durable counter in the card record; block after 3 and instruct the ATM to retain the card. Unblocking requires a branch or a verified channel.


4. DATA MODEL

CREATE TABLE accounts (
    id UUID PRIMARY KEY, customer_id UUID, account_type TEXT,
    balance DECIMAL(15,2), held DECIMAL(15,2) DEFAULT 0,   -- available = balance - held (+ limits)
    status TEXT
);
CREATE TABLE cards (
    card_number TEXT PRIMARY KEY, customer_id UUID,
    account_id UUID, pin_offset TEXT, status TEXT,
    failed_attempts INT DEFAULT 0
);
CREATE TABLE transactions (
    id UUID PRIMARY KEY, account_id UUID, type TEXT,
    amount DECIMAL(15,2), balance_after DECIMAL(15,2), created_at TIMESTAMPTZ,
    atm_id TEXT, status TEXT,                          -- PENDING | COMPLETED | REVERSED
    request_id TEXT UNIQUE                             -- ATM id + sequence: retries hit this
);
-- withdrawal = one transaction:
--   INSERT transaction (request_id)   -- conflict -> return stored result
--   UPDATE accounts SET held = held + :amt
--     WHERE id = :id AND balance - held - :floor >= :amt   -- 0 rows -> decline

5. TRADE-OFF ANALYSIS

Decision Choice Rationale
Protocol ISO 8583 Industry standard for ATM networks
PIN security HSM + encrypted PIN block PCI DSS compliance, never plaintext
Cash mgmt Per-ATM inventory Track denominations for optimal dispensing
Transaction Authorize → dispense → confirm/reverse, idempotent by request id Not a distributed 2PC: the ATM cannot "prepare" a dispense, so failures are compensated with reversals
Balance updates Conditional UPDATE ... WHERE available >= amt No read-modify-write; no optimistic-retry storms on hot accounts
Consistency Strong, single writer per account Balances are never served from a cache for authorization

6. FAILURE MODES & CAPACITY

Failure Behaviour
Response to ATM lost ATM retries with the same request id; issuer returns the original result
ATM reboots after dispensing, before confirming Journal on local disk records the dispense; confirmation is replayed on boot
Reversal cannot reach the bank Stored and forwarded until acknowledged; the hold stays until then
Issuer down Switch can run stand-in authorization with conservative limits and queue advices; or decline. A business risk decision
Hot account (payroll, merchant) Row lock held only for the conditional update; no cache in the write path
Cash/ledger mismatch End-of-day reconciliation per ATM: cassette counts vs completed withdrawals

Capacity: 50K transactions/hour at peak is ~14 TPS, small for a single relational primary per region; the hard requirements are latency (< 2 s end to end, dominated by network and HSM hops) and availability (99.999% means about 5 minutes of downtime per year, so active/passive failover with synchronous replication for the ledger). Ledger growth is ~1M rows/day at peak rates, about 0.5 KB each, so ~200 GB/year before indexes; partition by month and archive.


7. COST (Monthly)

Component Cost (10K ATMs)
ATM Controller per ATM $500/ATM (amortized hardware)
ATM Switch + Gateway $15,000
Core Banking DB $10,000
HSM (per region) $5,000
Total $35,000 (excluding ATM hardware)