CheckoutChallenge: Payment Gateway API
Build , an API based application that will allow a merchant to offer a way for their shoppers to pay for their product.
Deliverables
- Build an API that allows a merchant:
- To process a payment through your payment gateway.
- To retrieve details of a previously made payment.
- Build a bank simulator to test your payment gateway API.
Installation
Pre-requisites
- .NET Core 6.0 SDK
- Visual studio or VsCode
- Docker
Building the project
- In visual studio right-click on the solution file and select build solution.
- In VsCode, run the following command in the root directory to build the project
dotnet restore
dotnet build
Running the project
- In visual studio click the run button after selecting the api project as the default project.
- In vscode, run
dotnet run
from the root directory. - If using docker, run the docker compose file
docker-compose up
from the root directory.
You can browse the swagger APIs documentation at https://localhost:7277/swagger
To make request, please set a MerchantId and Idempotent header in the request. e.g
curl --location --request POST 'https://localhost:7277/Payments' \
--header 'x-idem-key: 123' \
--header 'MerchantId: 22d477d7-035f-412b-ac15-3b92b8b0f8b2' \
--header 'Content-Type: application/json' \
--data-raw '{
"currency": "string",
"amount": 0,
"description": "string",
"card": {
"number": "2022202220222022",
"cvv": "string",
"expiryMonth": 1,
"expiryYear": 1,
"ownerName": "string"
}
}'
The merchant keys can be found in the appsettings.json file.
Project Structure
Application
The solution is made up of four projects that are loosely based around the repository pattern.
- API project which interfaces with the merchants.
- Core project. This contains most of the domain specific codes
- Shared project. Contains reusabe codes that can be shared among the projects.
- Mock bank: A simple class that mocks the external bank infrastructure.
Tests
There are two projects that should hold tests for this solution.
-
Integration tests. This ensures that the components function correctly including API calls and database queries. I wrote a couple of test cases to handle some of the requirement.
-
Unit test: Tests individual components of our solution. Due to time constraints, I couldnt do much here.
Deployment
The project can be deployed to multiple cloud providers but I will pick AWS or Azure as most engineers are primaarily using either of the two so it will be easier to maintain moving forward.
- AWS: The application could be deployed to an elastic beanstalk infrastructure, ECS or even a lightsail instance. Secrets like connection keys could be stored AWS KMS.
- Azure: The application could be deployed to an app service, AKS(using the compose file) or an Azure VPS. Secrets like connection keys could be stored Azure key vault.
Assumptions/Theories
I made the following assumptions while building this solution
- Only merchants would be allowed access to the API's which will only be provided using the MerchantId
- The bank has a fast response time and zero downtime
Bonus points:
-
Idempotency: The API supports idempotency for safely retrying requests without accidentally performing the same operation twice.
-
API-KEY Auth: I added an implementation to only allow requests from allowed merchants. The implementation uses a static list of merchant ids which can be changed to a database implementation. The keys are in the appsettings file.
-
Logging: Serilog library was used to implement a file based logger. This can easily be extended to send logs to a database and slack.
-
Docker support was added to allow running the application in containers.
-
Continous integration was setup using a github action.
-
Metrics: API metrics are generated and can be viewed using prometheu and grafana. Please run the docker compose file in the visualization folder.
Improvements
- Caching: Adding caching to the repository layer. This will allow faster response and relieve the database.
- Add more robust tests
- Improve the validation for the models
- An improv3ed way of storing secrets. Using a keyvault instead of appsettings which keeps it in source control.