diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c25ebdb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs. +# https://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space + +[*.java] +indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 + +[*.xml] +indent_size = 4 + +[*.json] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a0a80d2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: Payflow API CI Pipeline + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + name: Build, Quality Audit & Test + runs-on: ubuntu-latest + + steps: + - name: Checkout Source Code + uses: actions/checkout@v4 + + - name: Set up JDK 25 + uses: actions/setup-java@v4 + with: + java-version: '25' + distribution: 'temurin' + + - name: Cache Maven Dependencies + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Execute Maven Verification Pipeline (Format, Checkstyle, Build & Test) + run: mvn clean verify -B diff --git a/.gitignore b/.gitignore index 09c8bc4..3400fe9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ build/ ### VS Code ### .vscode/ + +### Internal docs (not committed) ### +internal/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bb24838 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +All notable changes to the Payflow API project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- + +## [Unreleased] + +### Added +- Project scaffolding: MIT `LICENSE`, `CHANGELOG.md`, `.editorconfig`. +- Architectural Decision Records log (`docs/ADR.md`) and engineering standards guide (`docs/CONVENTIONS.md`). +- Spotless code formatting plugin (`com.diffplug.spotless:spotless-maven-plugin`) integrated into Maven build. +- Checkstyle static analysis (`checkstyle.xml`) plugin (`maven-checkstyle-plugin`) enforcing coding rules on `validate` phase. +- GitHub Actions CI pipeline (`.github/workflows/ci.yml`) for automated build, linting, formatting, and test execution on Java 25. +- Repository contribution guidelines and branch protection rules (`CONTRIBUTING.md`). +- GitHub status badges in `README.md`. + +--- + +## [0.1.0] - 2026-07-27 + +### Added +- Baseline Phase 0 implementation. +- Basic User (`/users`) and Transaction (`/transactions`) REST endpoints. +- Spring Data JPA entities (`User`, `Transaction`) and repositories. +- In-memory H2 database persistence for local development builds. +- Initial Spring Boot 4.0.6 project configuration with Java 25. +- System Architecture documentation (`docs/ARCHITECTURE.md`), API Specification (`docs/API_SPECIFICATION.md`), and Phased Roadmap (`docs/ROADMAP.md`). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..793ff8c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,70 @@ +# Contributing to Payflow API + +Thank you for participating in the evolution of the Payflow API payment backend! + +This guide outlines our development workflow, pull request expectations, quality gates, and repository standards. + +--- + +## 1. Development Workflow + +1. **Fork & Clone**: Clone the repository locally. +2. **Branch Naming**: Create a topic branch following our convention: + - `feat/phase--` for new features + - `fix/` for bug fixes + - `docs/` for documentation updates +3. **Local Build & Quality Check**: + Before opening a PR, ensure code formatting, static analysis, and tests pass: + ```bash + mvn spotless:apply + mvn clean verify + ``` + +--- + +## 2. Code Quality Gates & Automated Checks + +Every pull request triggers the **GitHub Actions CI pipeline**, which enforces strict quality gates: + +- **Formatting Check (`Spotless`)**: Validates code layout against Eclipse/Java format rules (`mvn spotless:check`). +- **Static Analysis (`Checkstyle`)**: Ensures compliance with naming, import, and structural standards (`mvn checkstyle:check`). +- **Compilation & Test Suite**: Verifies clean compilation on Java 25 and executes all unit and integration tests. + +> **Note**: PRs with failing CI checks, unformatted code, or lint violations will not be merged. + +--- + +## 3. Conventional Commit Guidelines + +We enforce the [Conventional Commits](https://www.conventionalcommits.org/) specification for clean git history: + +- **Format**: `(): ` +- **Allowed Types**: + - `feat`: A new feature or endpoint implementation + - `fix`: A bug fix in existing logic + - `docs`: Documentation changes (`README`, architecture guide, specs) + - `style`: Formatting changes that do not affect code logic + - `refactor`: Code reorganization without functional changes + - `test`: Adding or refactoring tests + - `chore`: Updating dependencies, build scripts, or configuration +- **Examples**: + - `feat(domain): replace Double balance with BigDecimal in User entity` + - `fix(locking): sort user UPI IDs alphabetically to prevent deadlock` + +--- + +## 4. Pull Request Requirements + +- **Scoped Changes**: Keep PRs small and aligned with a single sub-phase (target: <500 net lines of code). +- **Documentation Parity**: If changing API contracts, business logic, or configuration parameters, update `README.md`, `docs/ARCHITECTURE.md`, and `docs/API_SPECIFICATION.md` within the same PR. +- **Changelog Entry**: Add a concise entry under `[Unreleased]` in `CHANGELOG.md`. + +--- + +## 5. Recommended Repository Branch Protection Rules + +For repository maintainers, configure GitHub branch protection for `main`: + +- **Require a pull request before merging**. +- **Require status checks to pass before merging**: Select `Build, Quality Audit & Test`. +- **Require linear history** (squash or rebase merging preferred). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..28f272d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Shashank Chandel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO QUALITY, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index ba5b653..96a6fa4 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,51 @@ # Payflow API — Backend Service -Payflow is a highly resilient, transactional payments backend built using **Java 25** and **Spring Boot 4.x**. The project is designed with a pluggable, cloud-flexible architecture optimized for local developer iteration, Kubernetes deployment, and resource-constrained environments (like AWS Free Tier). +

+ Build + Java 25 + Spring Boot + PostgreSQL + License: MIT + PRs Welcome +

+ +Payflow is a highly resilient, transactional payments backend built using **Java 25** and **Spring Boot 4.x**. The project is designed with a pluggable, cloud-flexible architecture optimized for local developer iteration, Kubernetes deployment, and resource-constrained environments (like AWS Free Tier and Oracle Cloud Always Free). + +--- + +## Current Status & Evolution Roadmap + +The project is currently evolving through a phased implementation roadmap from a Phase 0 baseline into an enterprise-grade payment system. + +- **Phase 0 (Completed)**: Baseline REST API for User & Transaction CRUD with H2 in-memory storage. +- **Phase 1 (In Progress)**: Project hygiene, static analysis (Spotless, Checkstyle), and GitHub Actions CI. +- **Target Phases**: PostgreSQL & Flyway migrations, pessimistic row locking with deadlock avoidance, durable idempotency engine, transactional outbox pattern, JWT security, Resilience4j fault tolerance, Redis caching & Redisson distributed locking, Kafka event streaming, and Gen-AI transaction insights. + +See the complete step-by-step evolution in **[Phased Roadmap](docs/ROADMAP.md)**. --- -## Technical Features +## Technical Architecture Highlights - **ACID Transaction Hardening**: Enforces sequential consistency under race conditions using database-level pessimistic locking (`SELECT FOR UPDATE`). -- **Distributed Locking Coordination**: Integrates **Redis Redlock** (via Redisson) to synchronize distributed triggers, preventing multi-instance concurrency collisions. -- **JPA N+1 Resolution**: Avoids Hibernate lazy loading bottlenecks through explicit **Fetch Joins** and `@EntityGraph` definitions to batch-load related entities. -- **REST API Versioning**: Implements strict URI version control (`/api/v1`) protecting endpoints from breaking database changes. -- **Gen-AI Spending Assistant**: Integrates **Spring AI** and a Generative AI LLM to automatically categorize transactions and generate spend analysis. +- **Distributed Locking Coordination**: Integrates **Redis Redlock** (via Redisson) to synchronize distributed triggers across multi-instance deployments. +- **JPA N+1 Resolution**: Avoids Hibernate lazy loading bottlenecks through explicit **Fetch Joins** and `@EntityGraph` definitions. +- **REST API Versioning**: Implements strict URI version control (`/api/v1`) protecting endpoints from breaking changes. +- **Gen-AI Spending Assistant**: Integrates **Spring AI** (Ollama / Groq / Gemini) to automatically categorize transactions and generate spend analysis. - **Durable Idempotency Engine**: Enforces exactly-once execution on mutation APIs via SHA-256 request payload hashing and persistent state checking. -- **Transactional Outbox Pattern**: Guarantees atomic consistency between state transitions and event streaming to Kafka without dual-write vulnerabilities. +- **Transactional Outbox Pattern**: Guarantees atomic consistency between state transitions and event streaming without dual-write vulnerabilities. - **Pluggable Resilience Policies**: Configured via Resilience4j for client-side rate-limiting, exponential backoff retries with jitter, and call timeouts. -- **Telemetry & Observability**: Integrated with Spring Boot Actuator, Prometheus metrics, and Logback MDC-based distributed trace correlation. +- **Telemetry & Observability**: Integrated with Spring Boot Actuator, Prometheus metrics, Logback MDC-based distributed trace correlation, and Grafana dashboards. --- ## Project Documentation Directory -For complete details on the architecture, evolution, and APIs, refer to the following documents: - -1. 📘 **[System Architecture Guide](docs/ARCHITECTURE.md)**: Deep technical details on locking, N+1 query resolution, API versioning, AI spends insights, Spring profiles, and observability. +1. 📘 **[System Architecture Guide](docs/ARCHITECTURE.md)**: Deep technical details on topology, locking, idempotency, Spring profiles, and observability. 2. 🗓️ **[Phased Implementation Roadmap](docs/ROADMAP.md)**: Subdivided step-by-step phases (A and B) spanning database migrations, security, event streaming, and deployment. 3. 🌐 **[REST API Specification](docs/API_SPECIFICATION.md)**: Complete request and response payload schemas, versioned endpoints, validations, status codes, and RFC 7807 error formats. +4. 📋 **[Engineering Conventions](docs/CONVENTIONS.md)**: Coding standards, Git commit conventions, PR guidelines, and test naming rules. +5. 📜 **[Architecture Decision Records](docs/ADR.md)**: Log of architectural decisions, context, trade-offs, and consequences. --- @@ -32,17 +53,17 @@ For complete details on the architecture, evolution, and APIs, refer to the foll The application leverages profile isolation to adapt resources to the target environment dynamically: -- **`local` (Default)**: In-memory H2 database, automated hibernate schema updates, and mock environments. Excellent for standalone, dependency-free code exploration. -- **`test`**: Active during JUnit execution, utilizing **Testcontainers** to dynamically boot local Postgres/Kafka container instances for validation. +- **`local` (Default)**: In-memory H2 database, automated hibernate schema updates, and zero-dependency local startup. +- **`test`**: Active during integration tests, utilizing **Testcontainers** to dynamically boot local Postgres/Kafka container instances for validation. - **`prod`**: Production-grade settings. Database credentials and broker addresses are read from system environment variables. Schema evolution is strictly managed by **Flyway migrations**. -- **`prod-light` (AWS Free Tier Optimization)**: Tailored for a single constrained AWS virtual server (1 GiB RAM). Plugs out heavy Redis/Kafka containers and replaces them with lightweight in-memory fallbacks to prevent Out-Of-Memory (OOM) failures. +- **`prod-light` (Resource Constrained / Free Tier)**: Memory-optimized profile for single constrained virtual servers (1 GiB RAM). Uses lightweight in-memory fallbacks for Redis and Kafka to prevent OOM failures. --- ## Quick Start ### 1. Build and Run Local Tests -Requires JDK 25 and Maven. This will trigger integration tests using Testcontainers (if a Docker daemon is active) or unit tests. +Requires JDK 25 and Maven: ```bash mvn clean install mvn test @@ -58,11 +79,10 @@ mvn spring-boot:run - *Credentials*: `user` / `user` ### 3. Local Full-Stack Ecosystem Startup (Docker Compose) -To run a realistic local staging environment containing Postgres, Redis, Kafka KRaft, Prometheus, and Grafana: +To run a local staging environment containing Postgres, Redis, Kafka KRaft, Prometheus, and Grafana: ```bash docker-compose up --build ``` -The Spring Boot instance connects to the docker network using environment variables specified in the compose file. --- @@ -75,10 +95,10 @@ kubectl apply -f k8s/ ``` - **Liveness Probe**: Routed to `/actuator/health/liveness` - **Readiness Probe**: Routed to `/actuator/health/readiness` -- **Graceful Shutdown**: Handled via `server.shutdown=graceful` which coordinates with the container orchestration to process in-flight transactions before terminating the pod container. +- **Graceful Shutdown**: Handled via `server.shutdown=graceful` coordinating with container orchestration to process in-flight transactions before pod termination. ### GraalVM Native Compilation -To compile the application to a lightweight, platform-native binary that starts in milliseconds and consumes only ~30-50MB of RAM: +To compile the application to a lightweight, platform-native binary that starts in milliseconds and consumes ~30-50MB of RAM: ```bash # Compile native binary locally mvn -Pnative native:compile @@ -86,4 +106,9 @@ mvn -Pnative native:compile # Or build an optimized native OCI image via Buildpacks mvn -Pnative spring-boot:build-image ``` -*Note: GraalVM native compiler tools must be installed locally on the host.* + +--- + +## License + +This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details. diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..7ea9e08 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/ADR.md b/docs/ADR.md new file mode 100644 index 0000000..2e3d2ec --- /dev/null +++ b/docs/ADR.md @@ -0,0 +1,45 @@ +# Payflow API — Architecture Decision Records (ADRs) + +This document serves as the log of all architectural decisions made during the evolution of the Payflow API payment backend. + +ADRs document significant technical decisions, along with their context, rationale, trade-offs, and consequences. + +--- + +## ADR Index + +| ADR # | Title | Date | Status | +| :--- | :--- | :--- | :--- | +| *No ADRs recorded yet. ADRs will be appended as features are implemented.* | | | | + +--- + +## ADR Template + +When creating a new Architecture Decision Record, use the following template: + +```markdown +### ADR-XXX: [Short Title of Decision] + +**Date**: YYYY-MM-DD +**Status**: [Proposed | Accepted | Superseded | Deprecated] +**Phase**: [Phase X] + +#### Context & Problem Statement +What is the technical problem we are solving? What constraints or requirements impact this decision? + +#### Considered Options +1. **Option 1**: Description +2. **Option 2**: Description + +#### Decision Outcome +Chosen Option: **[Option Name]** + +##### Rationale +Why was this option chosen over the alternatives? + +#### Consequences +- **Positive**: What benefits do we gain? +- **Negative / Trade-offs**: What challenges, overhead, or limitations are introduced? +- **Risks & Mitigations**: How do we mitigate negative impacts? +``` diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 7b95dd1..85413af 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -75,6 +75,15 @@ When transaction logic spans distributed systems (such as reserve balance operat - **Use Case**: Locking a payment request before database transactions begin, preventing thundering herds and duplicate submission processing at the gateway container boundaries. - **Failover**: Configured with a short lease time (TTL) to prevent permanent resource locking if a pod node crashes during transaction execution. +### C. Append-Only Ledger Immutability +All transaction entries are treated as immutable, append-only logs. Once a transaction is successfully written, it is never modified or deleted. Any adjustments, reversals, or refunds are executed by writing a *new* transaction entry of type `REFUND`, preserving the complete historical audit trail. + +### D. Database Indexing Strategy +To ensure high database read throughput and statement generation speed, the following index constraints are established in migrations: +- `CREATE INDEX idx_tx_sender_created ON transactions(sender_upi_id, created_at DESC);` +- `CREATE INDEX idx_tx_receiver_created ON transactions(receiver_upi_id, created_at DESC);` +These composite indexes optimize transaction statement history pages and eliminate expensive full-table scans. + --- ## 4. JPA N+1 Query Resolution @@ -185,17 +194,19 @@ To support smart financial features, the project includes an **AI spend assistan --- -## 9. Resilience Policies +## 9. Resilience Policies & Thread Tuning System stability under load is enforced using **Resilience4j** configurations: * **Rate Limiting**: Enforced at the controller layer via a Redis-backed Token Bucket algorithm. Limits mutations to prevent denial-of-service attempts. -* **Connection & Read Timeouts**: Explicit timeouts configured on the HTTP clients and database connections to prevent thread pool depletion: +* **Connection & Read Timeouts**: Explicit timeouts configured on the HTTP clients and database connections to prevent thread pool depletion. +* **Retries & Backoff**: Outbound requests (e.g. to third-party banking processors) are wrapped in Resilience4j Retry policies using **exponential backoff with random jitter** to prevent thundering herd requests on recovering downstream hosts. +* **Virtual Threads Integration (Project Loom)**: + Java 25 virtual threads are enabled to handle high request-response volumes: ```properties - spring.datasource.hikari.connection-timeout=5000 # 5 seconds max connection wait - spring.datasource.hikari.maximum-pool-size=20 # Sized relative to virtual threads + spring.threads.virtual.enabled=true ``` -* **Retries & Backoff**: Outbound requests (e.g. to third-party banking processors) are wrapped in Resilience4j Retry policies using **exponential backoff with random jitter** to prevent thundering herd requests on recovering downstream hosts. + Since virtual threads do not block OS kernel threads, throughput scales efficiently. However, to prevent database pool exhaustion, HikariCP's maximum pool size must be explicitly configured and aligned with Postgres threshold capabilities (e.g., `maximum-pool-size=20`). --- @@ -218,3 +229,40 @@ The application complies with cloud-native deployment requirements when running * **Log Correlation (MDC)**: The logger is configured with a standard pattern using Spring Boot's Mapped Diagnostic Context (MDC). Every log line includes `traceId`, `spanId`, and `userId` context, enabling automated log correlation in tracing systems. * **Prometheus Metrics**: Actuator exposes metrics under `/actuator/prometheus`, providing telemetry on transaction volumes, payment failures, JVM garbage collection, and database connection pool saturation. + +--- + +## 12. Testing Strategy (Rigor, Concurrency & Unit Verification) + +To ensure maximum code coverage and high system reliability, the project defines a two-tier testing strategy consisting of isolated unit tests and full-stack integration tests. + +### A. Isolated Unit Testing Strategy +Unit tests focus on isolating individual components and verifying business logic without booting the database or messaging middleware: + +1. **Controller Layer (MockMVC)**: + - Evaluates HTTP serialization, URL routing, request DTO validation constraints (e.g. invalid UPI patterns, blank fields), and custom error mapping to RFC 7807 payloads. + - Tested using Spring's `@WebMvcTest` paired with `@MockBean` (or `@MockitoBean` in newer Spring Boot releases) to stub the service layers, ensuring lightning-fast execution. +2. **Service Layer (Mockito)**: + - Validates business rules, balance invariant checking, and custom exceptions throwing (e.g., `UserNotFoundException` or `InsufficientBalanceException`). + - Uses Mockito annotations (`@ExtendWith(MockitoExtension.class)`) to isolate business service operations from Spring lifecycle overhead. +3. **Repository Layer (DataJpaTest)**: + - Confirms that custom derived queries or complex JPQL Fetch Joins compile and execute successfully. + - Executed via `@DataJpaTest` running against an embedded H2 database instance. +4. **Mocking External Services**: + - Outbound REST endpoints, the AI model assistant API, and Kafka brokers are mocked or stubbed during unit verification to prevent flaky test execution and external network dependence. + +### B. Advanced Integration Testing (Rigor & Concurrency Verification) +Integration tests verify the full lifecycle of a transaction across actual container dependencies using **Testcontainers** to orchestrate PostgreSQL and Kafka instances during Maven build phases: + +1. **Concurrent Race Condition Testing**: + - To verify pessimistic row lock deadlocks and double-spend safety under high traffic contention: + - Initialize an `ExecutorService` with a thread pool (size: 10) and a `CountDownLatch` set to 10. + - Fire 10 concurrent threads at the same millisecond to withdraw $100 from a sender account holding $150. + - **Assertion**: Only 1 transaction commits successfully, 9 threads fail with validation or locking errors, and the final account balance is exactly $50. +2. **Idempotency Key Lock Contention Testing**: + - To verify that gateway-level locks block dual-processing on duplicate submissions: + - Spin up two concurrent threads executing a transaction using the same `Idempotency-Key` and request payload. + - **Assertion**: Thread 1 succeeds; Thread 2 is immediately rejected with a `409 Conflict` (custom `IdempotentRequestProcessingException`) without starting any database transaction, proving the gateway Redis lock isolated the duplicate execution path. +3. **Asynchronous Outbox Publisher Testing**: + - Confirms the outbox poller successfully dispatches records to Kafka. + - **Assertion**: Write an outbox record, wait for the scheduled poller execution, read the event from the Testcontainers Kafka consumer, and verify the message matches the expected transaction schema. diff --git a/docs/CONVENTIONS.md b/docs/CONVENTIONS.md new file mode 100644 index 0000000..ffecf43 --- /dev/null +++ b/docs/CONVENTIONS.md @@ -0,0 +1,68 @@ +# Payflow API — Engineering Conventions & Standards + +This document outlines the coding standards, repository conventions, Git workflow rules, testing guidelines, and PR expectations for the Payflow API project. + +--- + +## 1. Code Style & Formatting + +- **Java Version**: Java 25. +- **Formatter**: Google Java Format, enforced via the `spotless-maven-plugin`. +- **Linter**: Checkstyle, enforced via `maven-checkstyle-plugin`. +- **Indentation**: 4 spaces for Java, 2 spaces for YAML/JSON, 4 spaces for XML. +- **Imports**: Group imports logically; avoid wildcard imports (`import java.util.*`) except in test files where approved. +- **Naming Conventions**: + - `PascalCase` for classes, interfaces, enums, annotations. + - `camelCase` for variables, method names, fields, parameter names. + - `UPPER_SNAKE_CASE` for `public static final` constants and enum values. + +--- + +## 2. Architecture & Design Standards + +- **Constructor Injection**: Always use constructor injection for Spring beans. Field injection via `@Autowired` is prohibited. +- **DTO Separation**: Database entities (`@Entity`) must never be exposed directly via REST controllers. Use DTOs for request input and Java `record`s for API response models. +- **Immutability**: Prefer immutable data structures. Response DTOs should use Java `record`s where possible. +- **Financial Arithmetic**: Never use `double` or `float` for monetary calculations. Always use `BigDecimal` with explicit scale and `RoundingMode.HALF_EVEN` (banker's rounding). +- **Error Responses**: All API errors must return standardized RFC 7807 `ProblemDetail` payloads via `@RestControllerAdvice`. +- **Logging**: Never use `System.out.println()`. Use SLF4J loggers (`log.info()`, `log.warn()`, `log.error()`). + +--- + +## 3. Git Workflow & Commit Messages + +- **Branch Naming**: `feat/phase--`, `fix/`, `docs/`. +- **Commit Message Format**: Follow Conventional Commits: + ```text + (): + + [optional body explaining rationale] + ``` + - **Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `ci`. + - **Example**: `feat(domain): replace Double balance with BigDecimal in User entity` +- **PR Size**: Keep PRs small and scoped to a single sub-phase (target: <500 lines of code changed). + +--- + +## 4. Testing Conventions + +- **Unit Tests**: + - Located in `src/test/java/...`. + - Class naming: `[ClassName]Test.java` (e.g., `TransactionServiceTest.java`). + - Use Mockito for dependencies and MockMVC for controller slice tests (`@WebMvcTest`). + - Must run quickly without requiring Docker or external services. +- **Integration Tests**: + - Located in `src/test/java/.../integration/`. + - Class naming: `[Feature]IT.java` or `[ClassName]IntegrationTest.java`. + - Use Testcontainers to spin up real PostgreSQL/Kafka containers. +- **Test Method Naming**: Use descriptive names reflecting intent: + - `should[ExpectedBehavior]_when[StateUnderTest]()` + - Example: `shouldRejectTransfer_whenSenderHasInsufficientBalance()` + +--- + +## 5. Documentation Standards + +- **Parity**: Every feature implemented in code must be documented in `README.md`, `docs/ARCHITECTURE.md`, and `docs/API_SPECIFICATION.md`. +- **ADRs**: Any major architectural decision (e.g., locking strategy, caching library choice) must be recorded in `docs/ADR.md`. +- **Changelog**: Every merged PR must include an entry in `CHANGELOG.md` under `[Unreleased]`. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index fecc34f..26dcb22 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,12 +1,22 @@ # Payflow API — Roadmap -This roadmap outlines a structured, phased progression to evolve the Payflow API from a phase 0 baseline into a highly resilient, enterprise-grade transactional backend. +This roadmap outlines a structured, phased progression to evolve the Payflow API from a Phase 0 baseline into a highly resilient, enterprise-grade transactional backend. -Each major phase is subdivided into two iterative sub-phases (A and B) to support manageable implementation steps. +Each major phase is subdivided into iterative sub-phases scoped to a **single pull request**. Sub-phases are designed to be independently mergeable, buildable, and testable. Every sub-phase PR includes relevant documentation updates, diagrams, and testing to maintain a green build at all times. + +### Design Principles +- **Free-tier first**: All infrastructure defaults to free, open-source options. Paid services are activated via profile-driven configuration changes only. +- **Documentation parity**: Every documented feature has corresponding implemented and tested code. No aspirational claims. +- **PR-sized increments**: Each sub-phase is a single, reviewable pull request. + +### Status Legend +- ✅ Complete +- 🔄 In Progress +- ⬜ Not Started --- -## Phase 0 — Baseline implementation (Current Status) +## Phase 0 — Baseline Implementation ✅ *Goal: Establish a running transactional baseline.* @@ -18,132 +28,265 @@ Each major phase is subdivided into two iterative sub-phases (A and B) to suppor --- -## Phase 1 — Database & ACID Hardening +## Phase 1 — Project Hygiene & Dev Tooling ✅ + +*Goal: Establish professional project scaffolding, code quality enforcement, and continuous integration before any feature work begins.* + +### Phase 1A — Project Scaffolding & Repository Standards ✅ + +#### Work Items +- Add MIT license file and initialize a changelog following Keep a Changelog format. +- Add `.editorconfig` enforcing consistent formatting rules across IDEs. +- Create Architecture Decision Records (ADR) log to document key technical decisions. +- Create engineering conventions document (coding standards, commit message format, PR guidelines). +- Add GitHub badges to README (CI status, Java version, Spring Boot version, license, code coverage). +- Harden `.gitignore` for comprehensive IDE/OS artifact exclusion. + +### Phase 1B — Code Formatting & Static Analysis ✅ + +#### Work Items +- Integrate Spotless plugin (Eclipse Java Format) for automated code formatting. +- Integrate Checkstyle plugin with a baseline rule configuration (`checkstyle.xml`). +- Format all existing source files and bind checks to the Maven validate phase. + +### Phase 1C — GitHub Actions CI Pipeline ✅ + +#### Work Items +- Create a CI workflow triggered on push and pull request events. +- Configure JDK setup, dependency caching, and full Maven verify execution. +- Document branch protection recommendations in `CONTRIBUTING.md`. + +--- + +## Phase 2 — Domain Model & API Contract Hardening ⬜ + +*Goal: Transform bare JPA entities into a properly modeled domain with validated DTOs, clean error handling, and versioned API routes.* + +### Phase 2A — Entity Model Hardening ⬜ + +#### Work Items +- Replace `Double` with `BigDecimal` for all monetary fields (financial precision). +- Add audit timestamps, transaction status/type enums, and reference IDs. +- Adopt Lombok to eliminate boilerplate. Add JPA `@Version` for optimistic locking support. +- Refactor all field injection to constructor injection. + +### Phase 2B — DTO Layer, Input Validation & API Documentation ⬜ + +#### Work Items +- Create request DTOs with Jakarta validation annotations and response DTOs using Java records. +- Update controllers to accept validated DTOs and return response objects (never raw entities). +- Establish `/api/v1/` versioned route prefix on all endpoints. +- Return correct HTTP status codes (`201 Created`, `404 Not Found`, etc.). +- Integrate `springdoc-openapi` for auto-generated interactive Swagger UI and OpenAPI 3.0 specification. + +### Phase 2C — Error Handling & Exception Framework ⬜ + +#### Work Items +- Create domain-specific exception hierarchy (e.g., not found, insufficient balance, duplicate UPI, self-transfer). +- Implement a global exception handler using Spring's native RFC 7807 `ProblemDetail` support. +- Replace all `System.out.println` usage with SLF4J structured logging. + +--- + +## Phase 3 — ACID Transaction Logic & Concurrency Control ⬜ + +*Goal: Implement real money transfer mechanics with ledger integrity guarantees under concurrent load.* + +### Phase 3A — Money Transfer & Refund Implementation ⬜ + +#### Work Items +- Implement end-to-end transfer orchestration: validate participants, check balance, debit/credit, persist transaction record — all within a single `@Transactional` boundary. +- Implement transaction refund/reversal endpoint following append-only ledger semantics. +- Add transaction status lookup and paginated user transaction history endpoints. + +### Phase 3B — Pessimistic Locking & Deadlock Avoidance ⬜ + +#### Work Items +- Add pessimistic write lock (`SELECT ... FOR UPDATE`) on balance lookups during transfers. +- Implement deterministic lock ordering (alphabetical UPI ID sort) to prevent deadlock cycles. +- Enforce append-only ledger semantics on the transactions table. +- Resolve JPA N+1 query patterns using `@EntityGraph` and explicit fetch joins. + +--- + +## Phase 4 — Database Migration & Profile Infrastructure ⬜ + +*Goal: Replace volatile H2 with PostgreSQL, establish Flyway-managed schema evolution, and configure environment-specific profiles.* + +### Phase 4A — Flyway Migrations & PostgreSQL Integration ⬜ -### Phase 1A — Schema Migration & Persistence Foundation -*Goal: Replace volatile H2 memory DB with PostgreSQL and optimize Hibernate queries.* +#### Work Items +- Add PostgreSQL driver and Flyway dependencies. +- Create versioned SQL migration scripts for users, transactions, and performance indexes. +- Restructure configuration from `.properties` to YAML format. + +### Phase 4B — Spring Profiles & Testcontainers Setup ⬜ #### Work Items -- **Dependency Integration**: Add PostgreSQL driver and Flyway migration core dependencies to `pom.xml`. -- **Flyway Database Migrations**: Create initial schema SQL migrations (`V1__init_schema.sql` for users and transactions tables) inside `src/main/resources/db/migration`. -- **Integration Testing (Testcontainers)**: Setup Testcontainers to dynamically boot isolated PostgreSQL containers during the Maven test phase. -- **Spring Profile Configuration**: Ensure distinct configs: - - `local`: Point to H2 or a local Postgres container. - - `test`: Configure dynamic testcontainers JDBC URLs. - - `prod`: Externalize credentials using environment variables. -- **JPA N+1 Resolution**: Configure `@EntityGraph` or explicit JPQL `JOIN FETCH` queries on repositories to ensure related transaction collections are resolved in a single SQL join rather than N+1 queries. +- Create profile-specific configuration files (`local`, `test`, `prod`). +- Add Testcontainers dependencies and create a shared integration test base class. +- Enforce Flyway-only schema control in production (`ddl-auto=validate`). + +--- + +## Phase 5 — Testing ⬜ -#### Acceptance Criteria -- Running `mvn clean test` boots a real PostgreSQL instance via Testcontainers and successfully executes tests. -- Flyway migrations apply cleanly upon application startup in all profiles. -- Hibernate query execution logs show a single query instead of N+1 SELECT statements when retrieving a user's transaction list. +*Goal: Establish comprehensive test coverage with unit, integration, and concurrency verification.* -### Phase 1B — Concurrency Control & Idempotency Engine -*Goal: Guarantee ledger consistency under race conditions and network retry duplication.* +### Phase 5A — Unit Tests ⬜ #### Work Items -- **Pessimistic Row Locking**: Add `@Lock(LockModeType.PESSIMISTIC_WRITE)` to query methods in `UserRepository` for sender and receiver lookup. -- **Transactional Atomicity**: Configure `@Transactional(isolation = Isolation.READ_COMMITTED)` across services, sorting lock acquisition alphabetically by UPI ID to avoid deadlocks. -- **Persistent Idempotency Registry**: - - Write Flyway migration for `idempotency_registry` table. - - Implement an idempotency validator interceptor calculating request payload hashes (SHA-256). - - Implement logic to intercept duplicate keys, serving cached responses for completed actions and rejecting active in-flight duplicates (`409 Conflict`). +- Service layer tests using Mockito: transfer happy path, edge cases, refund scenarios. +- Controller layer tests using `@WebMvcTest` + MockMVC: input validation, status codes, error payloads. +- Repository layer tests using `@DataJpaTest`: custom query verification. -#### Acceptance Criteria -- Concurrent requests seeking to transfer balance from the same source user concurrently fail gracefully, maintaining positive balances. -- Duplicate POST requests using the same `Idempotency-Key` header return cached results without executing the transaction twice. +### Phase 5B — Integration & Concurrency Tests ⬜ + +#### Work Items +- Full transfer lifecycle tests against Testcontainers PostgreSQL. +- Concurrent race condition test: 10 simultaneous threads contending on the same balance, verifying exactly 1 succeeds and no double-spend occurs. +- Update CI pipeline to run integration tests. --- -## Phase 2 — Platform Security & Resilience +## Phase 6 — Idempotency & Transactional Outbox ⬜ + +*Goal: Guarantee exactly-once mutation execution and reliable event streaming without dual-write vulnerabilities.* + +### Phase 6A — Durable Idempotency Engine ⬜ + +#### Work Items +- Create idempotency registry schema (Flyway migration), entity, and repository. +- Implement a request filter that intercepts `Idempotency-Key` headers, computes payload hashes (SHA-256), and replays cached responses for duplicate requests. +- Handle concurrent duplicate submissions with `409 Conflict`. -### Phase 2A — JWT Authentication & Security -*Goal: Restrict mutation endpoints to authenticated actors and establish API versioning.* +### Phase 6B — Transactional Outbox Pattern ⬜ #### Work Items -- **API Versioning**: Map all controllers under versioned URL paths (e.g. `@RequestMapping("/api/v1/...")`), establishing a clear route namespace. -- **Spring Security Configuration**: Introduce Spring Security and block anonymous writes. -- **Stateless JWT Validation**: Create filters parsing Authorization headers and checking cryptographically signed JWT keys. -- **Secure Endpoints**: Restrict `POST /api/v1/transactions` so a user can only initiate transfers where the sender UPI matches their authenticated subject claim. +- Create outbox events schema (Flyway migration), entity, and repository. +- Write outbox events atomically within the same transaction as balance updates. +- Implement a scheduled dispatcher that polls and publishes pending events. +- Define a pluggable event publisher interface with an in-memory default implementation. + +--- -#### Acceptance Criteria -- Requests without a valid `Bearer JWT` token to transactions endpoints return `401 Unauthorized`. -- Authenticated users cannot trigger transfers masquerading as other users. -- Old endpoints are gracefully routed and version prefixes are strictly enforced. +## Phase 7 — Security & Access Control ⬜ -### Phase 2B — Distributed Caching & Resilience Policies -*Goal: Optimize query pathways, prevent OOMs on free instances, and implement distributed locks.* +*Goal: Restrict mutation endpoints to authenticated actors using stateless JWT validation.* + +### Phase 7A — Spring Security & JWT Authentication ⬜ #### Work Items -- **Redis Integration**: Add Redis dependencies and configure a local connection pool. -- **Cache-Aside Pattern**: Cache user profiles by UPI ID on read requests (`GET /api/v1/users/upi/{upiId}`), with automatic cache eviction/invalidation on user updates. -- **Redis Distributed Locking**: Integrate Redisson for cross-instance locking. Apply a Redis-backed lock on the `Idempotency-Key` at the gateway layer to prevent multiple servers from concurrently starting database transactions on the same key. -- **Resilience4j Policies**: - - Configure rate-limiting (Token Bucket) on transaction endpoints. - - Apply call timeouts on database locks and external integrations. - - Implement call retries with exponential backoff and random jitter for outbound message attempts. +- Integrate Spring Security with stateless JWT-based authentication. +- Create a JWT token provider and authentication filter. +- Configure endpoint security: permit public registration, authenticate transaction mutations. +- Configure CORS policy for frontend integration readiness. +- Add a simplified auth/login endpoint for token issuance. + +### Phase 7B — Authorization & Sender Verification ⬜ -#### Acceptance Criteria -- Reading a user profile by UPI hits the database once; subsequent calls load the profile from Redis. -- Gateway logs show concurrent duplicate requests hitting different nodes block on the Redis lock, preventing concurrent DB inserts. -- Flooding the transactions endpoint beyond rate-limit constraints returns `429 Too Many Requests`. +#### Work Items +- Verify that the authenticated user's identity matches the sender in transfer requests (`403 Forbidden` on mismatch). +- Restrict transaction history access to the owning user. +- Add comprehensive security-focused unit tests. --- -## Phase 3 — Event-Driven Telemetry & Orchestration +## Phase 8 — Observability, Resilience, Caching & Event Streaming ⬜ + +*Goal: Add structured telemetry, fault tolerance policies, distributed caching, and production event streaming.* + +### Phase 8A — Structured Logging, Tracing & Prometheus Metrics ⬜ + +#### Work Items +- Configure JSON-structured logging with MDC trace correlation (traceId, spanId, requestId). +- Add distributed tracing support with Micrometer Tracing for cross-service trace propagation. +- Expose custom Micrometer counters for payment volumes, failure rates, and amount distributions. +- Harden actuator endpoint exposure. + +### Phase 8B — Resilience4j Policies ⬜ + +#### Work Items +- Add rate limiting (token bucket) on transaction endpoints with `429 Too Many Requests`. +- Add retry with exponential backoff and jitter on outbox event dispatch. +- Add timeout policies on lock acquisition and external calls. +- Export resilience metrics to Prometheus. + +### Phase 8C — Distributed Caching & Locking (Redis) ⬜ + +#### Work Items +- Integrate Redis with Spring Cache abstraction for cache-aside pattern on user lookups. +- Automatic cache eviction on writes with `@CacheEvict`. +- Enhance idempotency filter with Redis-backed distributed locking (Redisson) for multi-instance gateway coordination. +- Profile-conditional bean wiring: Redis for `prod`, Caffeine for `local`/`test`. +- Add cache hit/miss metrics to Prometheus. -### Phase 3A — Asynchronous Streaming (Kafka & Outbox) -*Goal: Broadcast transaction events consistently without distributed dual-write bugs.* +### Phase 8D — Kafka Integration & Event Streaming ⬜ #### Work Items -- **Transactional Outbox Table**: Write Flyway migration creating the `outbox_events` registry table. -- **Atomicity Integration**: Modify transaction services to write the event to the outbox table inside the same balance-updating database transaction. -- **Scheduled Dispatcher**: Implement a lightweight scheduled background worker reading `PENDING` outbox logs, publishing them to a Kafka broker, and updating their status to `DISPATCHED`. -- **Kafka Local Setup**: Integrate local Kafka configs utilizing KRaft mode (ZooKeeper-less setup). +- Implement a Kafka-backed event publisher (activated in `prod` profile). +- Configure Kafka producer with durability guarantees (`acks=all`). +- Add Testcontainers Kafka integration tests for end-to-end event verification. + +--- + +## Phase 9 — Infrastructure, Docker & Deployment ⬜ + +*Goal: Production-grade containerization, local full-stack orchestration, Kubernetes readiness, and cloud deployment.* -#### Acceptance Criteria -- When a transaction commits, an outbox entry is atomically written. -- The background poller successfully dispatches the outbox event payload to the target Kafka topic. +### Phase 9A — Multi-Stage Dockerfile & Docker Compose ⬜ -### Phase 3B — Local Ecosystem & Observability -*Goal: Run the entire stack locally with telemetry monitoring.* +#### Work Items +- Rewrite Dockerfile with multi-stage build, correct JDK version, non-root user, and health check. +- Expand Docker Compose to orchestrate: application, PostgreSQL, Redis, Kafka, Prometheus, and Grafana. +- Add monitoring configuration (Prometheus scrape config, Grafana provisioned dashboards). + +### Phase 9B — Kubernetes Manifests ⬜ #### Work Items -- **Docker Compose Orchestration**: Develop a `docker-compose.yml` that maps the Spring Boot application container, PostgreSQL, Redis, Kafka, Prometheus, and Grafana. -- **Log Correlation (MDC)**: Update Logback patterns to inject Trace IDs and Span IDs into all standard console/file logs. -- **Prometheus Metrics**: Expose custom Micrometer gauges for successful payment counts, latency, and DB pool saturation to the `/actuator/prometheus` endpoint. +- Create Deployment, Service, ConfigMap, and Secret manifests. +- Configure liveness/readiness probes and graceful shutdown alignment. +- Validate deployment on a local Minikube/Kind cluster. -#### Acceptance Criteria -- Running `docker-compose up` launches all services cleanly. -- Logs include uniform trace metadata, and metrics are queryable inside the local Prometheus server. +### Phase 9C — CI/CD Enhancement & Code Coverage ⬜ + +#### Work Items +- Add SpotBugs static analysis, artifact archiving, and dependency caching to the CI workflow. +- Integrate JaCoCo for code coverage reporting with minimum threshold enforcement. +- Add CI status and code coverage badges to README. --- -## Phase 4 — AI Integrations, CI/CD, & Cloud +## Phase 10 — Gen-AI Spend Insights ⬜ + +*Goal: Integrate LLM-powered transaction categorization and budgeting insights.* -### Phase 4A — Gen-AI Spend Insights & CI/CD Pipelines -*Goal: Enforce build quality and integrate LLM spend analytics.* +### Phase 10A — Spring AI Integration & Categorization Endpoint ⬜ #### Work Items -- **Spring AI Integration**: Add Spring AI dependencies to `pom.xml` configured for the selected LLM provider. -- **AI Spend Categorization**: Implement `POST /api/v1/transactions/{id}/insights` to send transaction notes and values to the LLM provider, requesting spend classification and structured budgeting insights using a custom system prompt. -- **GitHub Actions Pipeline**: Create a workflow triggered on commits and pull requests that compiles code, runs Checkstyle, executes SpotBugs vulnerability linter, and runs tests. +- Integrate Spring AI with a provider-agnostic abstraction (Ollama for local, Groq/Gemini for deployed). +- Implement a spend insights service with structured prompt engineering and JSON-schema output parsing. +- Add authenticated insights endpoint with circuit breaker fallback. +- Feature-flag AI capabilities for environments without API keys. -#### Acceptance Criteria -- The AI insights endpoint returns a structured JSON payload containing spend categories (e.g. `Transport`, `Food`) and budget advice from the AI model. -- Code changes cannot merge without passing checkstyle rules, SpotBugs analysis, and all integration tests in the GitHub Actions runner. +--- + +## Phase 11 — Production Hardening & Documentation ⬜ + +*Goal: Final polish for production readiness, cloud deployment, and professional presentation.* -### Phase 4B — Kubernetes & Cloud Orchestration -*Goal: Ensure seamless migration to production cloud clusters.* +### Phase 11A — prod-light Profile, Virtual Threads & GraalVM ⬜ #### Work Items -- **Kubernetes Manifests**: Develop native YAML configurations: - - `Deployment` (with CPU/Memory resources, liveness/readiness probes, and graceful shutdown settings). - - `Service` (ClusterIP). - - `ConfigMap` & `Secret` (decoupling environment variables). -- **Graceful Pod Lifecycle**: Configure Spring properties to allow graceful request drains (`server.shutdown=graceful`) mapping to Pod execution properties. -- **IaC Architectural Guidelines**: Document Terraform templates and AWS deployment guidelines (managed database, cache, event broker, and container orchestration services) in the architecture guidelines. +- Create a memory-optimized profile for resource-constrained deployments (1 GiB RAM). +- Swap Redis/Kafka with lightweight in-memory alternatives using conditional bean wiring. +- Enable virtual threads (`spring.threads.virtual.enabled=true`) and tune HikariCP pool sizing. +- Validate and document GraalVM native compilation (`mvn -Pnative native:compile`). -#### Acceptance Criteria -- Applying the manifests to a local Kubernetes namespace (Minikube/Kind) deploys the application with fully functioning health checks and environment mapping. -- Graceful shutdown handles in-flight transaction threads before pod termination. +### Phase 11B — Cloud Deployment & Documentation Finalization ⬜ + +#### Work Items +- Document cloud deployment options with step-by-step guides for AWS Free Tier and Oracle Cloud Always Free. +- Ensure all documentation accurately reflects implemented, tested features. +- Finalize architecture docs with Mermaid diagrams, API specification, ADR log, changelog, and README. +- Validate that a new developer can clone and run the project within 5 minutes. diff --git a/pom.xml b/pom.xml index d954632..eba9f5d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,11 +12,14 @@ com.payflow payflow-api - 0.0.1-SNAPSHOT + 0.1.0-SNAPSHOT payflow-api 25 + 2.44.2 + 3.6.0 + 2.18.0 @@ -73,6 +76,59 @@ + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + + + + + + + + spotless-check + validate + + check + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.plugin.version} + + checkstyle.xml + true + true + false + + + + checkstyle-check + validate + + check + + + + + + + org.codehaus.mojo + versions-maven-plugin + ${versions.plugin.version} + + false + + diff --git a/src/main/java/com/payflow/PayflowApiApplication.java b/src/main/java/com/payflow/PayflowApiApplication.java index 05ad31c..87694b2 100644 --- a/src/main/java/com/payflow/PayflowApiApplication.java +++ b/src/main/java/com/payflow/PayflowApiApplication.java @@ -6,7 +6,7 @@ @SpringBootApplication public class PayflowApiApplication { - public static void main(String[] args) { - SpringApplication.run(PayflowApiApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(PayflowApiApplication.class, args); + } } diff --git a/src/main/java/com/payflow/controller/TransactionController.java b/src/main/java/com/payflow/controller/TransactionController.java index 640ae0a..d61f763 100644 --- a/src/main/java/com/payflow/controller/TransactionController.java +++ b/src/main/java/com/payflow/controller/TransactionController.java @@ -13,11 +13,11 @@ @RequestMapping("/transactions") public class TransactionController { - @Autowired - private TransactionService transactionService; + @Autowired + private TransactionService transactionService; - @PostMapping - public Transaction sendMoney(@RequestBody Transaction transaction) { - return transactionService.sendMoney(transaction); - } + @PostMapping + public Transaction sendMoney(@RequestBody Transaction transaction) { + return transactionService.sendMoney(transaction); + } } diff --git a/src/main/java/com/payflow/controller/UserController.java b/src/main/java/com/payflow/controller/UserController.java index 83ba831..5e391ac 100644 --- a/src/main/java/com/payflow/controller/UserController.java +++ b/src/main/java/com/payflow/controller/UserController.java @@ -18,32 +18,32 @@ @RequestMapping("/users") public class UserController { - @Autowired - private UserService userService; - - @PostMapping - public User registerUser(@RequestBody User user) { - System.out.println("User object with @RequestBody: " + user.getName()); - return userService.registerUser(user); - } - - @GetMapping - public List getAllUsers() { - return userService.getAllUsers(); - } - - @GetMapping("/{id}") - public Optional getUserById(@PathVariable Long id) { - return userService.getUserById(id); - } - - @GetMapping("/upi/{upiId}") - public Optional getUserByUpiId(@PathVariable String upiId) { - return userService.findByUpiId(upiId); - } - - @GetMapping("/balance/{amount}") - public List getUsersWithBalanceAbove(@PathVariable Double amount) { - return userService.getUsersWithBalanceAbove(amount); - } + @Autowired + private UserService userService; + + @PostMapping + public User registerUser(@RequestBody User user) { + System.out.println("User object with @RequestBody: " + user.getName()); + return userService.registerUser(user); + } + + @GetMapping + public List getAllUsers() { + return userService.getAllUsers(); + } + + @GetMapping("/{id}") + public Optional getUserById(@PathVariable Long id) { + return userService.getUserById(id); + } + + @GetMapping("/upi/{upiId}") + public Optional getUserByUpiId(@PathVariable String upiId) { + return userService.findByUpiId(upiId); + } + + @GetMapping("/balance/{amount}") + public List getUsersWithBalanceAbove(@PathVariable Double amount) { + return userService.getUsersWithBalanceAbove(amount); + } } diff --git a/src/main/java/com/payflow/entity/Transaction.java b/src/main/java/com/payflow/entity/Transaction.java index 03bc9c7..4d56966 100644 --- a/src/main/java/com/payflow/entity/Transaction.java +++ b/src/main/java/com/payflow/entity/Transaction.java @@ -10,58 +10,58 @@ @Table(name = "transactions") public class Transaction { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long transactionId; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long transactionId; - private String senderUpiId; + private String senderUpiId; - private String receiverUpiId; + private String receiverUpiId; - private Double amount; + private Double amount; - private String note; + private String note; - public Transaction() { - } + public Transaction() { + } - public Long getTransactionId() { - return transactionId; - } + public Long getTransactionId() { + return transactionId; + } - public void setTransactionId(Long transactionId) { - this.transactionId = transactionId; - } + public void setTransactionId(Long transactionId) { + this.transactionId = transactionId; + } - public String getSenderUpiId() { - return senderUpiId; - } + public String getSenderUpiId() { + return senderUpiId; + } - public void setSenderUpiId(String senderUpiId) { - this.senderUpiId = senderUpiId; - } + public void setSenderUpiId(String senderUpiId) { + this.senderUpiId = senderUpiId; + } - public String getReceiverUpiId() { - return receiverUpiId; - } + public String getReceiverUpiId() { + return receiverUpiId; + } - public void setReceiverUpiId(String receiverUpiId) { - this.receiverUpiId = receiverUpiId; - } + public void setReceiverUpiId(String receiverUpiId) { + this.receiverUpiId = receiverUpiId; + } - public Double getAmount() { - return amount; - } + public Double getAmount() { + return amount; + } - public void setAmount(Double amount) { - this.amount = amount; - } + public void setAmount(Double amount) { + this.amount = amount; + } - public String getNote() { - return note; - } + public String getNote() { + return note; + } - public void setNote(String note) { - this.note = note; - } + public void setNote(String note) { + this.note = note; + } } diff --git a/src/main/java/com/payflow/entity/User.java b/src/main/java/com/payflow/entity/User.java index f846220..2cb0811 100644 --- a/src/main/java/com/payflow/entity/User.java +++ b/src/main/java/com/payflow/entity/User.java @@ -11,59 +11,59 @@ @Table(name = "users") public class User { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long userId; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long userId; - private String name; + private String name; - @Column(unique = true) - private String upiId; + @Column(unique = true) + private String upiId; - private Double balance; + private Double balance; - private String phoneNumber; + private String phoneNumber; - public User() { - } + public User() { + } - public Long getUserId() { - return userId; - } + public Long getUserId() { + return userId; + } - public void setUserId(Long userId) { - this.userId = userId; - } + public void setUserId(Long userId) { + this.userId = userId; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getUpiId() { - return upiId; - } + public String getUpiId() { + return upiId; + } - public void setUpiId(String upiId) { - this.upiId = upiId; - } + public void setUpiId(String upiId) { + this.upiId = upiId; + } - public Double getBalance() { - return balance; - } + public Double getBalance() { + return balance; + } - public void setBalance(Double balance) { - this.balance = balance; - } + public void setBalance(Double balance) { + this.balance = balance; + } - public String getPhoneNumber() { - return phoneNumber; - } + public String getPhoneNumber() { + return phoneNumber; + } - public void setPhoneNumber(String phoneNumber) { - this.phoneNumber = phoneNumber; - } + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } } diff --git a/src/main/java/com/payflow/repository/UserRepository.java b/src/main/java/com/payflow/repository/UserRepository.java index 51a18f2..dc1e100 100644 --- a/src/main/java/com/payflow/repository/UserRepository.java +++ b/src/main/java/com/payflow/repository/UserRepository.java @@ -11,10 +11,10 @@ public interface UserRepository extends JpaRepository { - // Derived JPA query parsed from method name - Optional findByUpiId(String upiId); + // Derived JPA query parsed from method name + Optional findByUpiId(String upiId); - // JPQL query to find users with balance greater than a specified amount - @Query("SELECT u FROM User u WHERE u.balance > :amount") - List findUsersWithBalanceGreaterThan(@Param("amount") Double amount); + // JPQL query to find users with balance greater than a specified amount + @Query("SELECT u FROM User u WHERE u.balance > :amount") + List findUsersWithBalanceGreaterThan(@Param("amount") Double amount); } diff --git a/src/main/java/com/payflow/service/TransactionService.java b/src/main/java/com/payflow/service/TransactionService.java index 018f622..da86c90 100644 --- a/src/main/java/com/payflow/service/TransactionService.java +++ b/src/main/java/com/payflow/service/TransactionService.java @@ -9,12 +9,12 @@ @Service public class TransactionService { - // Spring creates the TransactionRepository bean and injects it during - // application startup. - @Autowired - private TransactionRepository transactionRepository; + // Spring creates the TransactionRepository bean and injects it during + // application startup. + @Autowired + private TransactionRepository transactionRepository; - public Transaction sendMoney(Transaction transaction) { - return transactionRepository.save(transaction); - } + public Transaction sendMoney(Transaction transaction) { + return transactionRepository.save(transaction); + } } diff --git a/src/main/java/com/payflow/service/UserService.java b/src/main/java/com/payflow/service/UserService.java index 5eb10d3..fc448b9 100644 --- a/src/main/java/com/payflow/service/UserService.java +++ b/src/main/java/com/payflow/service/UserService.java @@ -12,28 +12,28 @@ @Service public class UserService { - // Spring creates the UserRepository bean at startup and injects it here - // automatically. - @Autowired - private UserRepository userRepository; - - public User registerUser(User user) { - return userRepository.save(user); - } - - public List getAllUsers() { - return userRepository.findAll(); - } - - public Optional getUserById(Long id) { - return userRepository.findById(id); - } - - public Optional findByUpiId(String upiId) { - return userRepository.findByUpiId(upiId); - } - - public List getUsersWithBalanceAbove(Double amount) { - return userRepository.findUsersWithBalanceGreaterThan(amount); - } + // Spring creates the UserRepository bean at startup and injects it here + // automatically. + @Autowired + private UserRepository userRepository; + + public User registerUser(User user) { + return userRepository.save(user); + } + + public List getAllUsers() { + return userRepository.findAll(); + } + + public Optional getUserById(Long id) { + return userRepository.findById(id); + } + + public Optional findByUpiId(String upiId) { + return userRepository.findByUpiId(upiId); + } + + public List getUsersWithBalanceAbove(Double amount) { + return userRepository.findUsersWithBalanceGreaterThan(amount); + } }