The Card Cost Service is an application that provides with main two functionalities:
- a REST CRUD operation for the Country Clearing Cost
- a Service which can be given with a card number that will utilize the information provided by the public BinList API
- Java 11
- Maven
- Spring (Web, Security, Validation, Caching & Scheduling) and SpringBoot
- Hibernate & SpringData
- Flyway & H2 Database (in-memory)
- JUnit 5 Unit & Integration Tests
- Markdown (for this Readme)
- OpenApi-Swagger Docs
- Docker containerization and a docker-compose environment
- SonarQube (SonarCloud) for Static Code Analysis
- GitHub Action CI/CD
The Card Cost Service provides with a simple CRUD operation for creating, retrieving, updating and deleting a CountryClearingCost records.
The Country Clearing Cost objects consists of the following:
CountryClearingCost {
- countryCode : an ISO 3166-1 alpha-2 code (two-letter country code)
- cost : Non-negative decimal that show the clearing cost of the country
}
Currently, the Country Clearing Cost does not contain any information about the cost's currency. This could be implied by the country code as each country should have its own currency.
The CRUD operations are fully validated with the bean validation support provided by the
spring-boot-starter-validation
dependency.
This means that:
- country code - should be exactly 2 two letter and should be a valid country code
(the validation uses the Java's Locale.getISOCountries()) to fetch all the country codes and check if the given country code
is valid one (see CountryValidator.java).
Also the country code is not case sensitive, so
GR
andgr
is exactly the same for the CRUD's behaviour. - cost - should be a non-negative decimal number
If any field in the request is not valid an error message is returned.
A set of pre-configured records is automatically loaded into the database when the application starts by using flyway's migration mechanism. There values are:
Card issuing country | Clearing Cost |
---|---|
US | 5 |
GR | 15 |
The CRUD endpoint can be found under the /country-cost
path.
There also exists a default clearing cost value for all the other countries that are not configured in the database.
The value is initially set from the application.yml
by the card-cost.defaultClearingCost
property. This means that it can be pre-configured by the user before starting the application.
In a production environment restarting the whole server in order to change the default clearing cost value is not feasible.
That's why a special endpoint is available only to the users with the ADMIN
role, that enables changing the default
clearing cost value.
It can be found under /admin/defaultClearingCost
and requires a POST
call with the following JSON body to set the new value.
{
"cost" : <non-negative decimal>
}
A credit card number is also called PAN. The PAN is a 16 digit number displayed on one side of the card. The first 6 digits of a payment card number (credit cards, debit cards, etc.) are known as the Issuer Identification Numbers (IIN), previously known as Bank Identification Number (BIN). These identify the institution that issued the card to the card holder.
The Card Cost Service takes a PAN, extracts the BIN number, calls the public Binlist API to get the Bin information result and get's the card's origin country(alpha 2 iso code).
Then it calculates the clearing cost of the card by simply looking in the DB to find the clearing cost of the PAN's country. If a Country Clearing Cost record is found then it returns it, otherwise it returns the default clearing cost value.
In order to get the Bin Lookup Response schema I've used jsonschema2pojo instead of manually creating the Java POJO classes.
The card clearing cost calculation is depicted in the following diagram:
The Card Cost Service API can be found under /payment-cards-cost
and supports with the following action:
a POST on /payment-cards-cost
of the following JSON:
{
"card_number" : <pan>
}
should respond with a response
{
"countryCode": <iso2_code>,
"cost": <decimal>
}
The card_number
request body is also validated to be a valid card number or exactly 16 digits.
Otherwise an error is message is returned.
The BIN information obviously doesn't change frequently. This can be used to reduce the number of calls to the Binlist API.
The Card Cost Service does this by caching the Bin Response by using Spring Cache.
The cache is set to be cleared each day but the specific interval is configurable and can be changed by the user
by overwriting the binlist.api.clearBinCacheInterval
property in the application.yml.
The database that the application uses is an H2 in-memory DB.
The H2 in-memory db can be accessed once the application has started under the /h2-console
url.
The application uses Flyway in order to do any db migrations.
This configuration is set in the application.yml and all the migration scripts can be found under the db/migration folder. They are used in order to create the db schema and load the initial data into the db.
The application uses Exception Handling with Spring for the REST API.
There are two different Exception Handlers defined.
One for the validation exceptions which produce more user-friendly error message and one for all other exceptions. More info can be found here: ExceptionHandlers.java.
A special domain class called ErrorDetails is created for handling error which is returned in the response. The ErrorDetails contains the following field:
ErrorDetails {
- error : the error message
- timestamp : the timesamp of the error
- details : details around the request
}
The Card Cost Service is secured by the Spring Security and required a basic authentication that uses a username and a password to authenticate.
Every call and request to the Application should be authorized or an error with the status 401 Unauthorized
is returned.
Two default users are programmatically configured:
username | password | role |
---|---|---|
user | user | USER |
admin | admin | ADMIN |
For more info check the SecurityConfig.java.
Multiple unit & integration test are written in order to ensure the correct business logic and code execution. The cover over 90% of the application's code.
The tests are added in the CI/CD GitHub Action's pipeline as separate steps and if any test fail then the build will fail.
The JaCoCo library is being used to produce a test coverage report for both unit and integration test and merge the two reports into one final report.
This report is later used in the Static Code Analysis phase in order to pass the Sonar's project Quality Gates.
Each time the application is built a Docker image is created. The app is configured to produce a docker image by using the dockerfile-maven-plugin, that can be used to run the app directly.
There also exists a docker-compose environment to make this process even easier.
Documentation is an essential part of building REST APIs.
That's why the application uses the OpenAPI specification to generate OpenAPI 3 specification docs for the Rest API.
Once the application is started you can find the documentation under /swagger-ui.html
and it will display something like this
The project has its own pipeline configured with the GitHub Actions.
You can find the pipeline's configuration file in the maven.yml.
It consists of the following steps:
- Build
- Unit Tests
- Integration Tests
- Static Code Analysis (using Sonar Cloud)
This workflow/pipeline ensures:
- Green Builds
- Successful unit & integration tests executions
- Clean and safe code standards by using SonarCloud
- Test coverage with JaCoCo
The Static Code Analysis of the project can be found at: https://sonarcloud.io/dashboard?id=Valyo95_card-cost-service&branch=master