This project is a Spring Boot application that implements a simple payment gateway based on the requirements from the challenge:

https://github.com/cko-recruitment/#offline-coding-interview-preparation

The gateway allows merchants to retrieve payment details and process card payments while interacting with a simulated acquiring bank.

This implementation focuses on simplicity and maintainability while meeting the functional requirements of the challenge.

Key features

  1. Process a card payment
POST http://localhost:8090/api/payments
  1. Retrieve a previously processed payment
GET http://localhost:8090/api/payments/{id}

Note: The acquiring bank simulator listens on http://localhost:8080 (see docker-compose.yml). The gateway runs on 8090 so both can run on the same machine.

Requirements

  • JDK 17
  • Docker

Run the Application

To run the application, first start the bank simulator:

docker-compose up

Then run the spring boot project:

./gradlew bootRun

Run the Test

To run the tests:

./gradlew test

API Documentation

Documentation is available at: http://localhost:8090/swagger-ui/index.html

Design Consideration

  • Jakarta Bean Validation is used to validate requests before processing.
  • Lombok is used to reduce boilerplate code and improve maintainability.
  • All exceptions are handled and returned in a consistent format.
  • Separate DTOs are used for different communication purposes (i.e., API requests, responses, bank requests).
  • A mapper layer is used to convert consistently between DTOs.
  • Only the last four digits of the card number are stored, returned and logged to protect sensitive data.
  • CVV is not stored, returned or logged to protect sensitive data.
  • All incoming API requests are logged for visibility and to support monitoring.
  • Exceptions from bank calls are caught and handled to help identify failures and their causes.

Assumption

  • The requested currency is assumed to be a valid ISO code.
  • Full card numbers and CVV should not be stored or will be stored with encryption.
  • Bank non-HTTP 200 responses are assumed as follows:
  • 503 is a temporary bank issue (i.e., bank service maintenance).
  • Other 5xx belongs to bank server issues.
  • Other client errors or exceptions are assumed as gateway application error (i.e., 400 bad request).
  • If the acquiring bank returns any error, the payment failed and should not store the payment.
  • In-memory storage is only used for assessment simplicity.
  • Only one acquiring bank is assumed in this implementation.
  • Payments are processed synchronously with the bank simulator.

Future improvement

  • Manage validation messages by property configurations.
  • Implement pageable features for large data query.
  • Structure
  • Separate bank client for scalability
  • Separate DTO and entity for real database use
  • Use WebClient to integrate external API.
  • Implement retry mechanisms when communicating with the acquiring bank.
  • Use WireMock to simulate calling a real bank server (i.e., simulate HTTP errors).
  • Add currency validation for valid ISO code, using method like Enum, database or cloud service.

Feedback from the company

While you showed a good attitude about learning from and accepting feedback, solid knowledge of observability, and well-implemented error handling and API-s,

The implemented solution had multiple deviations from the specifications and best practices for code design, some of which you could not offer good fixes for. The implemented tests also showed multiple bugs and anti-patterns.