FraudShield Guide

๐Ÿ›ก๏ธ FraudShield

Real-time, event-driven fraud detection โ€” from raw payment event to user alert in under 200 ms.

Java 21 Spring Boot 4.1 Apache Kafka Spring Security 7 JWT + Refresh Tokens Redis PostgreSQL / H2 Resilience4j Docker Compose Swagger / OpenAPI 3
Getting Started

What FraudShield Is

๐ŸŽฏ

In one line: FraudShield watches every payment as it happens, decides within milliseconds whether it looks fraudulent, and instantly warns the user if something is wrong โ€” exactly like the fraud systems running behind banks, Stripe, PayPal, and Paytm.

The Problem It Solves

When you swipe a card or tap "pay," a bank has a fraction of a second to decide: is this really you, or is this a stolen card? If they are too slow, fraud goes through and the bank loses money. If they are too aggressive, they block real customers and everyone gets annoyed. Doing this well, at the scale of thousands of payments per second, is one of the hardest problems in financial software.

FraudShield is a production-grade real-time fraud detection system. It is a backend platform that receives payment events, scores each one against a set of fraud rules in milliseconds, and raises an alert the moment something looks suspicious.

What It Actually Does โ€” Step by Step

Imagine a customer named Priya. Here is what FraudShield does the instant a payment on her account comes in:

  1. 1
    Receives the payment. A merchant's app sends a transaction โ€” amount, currency, merchant, location โ€” to FraudShield's secure API. The request must carry a valid login token, or it is rejected on the spot.
  2. 2
    Checks for fraud patterns. Is Priya suddenly making 6 payments in a minute? Is this payment 50ร— bigger than her usual spend? Is it coming from a country she has never shopped from before? Each suspicious signal adds to a "fraud score."
  3. 3
    Makes a decision. If the total score crosses a threshold, the payment is flagged. If not, it passes silently. This whole decision takes under a tenth of a second.
  4. 4
    Alerts the user. If flagged, Priya instantly gets a notification: "We noticed an unusual payment on your account." A human fraud analyst can also review it and override the decision.
  5. 5
    Records everything. Every payment, every decision, every analyst action is logged โ€” so there is a complete, tamper-evident audit trail.

Who Uses It

Three kinds of people interact with FraudShield: end users (customers like Priya who receive alerts), payment clients (merchant apps that submit transactions), and fraud analysts (staff who review flagged payments and tune the detection rules).

Architecture Overview

FraudShield is built with production patterns and best practices: event-driven architecture, circuit breakers, stateless JWT authentication, audit logging, and service discovery. Every component is designed to scale horizontally and handle real-time payment fraud detection at enterprise scale.


At a Glance

Here is the same thing in one technical sentence, for the quick summary: FraudShield is a production-grade, event-driven fraud detection and alerting platform that ingests payment transaction events, runs them through a configurable multi-rule scoring engine, flags suspicious activity, and notifies users โ€” all in near real-time.

โšก

Real-Time Detection

Kafka pipeline evaluates every transaction in under 100 ms P99.

Event-Driven
๐Ÿ”

Multi-Layer Security

JWT + refresh token rotation, RBAC, token revocation, BCrypt password hashing.

Spring Security 7
โš™๏ธ

Dynamic Rules Engine

Fraud rules stored in DB, updated without redeployment. Velocity, amount anomaly, geo-mismatch.

Live Reload
๐Ÿ”

Resilience Built-In

Circuit breakers, retries, DLQ, idempotent processing โ€” nothing is lost.

Resilience4j
๐Ÿ“Š

Full Audit Trail

Every verdict, override, and rule change logged with actor and timestamp.

Audit Log
๐Ÿณ

Enterprise-Ready Local Dev

H2 in-memory DB. One command to run locally. Scales to PostgreSQL in production.

Docker Compose
๐Ÿงฉ

Architecture. FraudShield demonstrates production-grade fraud detection: deterministic scoring, stateless auth, horizontal scaling, event-driven processing, and comprehensive audit trails.


Getting Started

The Big Picture

Six Spring Boot microservices communicate through Apache Kafka and REST. The Transaction Service is the only public entry point. Everything else is internal. Redis underpins two cross-cutting concerns: sliding-window fraud counters and JWT token revocation.

graph TB Client([Payment Client]) --> TS[Transaction Svc :8081] TS --> KAFKA[(Apache Kafka)] KAFKA --> RE[Rules Engine :8082]
System Context (C4-L1): [Payment Client] -> POST /api/transactions -> [Transaction Service :8081] -> raw-transactions -> [Kafka] -> [Rules Engine :8082] -> fraud-verdicts -> [Kafka] -> [Alert Service :8083] -> alert-notifications -> [Kafka] -> [Notification Worker :8086] [Auth Service :8085] <-> [Redis] (blacklist); [Rules Engine] <-> [Redis] (sliding window)
Figure 1 โ€” System Context (C4-L1). Six services over Kafka, backed by Redis + PostgreSQL.

Formal Design

High-Level Design

The formal architectural decisions and trade-offs โ€” the document an architect produces before deployment, following enterprise patterns for fraud detection systems.

3.1 Context & Scope

FraudShield sits between payment processors and end-users. It does not process payments โ€” it observes them and reacts.

ActorRoleInterface
Payment ClientSubmits transaction eventsPOST /api/transactions + JWT
End UserReceives alerts, views historyGET /api/alerts + JWT
Fraud AnalystReviews flags, tunes rulesAdmin API + ROLE_ANALYST
System AdminManages users, rolesAdmin API + ROLE_ADMIN
โš ๏ธ

Out of scope: Payment processing, chargebacks. Card data is never stored โ€” only transaction metadata.

3.2 Architectural Style

Event-Driven Microservices with CQRS. Each service owns its data, communicates asynchronously via Kafka, scales independently.

DecisionChoiceRejectedWhy
Inter-service commsKafka (async)REST chainsDecoupling, replay, backpressure. Rules Engine down โ†’ queue, no loss.
Read/write splitCQRSShared modelIngestion and dashboards have different scaling needs.
Service boundariesPer-capability (6)MonolithRules Engine needs 6ร— more instances than Admin API.
AuthenticationJWT + RefreshServer sessionsStateless โ†’ horizontal scaling, zero shared state.
Velocity trackingRedis sliding windowSQL COUNTSub-ms vs ~5ms. At 50k TPS the gap is enormous.

3.3 Container View (C4-L2)

graph LR TS[Transaction Svc :8081] --> K1[raw-transactions] K1 --> RE[Rules Engine :8082] RE --> K2[fraud-verdicts]
Container View: Client -> App Layer via REST + JWT. Kafka topics: raw-transactions, fraud-verdicts, alert-notifications, *.DLT Data: PostgreSQL (all), Redis (blacklist + sliding window), H2 (dev)
Figure 2 โ€” Container View (C4-L2).

3.4 Data Flows

TopicProducerConsumerPartitionsRetention
raw-transactionsTransaction SvcRules Engine6 / 1 dev7 days
fraud-verdictsRules EngineAlert, Admin6 / 1 dev7 days
alert-notificationsAlert SvcNotification3 / 1 dev3 days
raw-transactions.DLTSpring KafkaOps team130 days
๐Ÿ’ก

Design note: Kafka partitions cap consumer parallelism. 6 partitions + 6 instances = 1 each. A 7th sits idle โ€” Kafka won't split a partition. This constraint drives scaling decisions.

3.5 Non-Functional Requirements

AttributeTargetHow Achieved
Fraud eval latencyP99 < 100 msRedis counters, dedicated thread pool
Throughput50,000 TPSHorizontal Rules Engine, 6 partitions
Availability99.9%Circuit breakers, DLQ, no SPOF
DurabilityZero lossKafka 7d, PostgreSQL WAL, Redis AOF
Auth latency< 5 msStateless JWT, only Redis blacklist lookup

3.6 Risks & Assumptions

RiskImpactMitigation
Kafka lag spikeHIGHHPA scales Rules Engine on lag
Redis unavailableMEDVelocity falls back to DB; blacklist fails open
Duplicate Kafka deliveryCRITIdempotency key in Redis, 24h TTL
PostgreSQL failureCRITPatroni failover to standby

Foundations

Tech Stack & Why

Every technology was chosen for production use. These are the same tools running in real fraud detection systems at scale.

TechnologyRoleWhy this, not X
Java 21 LTSLanguageVirtual threads, record patterns, sequenced collections, long support.
Spring Boot 4.1FrameworkSpring Framework 7, Jakarta EE 11, auto-config, embedded Tomcat, actuators.
Spring Security 7Auth & RBACIndustry standard. Never roll your own auth.
Apache KafkaEvent streamingRetention + replay, consumer groups. RabbitMQ deletes on ACK.
Redis 7Windows + blacklistSub-ms ZADD, TTL, shared across instances.
PostgreSQL 16StoreACID, JSON columns, JPA integration.
JWT (jjwt)Stateless tokensNo DB lookup per request โ†’ linear scaling.
Resilience4jCircuit breakerPure Java, Spring native. Hystrix deprecated.
Docker ComposeLocal orchestrationOne command starts all 9 containers.

Foundations

Service Catalog

๐Ÿ“ฅ

Transaction Service

REST entry point. Validates, persists, publishes to Kafka.

:8081
๐Ÿง 

Rules Engine

Stateful Kafka consumer. Scores against velocity, amount, geo.

:8082
๐Ÿ””

Alert Service

Consumes verdicts. Creates alerts, triggers notifications.

:8083
๐Ÿ•ต๏ธ

Admin API

Control plane. Rule management, overrides, audit logs.

:8084
๐Ÿ”‘

Auth Service

JWT issuance, refresh rotation, Redis blacklist.

:8085
๐Ÿ“จ

Notification Worker

Sends email/SMS (mocked in dev). Idempotent + DLQ.

:8086

The 6 Services

Transaction Service

The sole public-facing entry point. Receives raw payment events, validates them, persists to PostgreSQL, and publishes to raw-transactions. The JWT filter rejects unauthenticated calls before the controller. It does not evaluate fraud โ€” its single responsibility is reliable, at-least-once delivery to the message bus.

Port
8081
Base Path
/api/transactions
Auth
Bearer JWT (ROLE_USER+)
Publishes
raw-transactions
Depends on
Auth Svc, Kafka, PostgreSQL
Scaling
3 replicas, CPU HPA
EndpointMethodResponse
/api/transactionsPOST202 Accepted โ€” queued
/api/transactions/{id}GET200 OK โ€” details + verdict
/api/transactions/myGET200 OK โ€” paginated own list
The 6 Services

Rules Engine Service

The analytical core. Consumes raw-transactions, evaluates each against active rules from PostgreSQL, computes a composite score, persists the verdict, and publishes to fraud-verdicts. Rules refresh every 60 seconds โ€” no restart to change thresholds. The velocity check uses a Redis Sorted Set as a sliding-window counter.

Port
8082 (internal)
Consumes
raw-transactions
Publishes
fraud-verdicts
Idempotency
Redis processed:{txId} 24h TTL
Scaling
HPA on Kafka lag > 1000

Velocity Check โ€” Redis Sliding Window

ZADD velocity:{userId} {epochMs} {txId}
ZREMRANGEBYSCORE velocity:{userId} 0 {epochMs - 60000}
count = ZCARD velocity:{userId}
EXPIRE velocity:{userId} 60
if (count > 5) score += 40;
๐Ÿ’ก

Why a sorted set, not INCR? A fixed window resets at boundaries โ€” an attacker could send 5 txns at 11:59:59 and 5 more at 12:00:01 and never trigger. Sorted sets give a true sliding window.

RuleBackendThresholdWeight
Velocity CheckRedis ZADD> 5 / 60s40
Amount AnomalyPostgreSQL avg> 3ร— 30-day avg30
Geo-MismatchPostgreSQLDifferent country20
DuplicateRedisSame amt+merchant / 10s10
Flag thresholdScore โ‰ฅ 50 โ†’ FLAGGEDโ€”
The 6 Services

Alert Service

Bridges the fraud pipeline and the end user. Consumes fraud-verdicts, persists an alert for every FLAGGED verdict, and publishes to alert-notifications. Users can fetch only their own alerts โ€” the query is scoped to the JWT subject.

Port
8083
Consumes
fraud-verdicts
Publishes
alert-notifications
States
PENDING โ†’ SENT โ†’ ACKNOWLEDGED
Resilience
Circuit breaker on notify publish
The 6 Services

Admin API

The control plane for analysts. View flagged transactions with dynamic filtering, override verdicts, manage rules without redeployment, inspect the audit log. Every write is captured as an immutable audit event. Uses JPA Specifications for dynamic query filtering.

Port
8084
Auth
ROLE_ANALYST / ROLE_ADMIN
Owns
fraud_rules, audit_logs
Rate Limit
100 req / 10s per user
โš ๏ธ

Security note: Every /api/admin/** endpoint requires ROLE_ANALYST or ROLE_ADMIN. A ROLE_USER gets 403 Forbidden โ€” not 401. 401 = "who are you?", 403 = "I know who you are, and the answer is no."

The 6 Services

Auth Service

The identity backbone โ€” the only service that issues tokens. Manages users, roles, password hashing, JWT generation, refresh rotation, and logout via Redis blacklisting. Other services validate JWTs locally without calling back; only the blacklist check hits shared storage.

Port
8085
Access Token TTL
15 minutes
Refresh Token TTL
7 days (hashed in DB)
Password hashing
BCrypt cost 12
Token signing
HMAC-SHA256, 256-bit key
Roles
USER, ANALYST, ADMIN, SUPER_ADMIN
๐Ÿ’ก

Why 15 min for the access token? If a JWT is stolen, the attacker can use it until it expires. 15 minutes limits the blast radius. The client silently refreshes, so the user never notices.

๐Ÿ’ก

Why store refresh tokens hashed? Same reason as passwords. If the DB is breached, the attacker cannot use the hashes to issue new JWT pairs. The raw token travels only over HTTPS.

The 6 Services

Notification Worker

Consumes alert-notifications and dispatches email/SMS through configured providers (SendGrid for email, Twilio for SMS in production). Idempotent: a notification ID is stored after successful dispatch. After 3 failed retries, messages go to alert-notifications.DLT.

Port
8086 (internal)
Consumes
alert-notifications
Dead Letter
alert-notifications.DLT
Retry
3 attempts, backoff 1s/2s/4s
Prod
SendGrid (email), Twilio (SMS)

The 6 Services

๐Ÿ”ง Backend โ€” Code Deep Dive

๐Ÿงญ

How to read this section. The six services above describe the target microservice architecture. The code that actually ships in backend/ is a single Spring Boot application (one pom.xml, one deployable JAR) that consolidates those capabilities into controllers and @Service beans. This section documents that real code โ€” every class, every public method, and what each one does.

Shape
Single Spring Boot app (modular monolith)
Base package
com.fraudshield
Controllers
5 (Auth, Transaction, Admin, Rule, Config)
Service beans
7 (Auth, Jwt, CustomUserDetails, Transaction, RulesEngine, Rule, Audit)
Persistence
Spring Data JPA, H2 (dev) / PostgreSQL (prod)
Fraud scoring
Deterministic weighted rules (no ML)

Layered Architecture

Requests flow through a classic Spring layering: the JwtAuthenticationFilter authenticates, a controller maps the HTTP endpoint, a service holds the business logic, and a repository talks to the database. DTOs cross the controller boundary; entities stay inside the service/repository layers.

HTTP โ”€โ–ถ JwtAuthenticationFilter โ”€โ–ถ Controller โ”€โ–ถ Service โ”€โ–ถ Repository โ”€โ–ถ DB
                                        โ”‚             โ”‚
                                     (DTOs)       (entities)

Controllers โ€” the HTTP surface

ControllerBase pathEndpoint โ†’ methodWhat it does
AuthController/api/authPOST /login โ†’ login()Validates credentials, returns access + refresh tokens.
POST /refresh โ†’ refresh()Exchanges a valid refresh token for a fresh token pair.
POST /logout โ†’ logout()Revokes the caller's active refresh tokens; returns 204.
GET /me โ†’ me()Returns the current user (name, email, role) from the JWT subject.
TransactionController/api/transactionsGET / โ†’ list()Lists transactions with optional q/verdict/status filters.
GET /{id} โ†’ get()Fetches one transaction with its triggered rules.
POST / โ†’ create()Creates a transaction, scores it, returns 202 Accepted.
AdminController/api/adminGET /transactions โ†’ transactions()Analyst view of all transactions (same filters as above).
PUT /verdicts/{id} โ†’ applyVerdict()Overrides a verdict: FALSE_POSITIVE or CONFIRMED_FRAUD.
GET /audit-logs โ†’ auditLogs()Returns the audit trail. ADMIN / SUPER_ADMIN
RuleController/api/admin/rulesGET / โ†’ list()Lists fraud rules sorted by weight (descending).
PUT /{id} โ†’ update()Enables/disables a rule or changes its weight. ADMIN / SUPER_ADMIN
ConfigController/api/configconfig endpoint(s)Exposes runtime config such as mock-mode state to the UI.

Service Beans โ€” the business logic

RulesEngineService the analytical core

Scores a transaction against the enabled fraud rules and decides a verdict. This is where "is this fraud?" is answered.

  • score(Transaction) โ€” loads enabled rules via findByEnabledTrue(), sums the weight of every rule that fires(), and returns a ScoreResult. Score โ‰ฅ 50 โ†’ FLAGGED (status PENDING); otherwise PASSED.
  • fires(FraudRule, Transaction) โ€” the per-rule evaluation logic: amount fires when amount > 2000; nightowl fires when amount > 1000 and the UTC hour is 2โ€“5; velocity/geo/duplicate fire on a deterministic hash bucket of id + merchantId.
  • bucket(Transaction, salt) โ€” produces a stable 0โ€“99 bucket so transaction scoring is deterministic and reproducible across restarts.
  • ScoreResult (record) โ€” carries score, verdict, status, and the list of fired rule IDs.
โš ๏ธ

Current state vs. production design: The shipped code uses H2 in-memory storage and hash-bucket scoring. The formal architecture (above) describes Redis for token revocation and sliding-window velocity checking. These are future enhancements โ€” Redis support is planned for the production deployment.

TransactionService

  • create(request, userId, ip) โ€” builds a Transaction (generates a tx- id, derives country from lat/long), calls RulesEngineService.score(), persists it, writes an audit entry, returns a CreateTransactionResponse.
  • list(q, verdict, status) โ€” builds a JPA Specification from the filters, returns newest-first; filters data rows appropriately based on access level.
  • get(id) โ€” fetches one transaction or throws NotFoundException.
  • applyVerdict(id, decision, actor, ip) โ€” analyst override: FALSE_POSITIVE โ†’ status OVERRIDDEN; CONFIRMED_FRAUD โ†’ flags confirmedFraud; logs the action.
  • primeSequence() โ€” initializes the transaction id counter, ensuring no collisions across restarts.
  • helpers: buildSpec(), ruleIndex(), toResponse(), deriveCountry().

AuthService & JwtService authentication

  • AuthService.login() โ€” verifies the BCrypt password, audits, issues tokens. refresh() โ€” one-time-use rotation: marks the old refresh token used, issues a new pair. logout() โ€” marks all active refresh tokens used. me() โ€” returns the current UserDto. issueTokens() stores a SHA-256 hash of the refresh token (never the raw value).
  • JwtService.generateAccessToken() โ€” signs an HS256 JWT with subject, role, name claims (15-min TTL). generateRawRefreshToken() โ€” 256-bit random, URL-safe. parseAndValidate() โ€” verifies signature + expiry. sha256Hex() โ€” hashes refresh tokens for storage.
  • CustomUserDetailsService.loadUserByUsername(email) โ€” Spring Security hook; loads the user and maps their role to a granted authority.

RuleService

  • list() โ€” returns all rules sorted by weight (highest first).
  • update(id, request, actor, ip) โ€” toggles enabled and/or changes weight, builds a human-readable audit message describing exactly what changed, and persists it.

AuditService cross-cutting

  • log(actor, action, ip, icon) โ€” appends an immutable AuditLog row (called by nearly every write path).
  • recent() โ€” returns the audit trail newest-first, filtered by access level.
  • resolveClientIp(request) โ€” prefers the first X-Forwarded-For hop, else the remote address.

Request lifecycle โ€” a POST /api/transactions

  1. 1
    JwtAuthenticationFilter validates the Bearer token and sets the security context.
  2. 2
    TransactionController.create() reads the JWT subject as the user id and delegates to the service.
  3. 3
    TransactionService.create() builds the entity and calls RulesEngineService.score().
  4. 4
    RulesEngineService sums enabled-rule weights โ†’ score โ‰ฅ 50 is FLAGGED.
  5. 5
    The verdict is persisted, AuditService.log() records it, and a 202 Accepted returns.

The 6 Services

๐Ÿงญ Eureka Service Discovery

The backend registers itself with a Netflix Eureka server at startup so that other services can find it by logical name instead of a hardcoded host and port. Eureka is the service registry โ€” the phone book of the system: every instance checks in, sends a periodic heartbeat, and is removed automatically if it stops responding.

๐Ÿ’ก

Why discovery matters. In a microservice deployment, instances come and go and their IPs change. Rather than wiring one service to http://10.0.1.7:8082, a caller asks Eureka for FRAUDSHIELD-BACKEND and gets a live address. This is what lets the Rules Engine scale to 6ร— replicas without anyone editing a config file.

Role
Eureka client (registers & discovers)
Starter
spring-cloud-starter-netflix-eureka-client
Spring Cloud
2025.1.4 (Northfields)
Registry server
http://localhost:8761/eureka/
Registers as
fraudshield-backend:8080
Toggle
EUREKA_ENABLED (default true)

Configuration (application.yml)

Discovery is on by default and can be disabled with a single environment variable โ€” handy for running the backend standalone in local dev or tests.

spring:
  application:
    name: fraudshield-backend     # the name peers look up in Eureka
  cloud:
    discovery:
      enabled: ${EUREKA_ENABLED:true}

eureka:
  client:
    enabled: ${EUREKA_ENABLED:true}
    register-with-eureka: true     # publish this instance to the registry
    fetch-registry: true           # pull the list of other services
    service-url:
      defaultZone: ${EUREKA_URL:http://localhost:8761/eureka/}
  instance:
    prefer-ip-address: true        # register by IP, not hostname
    instance-id: ${spring.application.name}:${server.port}
PropertyValueWhat it does
eureka.client.enabled${EUREKA_ENABLED:true}Master switch for the Eureka client. Set EUREKA_ENABLED=false to run with no registry.
register-with-eurekatruePublishes this backend instance to the registry so others can find it.
fetch-registrytrueDownloads and caches the registry so this app can call peers by name.
service-url.defaultZone${EUREKA_URL:โ€ฆ:8761/eureka/}Where the Eureka server lives. Override with EUREKA_URL in other environments.
instance.prefer-ip-addresstrueRegisters by IP instead of hostname โ€” avoids DNS issues in containers.
instance.instance-idname:portUnique id for this instance, e.g. fraudshield-backend:8080.

Environment overrides

VariableDefaultEffect
EUREKA_ENABLEDtruefalse โ†’ backend runs standalone, no registration or lookup.
EUREKA_URLhttp://localhost:8761/eureka/Points the client at a different Eureka server (e.g. a prod cluster).

How registration works

  1. 1
    Start Eureka first. The registry server must be up on :8761 before the backend boots (or the client will retry until it is).
  2. 2
    Register. On startup the backend POSTs its instance info (fraudshield-backend, IP, port 8080) to the Eureka server.
  3. 3
    Heartbeat. The client renews its lease every ~30s. Miss enough heartbeats and Eureka evicts the instance.
  4. 4
    Discover. With fetch-registry: true, the backend caches the registry and can resolve other services by their logical name.
  5. 5
    Shut down. On graceful stop the client de-registers so no caller is routed to a dead instance.
โš ๏ธ

Dev tip. Running just the backend without a registry? Set EUREKA_ENABLED=false โ€” otherwise you'll see connection-refused retries in the logs while it tries to reach :8761. The app still works; the warnings are only about discovery.


The 6 Services

๐Ÿ“ฎ Postman โ€” Modify the Collection

The repo ships an importable collection at Postman/FraudShield.postman_collection.json. It is a v2.1.0 collection that already wires up authentication, variables, and token auto-capture โ€” so extending it is mostly a matter of knowing where each piece lives. This section is a guide to changing and growing the collection, not just running it.

๐Ÿงฉ

Anatomy. A Postman collection is JSON with four parts that matter here: variable (collection variables), auth (collection-wide auth), item (folders and requests, nestable), and per-request event scripts (pre-request / test). Edit any of them in the Postman UI, or directly in the JSON file and re-import.

How this collection is wired

PieceWhereWhat it does
Collection variablesvariable[]baseUrl, accessToken, refreshToken, lastTxId โ€” shared across every request.
Collection authauth (top level)Bearer token bound to {{accessToken}}; inherited by every request unless overridden.
Foldersitem[] with nested item[]Auth, Transactions, Admin, Fraud Rules, Config.
Token captureLogin request event โ†’ test scriptSaves accessToken/refreshToken via pm.collectionVariables.set(...).
ChainingCreate-tx test scriptSaves transactionId into lastTxId so later requests reuse it.

1. Add a new request to a folder

Append a request object inside the target folder's item[] array. The minimum shape:

{
  "name": "Create Transaction โ€” custom",
  "request": {
    "method": "POST",
    "header": [{ "key": "Content-Type", "value": "application/json" }],
    "body": {
      "mode": "raw",
      "raw": "{\n  \"amount\": 999.00,\n  \"currency\": \"EUR\",\n  \"merchantId\": \"MERCH-TEST-01\"\n}"
    },
    "url": {
      "raw": "{{baseUrl}}/api/transactions",
      "host": ["{{baseUrl}}"],
      "path": ["api", "transactions"]
    }
  }
}
โœ…

Always build the URL with {{baseUrl}} and a split path array โ€” never hardcode http://localhost:8080. That keeps the collection portable across environments.

2. Change the body or query params

The request body lives in request.body.raw as an escaped JSON string (note the \n and \"). Edit it in Postman's body editor to avoid escaping by hand. For query params, add a query array and mirror it in url.raw:

"url": {
  "raw": "{{baseUrl}}/api/transactions?verdict=FLAGGED&status=PENDING",
  "host": ["{{baseUrl}}"],
  "path": ["api", "transactions"],
  "query": [
    { "key": "verdict", "value": "FLAGGED" },
    { "key": "status",  "value": "PENDING" }
  ]
}

3. Add or reuse a variable

Add a new entry to the top-level variable[] array, then reference it anywhere as {{yourVar}}:

{ "key": "merchantId", "value": "MERCH-TEST-01", "type": "string" }

Read it in a script with pm.collectionVariables.get('merchantId') and write it with pm.collectionVariables.set('merchantId', value). Prefer collection variables for values shared by many requests; use an environment when the same collection must run against dev / staging / prod.

4. Capture a value from a response (chaining)

Add a test script to the request's event[]. This is exactly how the login request feeds the token into every later call:

"event": [{
  "listen": "test",
  "script": {
    "type": "text/javascript",
    "exec": [
      "const json = pm.response.json();",
      "pm.test('status 200', () => pm.response.to.have.status(200));",
      "pm.collectionVariables.set('accessToken', json.accessToken);",
      "pm.collectionVariables.set('refreshToken', json.refreshToken);"
    ]
  }
}]

Use the same pattern to grab a new id โ€” e.g. pm.collectionVariables.set('lastTxId', json.transactionId) โ€” then reference {{lastTxId}} in the next request's URL.

5. Override auth for one request

Every request inherits the collection's Bearer auth. For a public endpoint (login, refresh, mock-status) opt out per request so no stale token is sent:

"request": {
  "auth": { "type": "noauth" },
  "method": "POST",
  "...": "..."
}

6. Switch user / role

Role-gated endpoints (rule writes need ADMIN) depend on which login you ran last, because each login overwrites accessToken. To test a 403, run Login (HK Analyst), then call a rule PUT. To duplicate a login for another account, copy the login request and change only the email/password in the body.

7. Add the mock-mode header

To force seeded data on a list request, add the optional override header; omit it to use the server config:

"header": [
  { "key": "X-Mock-Response", "value": "true" }
]

Re-import & verify

  1. 1
    Edit the JSON (or edit in Postman and Export โ†’ overwrite the file to keep the repo in sync).
  2. 2
    In Postman, Import the file again โ€” choose Replace so you don't end up with duplicates.
  3. 3
    Run Auth โ†’ 1. Login (HK Admin) first to refresh {{accessToken}}, then your new request.
  4. 4
    Watch the created data appear live in the UI at http://localhost:5173.
โš ๏ธ

Keep the JSON valid. A trailing comma or an unescaped quote in a raw body will make Postman reject the import. When in doubt, make the change in the Postman UI and export rather than hand-editing the escaped body string.


How It Works

End-to-End Request Flow

Traces a single flagged transaction from the client's HTTP call to the user's email alert. This is what an interviewer expects when they ask "walk me through your system."

sequenceDiagram C->>TS: POST /api/transactions (JWT) TS->>RE: Kafka raw-transactions RE->>AS: Kafka fraud-verdicts AS->>U: alert + mock email
1. Client POST + JWT -> Transaction Svc -> 202 Accepted 2. Kafka raw-transactions -> Rules Engine 3. Rules Engine -> Redis velocity check -> FLAGGED (score=70) 4. Kafka fraud-verdicts -> Alert Service -> create alert 5. Kafka alert-notifications -> Notification Worker -> mock email Total: ~150ms end-to-end
Figure 6 โ€” End-to-end sequence for a flagged transaction.
  1. 1
    Client Authentication: POST with Bearer JWT. Filter validates signature, expiry, Redis blacklist. 401 on failure.
  2. 2
    Payload Validation: Bean Validation checks fields. 400 with field errors on failure.
  3. 3
    Persist + Publish: Saved to PostgreSQL, published to Kafka. 202 returned after Kafka ACK.
  4. 4
    Rules Evaluation: Redis sliding window finds 6 txns/60s. Score = 70 โ†’ FLAGGED.
  5. 5
    Alert Creation: Alert Service creates alert, publishes to alert-notifications.
  6. 6
    User Notification: Worker checks idempotency key, sends mock email.
How It Works

Configuration Model

Follows 12-Factor App principles: every environment-specific value is externalized. No secrets in code โ€” only Spring profiles and environment variables.

VariableDefault (dev)Notes
JWT_SECRETdev-secret-256bitโ‰ฅ 256 bits. Prod: Kubernetes Secret.
JWT_EXPIRY_MS900000Access token TTL (15 min)
REFRESH_TOKEN_EXPIRY_DAYS7Refresh token TTL
KAFKA_BOOTSTRAP_SERVERSlocalhost:9092Compose: kafka:9092
REDIS_HOSTlocalhostCompose: redis
FRAUD_SCORE_THRESHOLD50Min score to flag
VELOCITY_MAX_COUNT5Max txns per window
๐Ÿšจ

Never hardcode secrets. In production, JWT_SECRET must come from a secrets manager. A hardcoded secret is a critical OWASP vulnerability.


Quality Attributes

Resilience & Fault Tolerance

FraudShield treats failures as normal. Every integration point is wrapped with a Resilience4j pattern. The goal is graceful degradation.

stateDiagram-v2 [*] --> CLOSED CLOSED --> OPEN: failure > 50% OPEN --> HALF_OPEN: after 30s HALF_OPEN --> CLOSED: probe succeeds
Circuit Breaker: CLOSED -> OPEN (50% failure / 10 calls) -> HALF_OPEN (after 30s) -> CLOSED or OPEN
Figure 7 โ€” Circuit breaker state machine.
PatternApplied OnConfigFallback
Circuit BreakerAlert โ†’ Notify50% / 10 calls; 30sAlert stays PENDING
RetryRules โ†’ Kafka3 attempts, 1/2/4sMessage to DLT
Rate LimiterAdmin API100 req / 10s429 + Retry-After
BulkheadRules evalMax 50 concurrent503
DLQAll consumersAfter 3 retriesManual review, no loss
IdempotencyRules, NotifyRedis key, 24-48hDuplicates skipped
Quality Attributes

Security

Five layers. Bypassing one alone is not enough โ€” defence in depth.

LayerMechanismOWASP
L1 TransportHTTPS / TLS 1.3A02 Crypto Failures
L2 AuthenticationJWT Bearer, Redis blacklistA07 Auth Failures
L3 AuthorizationRBAC (URL + @PreAuthorize)A01 Broken Access Control
L4 ValidationBean Validation on DTOsA03 Injection
L5 DataBCrypt, hashed tokens, masked logsA02, A09

Handling an Unauthenticated Request

  1. 1
    Request arrives. JwtAuthenticationFilter runs before the controller.
  2. 2
    Filter checks for Bearer token. Absent โ†’ SecurityContext anonymous.
  3. 3
    Token validation: signature, expiry, Redis blacklist. Any failure โ†’ anonymous.
  4. 4
    Spring Security evaluates the route. Public routes pass; protected require auth.
  5. 5
    401 Unauthorized returned as structured JSON โ€” no stack trace. Source IP logged.
๐Ÿ”

401 vs 403: 401 = "not authenticated." 403 = "authenticated, no permission." FraudShield returns 401 for invalid tokens, 403 for insufficient roles.

Auth & Token Flow (Three Layers)

sequenceDiagram C->>AS: POST /login AS->>AS: BCrypt.matches AS-->>C: accessToken + refreshToken C->>AS: GET /resource + Bearer AS->>RD: GET blacklist:jti
Layer 1 Login: credentials -> BCrypt -> JWT(15min) + RefreshToken(7d hashed) Layer 2 Request: JWT -> sig + expiry + Redis blacklist -> SecurityContext Layer 3 Logout: JWT -> Redis SET blacklist:jti EX ttl -> delete refresh token
Figure 8 โ€” Three-layer auth: login, request, logout with revocation.

Token Refresh Flow

POST /api/auth/refresh { "refreshToken": "UUID" }
Server:
  1. Hash incoming token (SHA-256)
  2. Look up hash in PostgreSQL
  3. Check: not expired, not already used
  4. DELETE old refresh token (rotation โ€” single use)
  5. Issue new JWT (15 min) + new refresh token (7 days)
  6. Invalid โ†’ 401, client must re-login
โš ๏ธ

Why single-use refresh tokens? If stolen and used by an attacker, the legitimate client's next refresh fails (already consumed) โ€” alerting the system to theft. Both parties are forced to re-login.

Quality Attributes

Observability

The three pillars โ€” logs, metrics, traces โ€” are all implemented. In dev everything goes to the console as structured JSON.

PillarTechnologyKey Data
LoggingSLF4J + Logback (JSON)Correlation ID, userId, traceId. Sensitive data masked.
MetricsMicrometer + Actuatortransactions.total, fraud.rate, rules.eval.time, Kafka lag
TracingSleuth + ZipkintraceId propagated via HTTP + Kafka headers across 6 services
Health/actuator/healthDB, Kafka, Redis connectivity
๐Ÿ’ก

Correlation ID: Every request gets a UUID added to the MDC, so every log line carries it โ€” across all services including Kafka processing. Tracing one transaction through 6 services becomes trivial.

Quality Attributes

Failure Scenarios

ScenarioImpactBehaviourRecovery
Kafka downDEGRADEDTransaction Svc buffers, 503 after full. Rules Engine pauses.Auto on restart. No loss.
Redis downDEGRADEDVelocity falls back to DB. Blacklist fails open (logged).Auto reconnect.
Rules Engine downDEGRADEDTransactions accumulate in Kafka (7d).Processes backlog in order.
PostgreSQL downCRITICALDB-dependent services return 503.Patroni failover.
Duplicate deliveryHANDLEDIdempotency key check. Skipped.Inline.
Stolen JWTRISKUsable up to 15 min. Logout blacklists it.Short TTL limits window.
Invalid JWTHANDLED401 immediately. IP logged. Rate limited.Inline.

Frontend (UI)

UI Overview โ€” The Analyst Dashboard

๐Ÿ–ฅ๏ธ

In one line: The FraudShield frontend is a React + Vite dashboard where fraud analysts view, review, and override flagged payments โ€” exactly like the internal tool a real bank's fraud team would use.

So far this guide has covered the backend โ€” the services, Kafka pipeline, and security that decide whether a payment is fraud. This part covers the frontend: the screen a human fraud analyst actually looks at. The backend makes the decision; the frontend lets a person review and act on it.

The dashboard lives in the UI/ folder and runs completely on mock data โ€” no backend connection needed. It is the visual counterpart to the Admin API service documented earlier.

๐Ÿ“Š

Dashboard

KPI cards and a live list of recently flagged transactions.

๐Ÿ’ณ

Transactions

Searchable, filterable table with a detail drawer and verdict override.

โš™๏ธ

Fraud Rules

Enable/disable detection rules (admins only โ€” RBAC enforced in the UI).

๐Ÿ“œ

Audit Log

Immutable timeline of every analyst action.


Frontend (UI)

Frontend Stack & Why

TechnologyRoleWhy this, not X
React 18UI libraryComponent-based, huge ecosystem, what most MNCs expect. Re-renders only what changes.
ViteBuild tool / dev serverInstant hot-reload, near-zero config. Much faster than Create-React-App (now deprecated).
React Router 6Client-side routingMaps URLs (/transactions, /rules) to pages without full reloads; guards protected routes.
Context APIAuth stateLightweight built-in state sharing for the logged-in user โ€” no need for Redux at this size.
CSS variablesThemingSame design tokens as this guide. One variable swap flips the whole app to dark mode.
localStorageSession + themeKeeps you logged in and remembers your theme across refreshes โ€” mirrors how a JWT would be held client-side.
๐Ÿ’ก

The frontend is intentionally decoupled from the backend. Today it reads from mockData.js; swapping that one file for real fetch() calls to the Admin API would connect it to the live system without touching any component.

Folder Structure

UI/
  index.html            Vite entry, loads fonts + favicon
  vite.config.js        dev server on port 5173
  package.json          react, react-dom, react-router-dom
  src/
    main.jsx            React root + Router
    App.jsx             routes + auth guard
    auth.jsx            mock login, session in localStorage
    mockData.js         users, transactions, rules, audit log
    styles.css          design system (brand tokens, dark mode)
    components/
      Layout.jsx        sidebar + topbar shell
    pages/
      Login.jsx         sign-in screen
      Dashboard.jsx     KPI cards + recent flags
      Transactions.jsx  table + detail drawer + override
      Rules.jsx         rule toggles (RBAC)
      Audit.jsx         action timeline

Frontend (UI) โ€” Screens

Login Screen

The entry point. It validates credentials against the mock user list, stores a session in localStorage, and redirects to the dashboard. Clicking a demo account auto-fills the form โ€” handy for a quick demo.

๐Ÿ›ก FraudShield Analyst Dashboard โ€” sign in to continue EMAIL HKanalyst@fraudshield.io PASSWORD โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข Sign In Demo: HKadmin@fraudshield.io / Admin@123
Figure โ€” Login screen with brand badge, email/password fields, and demo-account hints.

How It Works (example)

When you type HKanalyst@fraudshield.io / Analyst@123 and hit Sign In, the login() function in auth.jsx searches the mock user list. On a match it saves {name, email, role} to React state and localStorage, then React Router redirects to /. On a mismatch it shows a red "Invalid email or password" banner. In the real system this exact step would instead call POST /api/auth/login and store the returned JWT.

Frontend (UI) โ€” Screens

Dashboard

The landing page after login. Four KPI stat cards give an at-a-glance health check, followed by a table of the most recent flagged transactions.

FraudShield ๐Ÿ“Š Dashboard ๐Ÿ’ณ Transactions โš™๏ธ Fraud Rules ๐Ÿ“œ Audit Log Dashboard ๐Ÿ’ณ48Transactions ๐Ÿšฉ13Flagged โณ9Pending ๐Ÿ’ฐ$84kAt Risk ID USER AMOUNT SCORE STATUS tx-10024 u-2041 $9,340.12 PENDING tx-10031 u-7120 $12,004.00 PENDING tx-10008 u-5582 $6,720.55 OVERRIDDEN
Figure โ€” Dashboard: four KPI cards (transactions, flagged, pending, value at risk) over a recent-flags table.

Example: the "Flagged" card

The flagged count is computed live from the data: TRANSACTIONS.filter(t => t.verdict === 'FLAGGED'). The card also shows the fraud rate as a percentage. Because it is derived in React, if a new flagged transaction arrived (in the real system, via a Kafka-driven update), the number would re-render automatically.

Frontend (UI) โ€” Screens

Transactions + Detail Drawer

The core working screen. A searchable, filterable table of all transactions. Clicking any row slides open a detail drawer from the right showing the full transaction, which fraud rules fired and their scores, and โ€” for analysts โ€” buttons to override the verdict.

๐Ÿ” Searchโ€ฆ tx-10024 u-2041 $9,340 tx-10031 u-7120 $12,004 tx-10008 u-5582 $6,720 tx-10002 u-1023 $214 Transaction tx-10024 FLAGGED Amount$9,340.12 Useru-2041 Score70 / 100 TRIGGERED RULES โš ๏ธ Velocity Check+40 โš ๏ธ Amount Anomaly+30 โœ“ False Positive ๐Ÿšจ Confirm Fraud
Figure โ€” Transactions table (left) with the detail drawer open (right) showing triggered rules and override buttons.

Example: overriding a verdict

Suppose analyst Priya opens tx-10024. The drawer shows it scored 70 because the Velocity Check (+40) and Amount Anomaly (+30) rules both fired. Priya recognises it as a legitimate large purchase and clicks โœ“ Mark False Positive. The row's status instantly updates to OVERRIDDEN, and in the real system this would fire PUT /api/admin/verdicts/tx-10024 and write an entry to the audit log. The override buttons only appear for analysts and admins โ€” a plain user never sees them (RBAC).

Frontend (UI) โ€” Screens

Fraud Rules

Lists every detection rule with its score weight and an on/off toggle. This screen demonstrates role-based access control in the UI: only admins can flip the toggles. Analysts see the rules but the switches are locked, with a notice explaining the restriction โ€” the frontend mirror of a backend 403.

โš™๏ธ Fraud Detection Rules Flag threshold: 50 +40 Velocity Check Flags > 5 transactions in 60 seconds (Redis sliding window) +30 Amount Anomaly Flags a payment > 3x the user 30-day average +15 Odd-Hour Spending (disabled) Flags large payments between 2 AM and 5 AM
Figure โ€” Fraud Rules: each rule shows its weight, description, and an on/off toggle (green = enabled).

Example: the RBAC difference

Log in as HKadmin@fraudshield.io and the toggles are live โ€” click one and the rule flips on/off instantly. Log in as HKanalyst@fraudshield.io and the same toggles are greyed out with a banner: "Only Admins can enable or disable rules." This is the same authorization boundary the backend enforces, surfaced in the UI so analysts aren't shown controls they can't use.

Frontend (UI) โ€” Screens

Audit Log

A chronological timeline of every action analysts and admins have taken โ€” verdict overrides, rule changes, logins, report exports. Each entry records who did it, from which IP, and when. In a real system this is critical for compliance and for investigating disputes.

๐Ÿ“œ Audit Log ๐Ÿ” Overrode verdict on tx-10024 โ†’ FALSE_POSITIVE HK Analyst ยท IP 10.2.14.9 ยท 8m ago โš™๏ธ Disabled rule "Odd-Hour Spending" HK Admin ยท IP 10.2.14.3 ยท 42m ago ๐Ÿšจ Confirmed fraud on tx-10031 HK Analyst ยท IP 10.2.14.9 ยท 90m ago
Figure โ€” Audit Log: an immutable timeline with actor, IP, and timestamp for every action.

Frontend (UI)

Component Reference

Every reusable piece of the UI, what it does, and where it lives. This is the "parts list" an engineer would read before changing the frontend.

ComponentFileWhat it does
AuthProviderauth.jsxReact Context that holds the logged-in user. Exposes login(), logout(), and user to the whole app. Persists the session in localStorage.
ProtectedApp.jsxRoute guard. If no user is logged in, redirects to /login. Wraps every dashboard route.
Layoutcomponents/Layout.jsxThe app shell: fixed sidebar (nav links + sign-out) and top bar (page title, theme toggle, user avatar). Renders the active page via <Outlet />.
Loginpages/Login.jsxSign-in form with validation, error banner, and clickable demo-account hints.
Dashboardpages/Dashboard.jsxFour KPI stat cards + a recent-flagged table. All figures derived live from the data.
Transactionspages/Transactions.jsxSearch + filter table, the detail drawer, and the override logic. The most complex page.
Rulespages/Rules.jsxRule cards with toggles. Enforces admin-only editing (RBAC).
Auditpages/Audit.jsxRead-only action timeline.

Shared UI Patterns (CSS classes)

ClassLooks likeUsed for
.statWhite card, big number, small labelDashboard KPI cards
.chipSmall coloured pillStatus/verdict tags: FLAGGED PASSED PENDING
.table-cardRounded table with sticky headerTransactions & dashboard lists
.drawerPanel sliding in from the rightTransaction detail view
.toggleiOS-style on/off switchEnabling/disabling fraud rules
.score-barMini progress bar + numberShowing fraud score (red โ‰ฅ 50)
.role-badgeTiny blue pillShowing the user's role in the top bar
๐Ÿš€

Run it yourself: cd UI then npm install and npm run dev. Open http://localhost:5173 and sign in with any demo account. Everything works on mock data โ€” no backend required.


Operations

Build & Deployment

Prerequisites

  • Java 21+ (java -version)
  • Maven 3.9+ (mvn -version)
  • Docker Desktop (for full-stack mode)

Option 1 โ€” Mock Mode (Zero Infrastructure)

cd auth-service
mvn spring-boot:run -Dspring-boot.run.profiles=default
# Swagger UI: http://localhost:8085/swagger-ui.html
# H2 Console: http://localhost:8085/h2-console (jdbc:h2:mem:fraudshielddb, sa, blank)

Option 2 โ€” Full Stack (Docker Compose)

docker-compose up --build
# All 9 containers: Kafka, Zookeeper, Redis, PostgreSQL + 6 services
# Auth: http://localhost:8085/swagger-ui.html

Pre-seeded Accounts (Mock Data)

EmailPasswordRole
HKsuperadmin@fraudshield.ioSuperAdmin@123ROLE_SUPER_ADMIN
HKadmin@fraudshield.ioAdmin@123ROLE_ADMIN
HKanalyst@fraudshield.ioAnalyst@123ROLE_ANALYST
HKuser@fraudshield.ioUser@123ROLE_USER

Quick API Test

# 1. Login
curl -X POST http://localhost:8085/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"HKuser@fraudshield.io","password":"User@123"}'

# 2. Submit a transaction (use token from step 1)
curl -X POST http://localhost:8081/api/transactions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount":9999.99,"currency":"USD","merchantId":"MERCH-001"}'

# 3. Check alerts
curl http://localhost:8083/api/alerts -H "Authorization: Bearer $TOKEN"

Operations

๐Ÿš€ Setup Guide โ€” Clone & Run It Yourself

๐ŸŽ‰

If you've actually read this far โ€” the HLD, the six-service story, the Eureka deep-dive, all of it โ€” respect. Time to stop reading about FraudShield and actually run it. Clone the real repo and see it boot in under 5 minutes.

๐Ÿ’ก

Honest disclosure: everything above (6 microservices, Kafka, Docker Compose with 9 containers) describes the target architecture this guide is built around. What's actually in the GitHub repo is a single Spring Boot modular monolith โ€” same ideas (JWT, RBAC, rules engine, Eureka, audit trail), way less infra to spin up. The steps below match the real, shipped code. ๐Ÿ“ฆ View on GitHub

What You Need

PartRuns onNeeds
Backend:8080Java 17, Maven 3.9+
UI:5173Node 18+, npm
Eureka server (optional):8761only if EUREKA_ENABLED=true

Step 1 โ€” Clone It

git clone https://github.com/hanyak10/FraudShield.git
cd FraudShield

Real layout you'll see on disk:

fraudshield/
โ”œโ”€โ”€ backend/                 Spring Boot 3.2 monolith (Java 17, H2, JWT, Eureka client)
โ”œโ”€โ”€ UI/                      React 18 + Vite analyst dashboard
โ”œโ”€โ”€ Postman/                 Importable Postman collection (auto-manages tokens)
โ”œโ”€โ”€ api-request-bodies.json  Postman-ready request-body reference
โ””โ”€โ”€ README.md                You are here

Step 2 โ€” Run the Backend

cd backend
mvn spring-boot:run
# Fresh, seeded H2 database every restart โ€” no setup needed.
# Swagger UI : http://localhost:8080/swagger-ui.html
# H2 console : http://localhost:8080/h2-console (jdbc:h2:mem:fraudshield, user sa, blank password)
# Health     : http://localhost:8080/actuator/health

Running standalone without a Eureka registry handy? Skip discovery so it doesn't retry connections forever:

# PowerShell
$env:EUREKA_ENABLED="false"; mvn spring-boot:run
# bash
EUREKA_ENABLED=false mvn spring-boot:run

Step 3 โ€” Run the UI

cd UI
npm install
npm run dev
# Opens http://localhost:5173 โ€” backend already allows CORS for this origin.

Step 4 โ€” Eureka Server (Optional)

The backend is a Eureka client; it expects a registry at http://localhost:8761/eureka/. Start any standard Eureka server on :8761 first if you want to see live service-discovery registration (the same check this guide's v1.1.0 upgrade verified) โ€” or just set EUREKA_ENABLED=false and skip it entirely.

Step 5 โ€” Log In

EmailPasswordRoleCan do
HKadmin@fraudshield.ioAdmin@123ROLE_ADMINEverything, incl. toggling rules
HKanalyst@fraudshield.ioAnalyst@123ROLE_ANALYSTReview + override verdicts
HKsuperadmin@fraudshield.ioSuperAdmin@123ROLE_SUPER_ADMINEverything

Prefer Postman?

Import Postman/FraudShield.postman_collection.json and start the backend. Running Auth โ†’ 1. Login (HK Admin) auto-saves accessToken/refreshToken as collection variables, and every other request attaches Authorization: Bearer {{accessToken}} for you โ€” no copy-pasting tokens by hand.

Mock vs. Real Data

The backend serves either seeded demo rows (mock) or only what you create at runtime (real). Force seeded data on any list call with a header:

curl -H "X-Mock-Response: true" http://localhost:8080/api/transactions
๐Ÿ›ก๏ธ

That's it. No Kafka cluster, no Postgres container, no nine-service orchestra to wrangle โ€” just mvn spring-boot:run, npm run dev, and a login screen. Go flag some fake fraud.


Reference

Glossary

TermDefinition
JWTStateless, signed token carrying claims. No DB lookup to validate โ€” just verify the signature.
Refresh TokenLong-lived single-use token (hashed in DB) to obtain new JWT pairs without re-login.
Token RotationEach refresh invalidates the old token and issues a new pair.
Token BlacklistRedis set of revoked JWT IDs (jti) with TTL. Honours logouts before natural expiry.
Kafka TopicNamed, ordered, append-only log of events. Producers write; consumer groups read.
Consumer GroupConsumers sharing a group ID. Kafka distributes partitions โ€” one per consumer max.
Dead Letter TopicTopic for messages that failed processing after all retries.
IdempotencyAn operation producing the same result no matter how many times applied.
Sliding WindowTime-based counter over a rolling period. More accurate than a fixed window.
RBACRole-Based Access Control. Permissions on roles; roles on users.
Circuit BreakerStops calling a failing service, lets it recover, resumes once confirmed.
CQRSCommand Query Responsibility Segregation. Separates write from read models.
BCryptPassword hashing with a cost factor. Cost 12 โ‰ˆ 250ms per hash.
HPAKubernetes Horizontal Pod Autoscaler. Scales on CPU or custom metrics.
jtiJWT ID claim. Unique per token. Used as the Redis blacklist key.
Versioning (X.Y.Z)X = major app feature release, Y = tech stack/framework upgrade, Z = patch or bug fix. E.g. v1.1.0 is a Y-level tech stack upgrade (Java 21 + Spring Boot 4.1).
Reference

FAQ

Why JWT and not server-side sessions?

Sessions require a shared store and a DB lookup per request. JWT is stateless โ€” the server only needs the secret key. You can scale to 100 instances with no shared auth state. The only shared state we add back is the Redis blacklist, a conscious tradeoff for logout support.

Why Kafka over RabbitMQ or REST calls?

Retention and replay. Kafka keeps messages 7 days regardless of consumption. If Rules Engine is down for an hour, every transaction is waiting when it recovers โ€” in order. RabbitMQ deletes on ACK. REST creates synchronous coupling.

How does the Rules Engine scale without double-processing?

Kafka consumer groups assign one partition to exactly one consumer, so instances partition the work without overlap. For rare redeliveries, the Redis idempotency key ensures double-processing is silently skipped.

What happens when an unauthenticated user hits a protected endpoint?

The JWT filter runs before any controller. With no valid token the SecurityContext stays anonymous and Spring Security returns 401 with structured JSON โ€” no stack trace. The request is logged with source IP.

Why BCrypt cost factor 12?

Cost is exponential. Cost 10 โ‰ˆ 75ms (too fast, GPU-brute-forceable). Cost 14 โ‰ˆ 1000ms (too slow under load). Cost 12 โ‰ˆ 250ms โ€” the sweet spot.

Why hash refresh tokens before storing?

If the DB is breached, plaintext refresh tokens let an attacker issue JWT pairs for every user. Hashing (SHA-256) gives the attacker only useless hashes. The raw token travels only over HTTPS.

How do you change fraud rules without redeploying?

Rules live in the fraud_rules table, not code. The Rules Engine re-queries every 60 seconds via a scheduled job. An analyst updates a threshold via the Admin API โ€” it takes effect within 60 seconds, no restart.

Can I replace H2 with PostgreSQL in dev?

Yes. Set the profile to dev, run docker-compose up postgres, update the DB env vars. JPA DDL auto-create handles schema generation in both โ€” the same entity classes work unchanged.


Reference

Version Control & Upgrade History

Release Timeline

VersionRelease DateChangesStatus
v1.0.0Initial ReleaseJava 17 LTS, Spring Boot 3.2, deterministic rules engine, JWT auth, Eureka service discovery, Redis cachingโš ๏ธ EOL
v1.1.0Q4 2026Java 21 LTS, Spring Boot 4.1, Spring Security 7, Spring Cloud 2025.1.4, comprehensive test suite, live Eureka registration verified, performance optimizations (virtual threads)โœ… Current

Why Upgrade to v1.1.0? (Security Patches & Rationale)

FraudShield moved from v1.0.0 to v1.1.0 purely to adopt the latest LTS (Long-Term Support) versions of its core stack. This is a Y-level (tech stack) upgrade โ€” no new fraud-detection features were added, but every dependency now sits on a newer, better-patched foundation.

Security Patches & Upgrades Applied

  • Java 17 โ†’ Java 21 LTS: Picks up 2+ years of JDK security patches and enables virtual threads for lighter-weight concurrency.
  • Spring Boot 3.2 โ†’ 4.1 (Spring Framework 7): Brings the latest CVE fixes bundled in Spring's 7.x security releases and moves fully to the Jakarta EE 11 baseline.
  • Spring Security 6 โ†’ 7: Ships hardened defaults for the security filter chain and drops legacy, exploit-prone configuration patterns (like .and() chaining).
  • Spring Cloud 2023.0.1 โ†’ 2025.1.4: Updates the Eureka client library to a release with patched service-discovery dependencies.
  • Jackson 2 โ†’ Jackson 3: Closes known deserialization-related vulnerabilities present in older Jackson 2.x minor releases.
โš ๏ธ

Why it matters: Running on an End-of-Life (EOL) Java 17 / Spring Boot 3.2 stack means no more security patches from upstream. Staying current keeps FraudShield protected against newly disclosed CVEs without waiting for a major feature release to justify the work.

v1.0.0 โ†’ v1.1.0 Migration Guide

Breaking Changes

  • Java 21 minimum requirement (was Java 17). Older JDKs are not supported.
  • Spring Framework 7 (from 6): Jakarta EE 11 baseline โ€” no more javax.* imports, all packages use jakarta.*.
  • Spring Security 7 (from 6): Lambda DSL required for security configuration; the .and() method chaining pattern is removed.
  • Spring Boot starter rename: spring-boot-starter-web is now spring-boot-starter-webmvc.
  • Jackson 3 default (from 2): Package namespace changed to tools.jackson.*.
  • Spring Cloud 2025.1.4 (from 2023.0.1): Ensure Eureka client version compatibility; the new release uses Spring Boot 4.1 as baseline.

Upgrade Checklist

  1. 1
    Update pom.xml dependencies: Change parent version from 3.2.5 to 4.1.2, java.version from 17 to 21, spring-cloud.version from 2023.0.1 to 2025.1.4, springdoc.version from 2.3.0 to 3.1.1. Rename spring-boot-starter-web to spring-boot-starter-webmvc.
  2. 2
    Compile and check errors: Run mvn clean compile. Compilation errors are usually minimal if code avoids deprecated APIs. Watch for import errors (javax โ†’ jakarta).
  3. 3
    Identify renamed config properties: Add spring-boot-properties-migrator temporarily to pom.xml, run mvn spring-boot:run, check logs for property warnings, then remove the dependency.
  4. 4
    Run full test suite: Execute mvn test. If tests exist, they validate the upgrade. If none exist, add smoke tests (e.g., context-load tests to verify all 50+ beans initialize).
  5. 5
    Boot and verify Eureka registration: Start both Eureka server and backend service. Check http://localhost:8761/eureka/apps to confirm the client instance shows status UP with the correct port and hostname.
  6. 6
    Verify all APIs: Test login (JWT token generation), token refresh, fraud rules engine scoring on seeded transactions, Swagger UI, and audit log endpoints.

What Was Tested in v1.1.0

  • โœ… Full application context loads โ€” all 50+ Spring beans, security filter chain, JPA repository auto-configuration, JWT filter, Eureka auto-config
  • โœ… Unit tests for rules engine โ€” 5 tests covering scoring logic, rule firing, velocity detection, amount anomaly, and edge cases
  • โœ… Live Eureka registration โ€” backend registers as FRAUDSHIELD-BACKEND:8080 with status UP and remains discoverable
  • โœ… JWT authentication flows โ€” login endpoint, token generation with correct expiry (15 min access, 7d refresh), token refresh with rotation, logout with blacklist
  • โœ… Fraud scoring on seeded data โ€” rules engine correctly identifies violations (velocity, amount anomaly, duplicates)
  • โœ… Database compatibility โ€” H2 in-memory database (no migration required, schema auto-created via JPA DDL)
  • โœ… Spring Security RBAC โ€” role enforcement on protected endpoints, 401/403 distinction
๐Ÿ’ก

Zero application code changes required. The upgrade is a pure dependency bump. FraudShield's application layer already avoids deprecated APIs and follows modern Spring patterns. This demonstrates mature, production-ready code that survives major framework upgrades without refactoring.

๐Ÿ”

Request Security Pipeline โ€” Live Walkthrough

Every HTTP request passes 9 security & processing gates. See what each checks, what fails it, and what gets through.

Gate 0
Step 1 of 10