This project runs over python 3.6 version.
This section doesn't cover environment setup. Just make sure the python3.6 command is available on your path and the dependencies are installed.
The dependencies are split in production and development. Install the development dependencies using the command:
python3.6 -m pip install -r requirements/development.txt
This will install the production dependencies plus the development packages, such as pytest and other tools.
You can execute the project tests available on tests/ folder by invoking Makefile command test:
make test
This command invokes pytest to discover tests around the project working folder, execute them and show the code coverage.
ATTENTION: Make sure the dependencies are installed.
make run
This command invokes migrate followed by runserver django command.
All the code architecture focus in making the code life cycle easier by splitting components to ensure each has it owns responsibility and business rules strictly declared. This also helps the development of tests focused on unit testing.
Package | Description |
---|---|
cards.accounting | Django application for accounting. |
cards.api | The Endpoints views. |
cards.issuer | Issuer service persistence implementation. |
issuer.service | The issuer service operations used by the Scheme. |
issuer.db | Abstract class for issuer persistence. |
Available at issuer/service.py it's a bridge class responsible to implement the business rules related to the webhook used by the Scheme.
It wraps the database calls:
- Normalizing the return values (and exceptions)
- Logging stuff
- Doing minor parameters validation.
The database layer is composed by the calls made by the issuer service plus the accounting business rules to make these calls work.
The issuer service implementation necessary is declared at issuer/db.py file over the abstract class IssuerDatabase.
The implementation is done by CardsIssuerDatabase at cards/issuer.py, making sure the calls are properly encapsulated to raise the properly exceptions if an error occurs. This implementation also encapsulate accounting operations such as:
- Funds transfer around accounts
- Presentment settlement operation: debit and credit (Issuer and Scheme split).
There are 2 contexts implemented:
- Accounting
- Cards
There is an ambiguous thing related to CARD_ID. On the accounting context makes more sense to call it ACCOUNT_OWNER since for example the Issuer and the Scheme doesn't have cards but still have accounts ("balance on some currency").
This application is a ALPHA PROTOTYPE so the concepts may be a little inconsistent.
Some entities have similar responsibilities and naming convention, the project follows this approach:
Entity | Description |
---|---|
Transaction | Used for cards transactions, not related to accounting. |
Batch | Is the real accounting transaction, transfers are known as Journal. |
At this project Django is being used for 2 different things:
- Endpoints serving (views).
- Database abstraction (models).
It's important to say that the parts are "easily" plug-able. Easily meaning possible not without work.
They don't relay in each other. The models are only used by the CardsIssuerDatabase layer, the RESTFul API stuff doesn't.
The Scheme webhook endpoints doesn't rely directly on the models implemented at cards.accounting Django application.
The views calls directly the Issuer service implemented by the python module issuer.service.
Some items were implemented in an different way than the specification:
- HTTP 201 is used at the /authorisation endpoint instead of HTTP 200. This is the default approach.
- NO Endpoint Documentation — As far the has 2 endpoints at the moment there is no endpoint documentation written but the parameters definitions for each endpoint it's available at cards/api/serializers.py that can auto generate docs based on 3rd party libraries such as swagger, apidoc.
- Presentment Endpoint — Despite the Scheme calls it again with the same parameters used previously on the authorisation endpoint the previous used parameters are not validated or used.
There is an account for Scheme and another for the Issuer, this represents the real money available at the Issuer on it hands and what it owns the Scheme.
The presentment logic is implemented within CardsIssuerDatabase class at make_presentment_batch method.
The settlement daily task isn't implemented but changing Transaction of type authorisation old more than next day will make the task works. This can be done by just setting an Transaction to type expired.
The balance calculation is implemented at cards/accounting/managers.py file. Only a Transaction which type/status of authorisation is being summarized.
This project needs some nice features, small and major changes.
A public/private key signing should be a good second security layer, this layer would be applied at database level to ensure any card movement record stored over the database was stored by the service application and not hijacked someway.
A new authorisation should be double signed:
- Issuer Service Signature — Adds a field to the Transaction table schema that stores a hash that confirms this record was inserted by the service layer.
- Scheme Signature — Makes sure it was signed by the scheme application who called the authorisation endpoint previously and not other service.
And at the presentment should be:
- Check if the presentment was really signed by the issuer service application.
- Check if the Scheme calls at presentment endpoint previously signed this authorisation.
ATTENTION: Maybe this approach is overthink about the security layer, but would be nice to have
Some 'small things' not implemented but it would be cool.
-
Documentation
- api documentation
- sphinx docstring
- output sphinx doc
-
tests
- coverage
- linter (flake8)
-
logging
-
12factor / settings outside the code base
-
Docker image
- setuptools packaging