As the name of the project suggests, this is a simple implementation of MPESA. If you are East-African you already know what MPESA is, but if you don't, I will have a description herein to help you understand inner workings that you may not have been aware of.
MPesa (M for mobile, pesa is Swahili for money) is a mobile phone-based money transfer service, payments and micro-financing service, launched in 2007 by Vodafone Group plc and Safaricom, the largest mobile network operator in Kenya. M-Pesa allows users to deposit, withdraw, transfer money, pay for goods and services (Lipa na MPesa), access credit and savings, all with a mobile device.
The service allows users to deposit money into an account stored on their cell phones, to send balances using PIN-secured SMS text messages to other users, including sellers of goods and services, and to redeem deposits for regular money. Users are charged a small fee for sending and withdrawing money using the service. MPesa is a branchless banking service; MPesa customers can deposit and withdraw money from a network of agents that includes airtime resellers and retail outlets acting as banking agents.
This is the big question. How does it work generally and how does it operate in the interior. We start with an overview.
We can use the description above to make general inferences on how MPESA works.
To begin with, we can make inferences about what kind of customers MPESA operates with. We can get this information from the description.
The service allows users to deposit money into an account stored on their cell phones, to send balances using PIN-secured SMS text messages to other users, including sellers of goods and services, and to redeem deposits for regular money.
MPesa customers can deposit and withdraw money from a network of agents that includes airtime resellers and retail outlets acting as banking agents.
Subscribers
- customers who can deposit and withdraw money into an account on their cell phonesMerchants
- customers with accounts that accept payments of goods and servicesAgents
- customers that have the function of allowing other customers to deposit cash or withdraw cash from their accounts
We have managed to infer 3 types of customers that MPESA serves.
MPESA is a financial service, so we are more interested in the operations that involve the financial part. We are going to ignore user registration and management at this point for now. Let's look at this description:-
M-Pesa allows users to deposit, withdraw, transfer money, pay for goods and services (Lipa na MPesa), access credit and savings, all with a mobile device.
Deposit
- customers can credit their accountsWithdraw
- customers can debit their accountsTransfer
- customers can make transfers of moneys to other customers, through payments or regular transfers.
Those are the major 3 operations but if we go deeper we can also infer that customers can have some form of Savings account
and can access credit (loans)
. From the above description we can't infer enough to know whether the savings account accrue
interest and whether the loans(credit) are some form of overdraft, short term or long term loan.
TO KEEP THE API SIMPLE WE ARE GOING TO CONCENTRATE ON THE 3 MAJOR OPERATIONS.
A subscriber is a regular or standard customer with a cell phone and can perform the following operations
- deposit money into their account
- make transfers and receive money to/from other customers
- make payments to merchants
- can withdraw money
On top of the above operations a subscriber can retrieve full and mini statements
on their accounts and fetch account balance
.
A merchant is a customer that sells goods or services and usually has an account to majorly assist in receiving payments. However, a merchant can do other operations with their account.
- receive payments
- make transfers to other customers
- can withdraw money
An agent has 2 primary functions. From the descriptions, agents receive cash and can make a deposit on behalf of another customer and agents can make a withdrawal on behalf of another customer and in turn give me cash.
- make and receive transfers
- can withdraw money
- can deposit money
- can make payments
An agent usually operates with a float account, and a separate commission account. The float account is an account they
use to facilitate withdrawals and deposits. During onboarding, an agent is required to have a minimum amount in
their float account before they can begin transacting. Agents can acquire this float from a super-agent
.
When doing a deposit for a customer, the agent receives cash from the customer and initiates the transaction. Money will be debited from the agent's float account and moved to the customer's account.
When a customer is withdrawing from an agent, money is debited from the customer's account and credited to the agent's float account. The agent then gives the customer cash.
An agent earns some commission from the system for enabling customer withdrawals. The commission is a percentage or a fixed amount of the transaction cost that the customer pays when withdrawing.
That's a good brief overview of MPESA and how everything generally comes together.
Let's now discuss the components that make up MPESA as a whole. From what we have now gathered about MPESA we can infer a some components. If we are following a Domain Driven Design we can call these components Domains, and if we are following a service oriented design we can call then services. We are going to infer the major ones.
We begin with what makes up one of the most important business aspect of a Financial Service. A transaction domain is necessary to enable and record all transactions that are happening in the system between accounts and between users. It's functions include:
- enabling and recording all transactions regarding transfers of money between accounts
- enabling and recording all transactions regarding deposits of money to accounts
- enabling and recording all transactions regarding withdrawals of money from accounts
- apply transaction cost on all applicable transactions
We can lay out all possible transactions that customers can perform between each other. We know we have the following
operations transfer
, deposit
and withdraw
This is moving money from one account to another between customers. All customer types can make transfers, however some transfers are treated specially. Also, some customer types cannot make transfers to other particular customers. Let's define possible transfers that can happen.
- A transaction between a
subscriber
andsubscriber
- A transaction between an
agent
and anagent
- A transaction between a
merchant
and asubscriber
- A transaction between a
subscriber
and amerchant
is considered aPAYMENT
but its inner workings is a transfer. - A transaction between an
agent
and amerchant
is also considered aPAYMENT
but its inner workings is a transfer.
This is when a customer makes a cash deposit at an agent. Deposits can only be done at agents. We can define which customers can make deposits.
- A deposit between a
subscriber
andagent
- A deposit between an
agent
andagent
This when a customer makes a cash withdrawal at an agent. Just like deposits, withdrawals can only be done at agents. We similarly define which customers can make withdrawals.
- A withdrawal at an
agent
by asubscriber
- A withdrawal at an
agent
by aagent
- A withdrawal at an
agent
by amerchant
Users are charged a small fee for sending and withdrawing money using the service.
In order to return a profit (RoI), a financial service such as MPESA will charge a small fee for using the service on selected operations.
MPESA applies a transaction fee on the following services:
- querying balance
- getting mini statement and full statement
- making a transfer to another customer
- making a payment to a merchant
- withdrawing from an agent
The tariff domain will be responsible for the following functions:
- configuring and maintaining a tariff on selected operations.
- given a type of transaction, tariff domain will return the configured cost.
When discussing the Transaction domain
, we detailed and defined all possible transactions between customers. All these
transactions are possible areas a tariff could be applied. Apart from the transactions, a configured tariff can also be
applied to services offered such as fetching mini and full statement
and fetching balance
depending on the business
decisions.
The service allows users to deposit money into an account stored on their cell phones,
Once registered, customers need to have an account so that they can begin transacting and using the service. We can also refer to them as wallets, but since for the Simple MPESA application we are going to allow a customer to hold only one wallet, then we are going to refer to them as accounts.
The account domain will be responsible for the following functions:
- maintaining state and status of customer accounts
- managing access to customer accounts i.e if an account is not marked active, we can't transact with it
- recording and updating account balance after transactions
On a side note
In MPESA, your cell phone number is your account number, in contrast to a bank account where, you are given an account number when you successfully open an account. In the Kenyan variant of MPESA, that is owned by a Mobile Network Operator called Safaricom, the process of registration into MPESA is nearly similar to the process of registering your new SIM Card or is done at the same time. When getting a new Safaricom SIM Card a customer's KYC details are taken and in the registration form, a customer can opt in to use MPESA services. Your details for SIM registration are then going to be used to register and open an account for you with MPESA.
We have 3 types of customers, we will have to find a way of managing KYC (Know Your Customer) data of our customers and limiting access to different resources within the system by authenticating and authorizing based on the type of customer. On the other hand, the system needs some form of administrator user(s) who runs the service and makes sure its available and reliable for our customers to use and experience.
So we can infer that we have 2 types of users of our system i.e administrator(s)
and customers
and further, we have
3 types of customers i.e subscribers
, merchants
and agents
. Each user of the system can perform specific actions,
based on how they authenticate.
Our user and customer domain will have the following responsibilities:
- Manage and record user information(data)
- Authenticate and authorize users
- Provide and manage access to user data
To this point we have been using various resources we could acquire to help us infer about MPESA, and we have managed to
answer; what it is?
, how it works?
, what is it made of?
.
Going forward we cannot infer anything about the System Design and Architecture unless we have access to the proprietary code, so we build our own implementation that would work closely to how we experience using MPESA.
Whatever follows, is the design and architecture of our simple MPESA application.
This application is built as a Monolith
application following Domain Driven Design principles. The application uses a
postgres database for storage.
Domain Driven Design is at the heart of our simple mpesa application.
DDD principles have concepts called contexts. The application uses the following bounded contexts:
- Admin
- Agent
- Merchant
- Subscriber
- Transaction
- Account
- Statement
- Tariff
- Auth
- Customer
The application needs some form of administration by a super user charge with the responsibility of running and maintaining
the application to ensure reliability and stability. This user is an admin
and is given their own bounded context. Some
responsibilities/actions of this user are:
- Can login to system or register.
- Can assign float to a Super Agent.
- Can configure tariff
- Can suspend/change status of a customer account
- Can view/edit/delete customer accounts
As the application grows and scales the administrator context would have more responsibilities.
-
The application would need more than one administrator and more so more than one category of administrators. In the case of MPESA, some examples of administrators with their roles include:
i. Customer Care - is a part admin who would assist customers with information about the system and troubleshoot problems.
ii. Finance - is a part admin whose responsibilities would be financial and accounting aspect in the system.
iii. IT - an admin whose responsible for the infrastructure that the system runs on.
iv.
We have acquired or developed a wallet and money transfer service for a Telco and we have been given the go ahead by the Central Bank to deploy the application and get some customers to use our system. There are however some initial steps the business has to perform to start onboarding new customers. The system should be able to have some level of autonomy when it comes to the flow of money. That is where agents come in.
The initial obstacle in the pilot was gaining the agent’s trust and encouraging them to process cash withdrawals and agent training.
Source wikipedia
Our first initial steps before we can roll out
- Acquire and entity licensed to hold public money. A bank.
- Create a super agent(s) whose task would be depositing money to our bank account.
- Once a super agent deposits to our account, we assign them with an equivalent amount of float they can sell to other agents.
- When we onboard an ordinary agent, they will have a balance of zero, and they will approach the super agent to get float.
Agents are important customers to the system. They can also have various categories depending on the business use case. For our example we have 2 types of agents:
- Super Agent
- Ordinary Agent
MPESA has 2 types of merchants.
- A merchant to provides utility services to their customers
- A merchant that sells goods and services to customers
Both merchants have unique ways of how customers pay for their services/goods. However both merchants have an account number.
- Pay bill number as an account number for a merchant - Customer provides the
pay bill number
,a customer account number
and theamount
. - Till number as an account number for a merchant - Customer provides the
till number
andamount
.
Pay bill number
is usually given to utility companies that need to identify from whom the payment is coming from by the
customer account number
.
Till number
is usually given to small scale traders that want to accept payment via MPESA from their customers.
For our example we stick to one general merchant that accepts payment.
A subscriber does not have much going on. They can authenticate and perform a transaction.
Contains all business logic in regard to transactions happening in the system. It enforces the transaction rules and business policy.
Business policies:
- A transaction cannot happen between identical customers i.e a customer cannot transact with themselves
- A deposit cannot be done by none other customer than an agent
- A customer cannot perform a withdrawal with no other customer than an agent
- A super agent is however only allowed to do deposits for other agents only
- Customers are not allowed to deposit, withdraw or transfer money below the minimum amount allowed
- Apply transaction fee as per the tariff configured
The main responsibility of this context is managing customer accounts/wallets. Responsibilities:
- Updating account balances, credit/debit accounts
- Updating system ledger after changing account balances
The main responsibility of this context is managing the system ledger. If we scale the system, we can view this ledger
as the statements/transactions event store. Borrowing from event sourcing
design, our statement context is a record
of every event with customer transactions.
This context has a responsibility of configuring and maintaining the tariff used in various transactions.
The system has 4 different types of users, admin
, agent
, subscriber
and merchant
. The auth context is responsible
for authenticating and authorizing these users into the system.
This context is mainly an aggregator of the agent
, merchant
and subscriber
contexts. It exposes common functionality
for, which can be used in other core contexts.
To begin with, the application uses postgres as the backend database.
The application uses postgres as the database server. So here are instructions on how to setup postgresql on your machine using docker.
Get the official postgres docker image.
$ docker pull postgres
Then create a container from the image with the following variables
$ docker create \
--name mpesa-db \
-e POSTGRES_USER=mpesa \
-e POSTGRES_PASSWORD=mpesa \
-p 5432:5432 \
postgres
Run the following command to start the container
$ docker start mpesa-db
Running the application is as simple as running any other go application but first we need to copy and create our configuration.
$ git clone https://github.com/SirWaithaka/simple-mpesa.git
$ cd simple-mpesa
$ cp config.yml.example config.yml
This configuration file looks something like this
database:
host: "127.0.0.1"
port: "5432"
user: "mpesa"
password: "mpesa"
dbname: "mpesa"
app_secret_key: "eQig7GS4cHO2su"
You can change the config variables depending on your database setup, here I choose to follow the default setup shown at database installation step.
$ mkdir bin
$ go build -o bin/mpesa-server cmd/mpesa-server.go
$ ./bin/mpesa-server
It will install all dependencies required and produce a binary for your platform.
Make sure you have docker installed and working properly.
$ docker build -t simple-mpesa:latest .
$ docker container create --network=host --name mpesa-server --restart unless-stopped simple-mpesa
$ docker container start mpesa-server
The server will start at port 6700
.
Enjoy.
A description of the api.
All the routes exposed in the application are all defined in this function
func apiRouteGroup(api fiber.Router, domain *registry.Domain, config app.Config) {
api.Post("/login/:user_type", user_handlers.Authenticate(domain, config))
api.Post("/user/:user_type", user_handlers.Register(domain))
// create group at /api/admin
admin := api.Group("/admin", middleware.AuthByBearerToken(config.Secret))
admin.Post("/assign-float", user_handlers.AssignFloat(domain.Admin))
admin.Post("/update-charge", user_handlers.UpdateCharge(domain.Tariff))
admin.Get("/get-tariff", user_handlers.GetTariff(domain.Tariff))
admin.Put("/super-agent-status", user_handlers.UpdateSuperAgentStatus(domain.Agent))
// create group at /api/account
account := api.Group("/account", middleware.AuthByBearerToken(config.Secret))
account.Get("/balance", account_handlers.BalanceEnquiry(domain.Account))
account.Get("/statement", account_handlers.MiniStatement(domain.Statement))
// create group at /api/transaction
transaction := api.Group("/transaction", middleware.AuthByBearerToken(config.Secret))
transaction.Post("/deposit", transaction_handlers.Deposit(domain.Transactor))
transaction.Post("/transfer", transaction_handlers.Transfer(domain.Transactor))
transaction.Post("/withdraw", transaction_handlers.Withdraw(domain.Transactor))
}
The routes are mounted on the prefix /api
so your requests should point to
POST /api/login/<user_type> <-- user_type can be either of agent, admin, merchant, subscriber
POST /api/user/<user_type> # for registration <-- user_type can be either of agent, admin, merchant, subscriber
POST /api/admin/assign-float
POST /api/admin/update-charge
GET /api/admin/get-tariff
PUT /api/admin/super-agent-status
GET /api/account/balance
POST /api/account/statement
POST /api/transaction/deposit
POST /api/transaction/transfer
POST /api/transaction/withdraw
The api can be used to register 4 types of users: admin
, agent
, merchant
and subscriber
An admin can be registered to the api with the following POST
parameters
firstName
, lastName
, email
, password
Curl request example
curl --request POST \
--url http://localhost:6700/api/user/administrator \
--header 'content-type: application/x-www-form-urlencoded' \
--data firstName=Admin \
--data lastName=Waithaka \
--data [email protected] \
--data password=mnbvcxz
Response example
{
"status": "success",
"message": "user created",
"data": {
"userID": "ac8f944b-b0aa-4029-9caf-dfe67007bc84",
"userType": "administrator"
}
}
At minimum, you need to create 2 agents, one of which will become a super agent
. An agent can be registered to the api
with the following POST
parameters
firstName
, lastName
, email
, phoneNumber
, password
Curl request example
curl --request POST \
--url http://localhost:6700/api/user/agent \
--header 'content-type: application/x-www-form-urlencoded' \
--data firstName=Agent \
--data lastName=Waithaka \
--data [email protected] \
--data phoneNumber=254700000000 \
--data password=mnbvcxz
Response example
{
"status": "success",
"message": "user created",
"data": {
"userID": "cca7d227-74ae-4d47-aae8-a0ab952aac28",
"userType": "agent"
}
}
A merchant can be registered to the api with the following POST
parameters
firstName
, lastName
, email
, phoneNumber
, password
Curl request example
curl --request POST \
--url http://localhost:6700/api/user/merchant \
--header 'content-type: application/x-www-form-urlencoded' \
--data firstName=Merchant \
--data lastName=Waithaka \
--data [email protected] \
--data phoneNumber=254700000000 \
--data password=mnbvcxz
Response example
{
"status": "success",
"message": "user created",
"data": {
"userID": "c3a81710-ef66-47d9-adc9-f365a324ed5c",
"userType": "merchant"
}
}
A subscriber can be registered to the api with the following POST
parameters
firstName
, lastName
, email
, phoneNumber
, password
Curl request example
curl --request POST \
--url http://localhost:6700/api/user/subscriber \
--header 'content-type: application/x-www-form-urlencoded' \
--data firstName=Subscriber \
--data lastName=Waithaka \
--data [email protected] \
--data phoneNumber=254700000000 \
--data password=mnbvcxz
Response example
{
"status": "success",
"message": "user created",
"data": {
"userID": "cf9d8f28-357e-4ac7-9b5f-eaa8609e6c2f",
"userType": "subscriber"
}
}
You can use the following POST
parameters for login with any of the 4 users
email
, password
Curl request example for subscriber login
curl --request POST \
--url http://localhost:6700/api/login/subscriber \
--header 'content-type: application/x-www-form-urlencoded' \
--data [email protected] \
--data password=mnbvcxz
Response example
{
"userId": "cf9d8f28-357e-4ac7-9b5f-eaa8609e6c2f",
"userType": "subscriber",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6ImNmOWQ4ZjI4LTM1N2UtNGFjNy05YjVmLWVhYTg2MDllNmMyZiIsInVzZXJUeXBlIjoic3Vic2NyaWJlciJ9LCJleHAiOjE2MDU0MTYwNjAsImlhdCI6MTYwNTM5NDQ2MH0.lAJ4WpF2Mnfg52iuTOoPV8nvbHV3JrMQOC-5xXrQ5EE"
}
NOTE: The remaining endpoints require the token acquired above for authentication
There are some initial setups that need to be done before you can begin doing transactions.
Before you can start transacting, you need to login as an administrator and create a super agent by changing the status of an existing agent. When registering an agent, you ought to have created at minimum 2 agents. It is now that we need make one of those agents a super agent.
The following endpoint is used to update the super agent status
of an agent.
PUT /api/admin/super-agent-status
requires the following post parameters: email
Curl request example
curl --request PUT \
--url http://localhost:6700/api/admin/super-agent-status \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6Ijc2YmM0YWEzLTAyNWQtNGQ1YS1hNWZiLWY1NDk1NTdmNjM0YSIsInVzZXJUeXBlIjoiYWRtaW5pc3RyYXRvciJ9LCJleHAiOjE2MDU0NTE4MDUsImlhdCI6MTYwNTQzMDIwNX0.8lTWl9hGr9GTST7WpEpzKdm_gqhMkf4qUellLx4o5bw' \
--header 'content-type: application/x-www-form-urlencoded' \
--data [email protected]
Response example
{
"status": "success",
"message": "Super Agent Status updated"
}
Logged in as an administrator, you need to assign float to your super-agent
using the following endpoint
POST /api/admin/assign-float
Curl request example
curl --request POST \
--url http://localhost:6700/api/admin/assign-float \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6ImE3OGVjNjNhLTA0ZWItNDAzNC1iZmVkLTBhNmMwMjU3ZTJlNCIsInVzZXJUeXBlIjoiYWRtaW5pc3RyYXRvciJ9LCJleHAiOjE2MDUzMjExNzUsImlhdCI6MTYwNTI5OTU3NX0.4fGlMJQB-eylKwOAwa4d16nVQQt3uYgwPbUjYt7j9zA' \
--header 'content-type: application/x-www-form-urlencoded' \
--data [email protected] \
--data amount=100000
Response example
{
"status": "success",
"message": "Float has been assigned.",
"data": {
"balance": 100000
}
}
The super-agent
is limited to depositing to agents only. You will need to transfer the acquired float to other agents
you have registered.
The default tariff in the system is set to zero amount for all chargeable transactions. You could begin testing transactions using the default tariff and later choose to configure your own tariff. Choose your poison :-).
You can configure a tariff by updating the available charges. The system doesn't allow you to add any other charge band.
GET /api/admin/get-tariff
- use this endpoint to get the available configured transaction charges
Response example
{
"status": "success",
"message": "Tariff retrieved",
"data": [
{
"id": "acf3e6bf-c9de-45b4-a8b6-bf97f92b783a",
"txnOperation": "WITHDRAW",
"srcUserType": "subscriber",
"destUserType": "agent",
"fee": 0
},
{
"id": "0e5a4aaa-135a-4464-96c9-d021f769bdb7",
"txnOperation": "WITHDRAW",
"srcUserType": "merchant",
"destUserType": "agent",
"fee": 0
},
{
"id": "243e7ecc-c2dd-41bb-9953-1278050bfb64",
"txnOperation": "WITHDRAW",
"srcUserType": "agent",
"destUserType": "agent",
"fee": 0
},
{
"id": "f8835176-316c-49de-b001-687e2c4a338d",
"txnOperation": "TRANSFER",
"srcUserType": "agent",
"destUserType": "agent",
"fee": 0
},
{
"id": "4edeb6d0-37cd-4c67-997a-0b3fa93b722d",
"txnOperation": "TRANSFER",
"srcUserType": "subscriber",
"destUserType": "subscriber",
"fee": 0
},
{
"id": "94c0ae8b-a131-41b9-b5af-5235b8926fa4",
"txnOperation": "TRANSFER",
"srcUserType": "merchant",
"destUserType": "subscriber",
"fee": 0
},
{
"id": "450e4baa-58c3-41b3-abe5-a55555492e0c",
"txnOperation": "TRANSFER",
"srcUserType": "subscriber",
"destUserType": "merchant",
"fee": 0
},
{
"id": "3623a89f-c496-41c8-b6c9-73429cc4ef9d",
"txnOperation": "TRANSFER",
"srcUserType": "agent",
"destUserType": "merchant",
"fee": 0
}
]
}
POST /api/admin/update-charge
- use this endpoint to update a charge using its id
.
You need the following POST
parameters
amount
, chargeId
- The amount should be in cents
.
Curl request example
curl --request POST \
--url http://localhost:6700/api/admin/update-charge \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6Ijc2YmM0YWEzLTAyNWQtNGQ1YS1hNWZiLWY1NDk1NTdmNjM0YSIsInVzZXJUeXBlIjoiYWRtaW5pc3RyYXRvciJ9LCJleHAiOjE2MDU0NTE4MDUsImlhdCI6MTYwNTQzMDIwNX0.8lTWl9hGr9GTST7WpEpzKdm_gqhMkf4qUellLx4o5bw' \
--header 'content-type: application/x-www-form-urlencoded' \
--data amount=1050 \
--data chargeId=acf3e6bf-c9de-45b4-a8b6-bf97f92b783a
Response example
{
"status": "success",
"message": "charge configured"
}
While configuring a charge requires you to provide the amount in cents
, performing transactions requires the amount to
be in whole units i.e. shillings
Transacting also requires you to provide an accountNo
, use the email
of the customer as the accountNo
customerType
can be either of agent
, merchant
or subscriber
A deposit is only done by an agent
. You need an agent
token to perform this transaction.
You need the following POST
parameters
amount
, accountNo
and customerType
Curl request example
curl --request POST \
--url http://localhost:6700/api/transaction/deposit \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6ImNjYTdkMjI3LTc0YWUtNGQ0Ny1hYWU4LWEwYWI5NTJhYWMyOCIsInVzZXJUeXBlIjoiYWdlbnQifSwiZXhwIjoxNjA1MzIxODEyLCJpYXQiOjE2MDUzMDAyMTJ9.jFLfjScuvHaOV68n11sRticy2ntzQRhwbNq5E4sPmQI' \
--header 'content-type: application/x-www-form-urlencoded' \
--data amount=400 \
--data [email protected] \
--data customerType=subscriber
Response example
{
"status": "success",
"message": "Success",
"data": {
"message": "Transaction under processing. You will receive a message shortly."
}
}
You need the following POST
parameters
amount
, agentNumber
Use agent email for agentNumber
Curl request example
curl --request POST \
--url http://localhost:6700/api/transaction/withdraw \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6ImNmOWQ4ZjI4LTM1N2UtNGFjNy05YjVmLWVhYTg2MDllNmMyZiIsInVzZXJUeXBlIjoic3Vic2NyaWJlciJ9LCJleHAiOjE2MDU0MTYwNjAsImlhdCI6MTYwNTM5NDQ2MH0.lAJ4WpF2Mnfg52iuTOoPV8nvbHV3JrMQOC-5xXrQ5EE' \
--header 'content-type: application/x-www-form-urlencoded' \
--data amount=40 \
--data [email protected]
Response example
{
"status": "success",
"message": "Success",
"data": {
"message": "Transaction under processing. You will receive a message shortly."
}
}
You need the following POST
parameters
amount
, accountNo
and customerType
Curl request example
curl --request POST \
--url http://localhost:6700/api/transaction/transfer \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6ImNmOWQ4ZjI4LTM1N2UtNGFjNy05YjVmLWVhYTg2MDllNmMyZiIsInVzZXJUeXBlIjoic3Vic2NyaWJlciJ9LCJleHAiOjE2MDUzMjE5MDMsImlhdCI6MTYwNTMwMDMwM30.vLiHdNTr4onTVqUZbLbdpwgbH98VYzHJJU-JKtFOHVg' \
--header 'content-type: application/x-www-form-urlencoded' \
--data amount=30 \
--data [email protected] \
--data customerType=merchant
Response example
{
"status": "success",
"message": "Success",
"data": {
"message": "Transaction under processing. You will receive a message shortly."
}
}
This is just a GET
request, no params
Curl request example
curl --request GET \
--url http://localhost:6700/api/account/balance \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6ImNmOWQ4ZjI4LTM1N2UtNGFjNy05YjVmLWVhYTg2MDllNmMyZiIsInVzZXJUeXBlIjoic3Vic2NyaWJlciJ9LCJleHAiOjE2MDUzNjY3NTMsImlhdCI6MTYwNTM0NTE1M30.-Piib6bXzYqb0S8nLo76SBTyGmWi7UPUMExptIcqBZI'
Response example
{
"status": "success",
"message": "Your current balance is 690",
"data": {
"userID": "cf9d8f28-357e-4ac7-9b5f-eaa8609e6c2f",
"balance": 690
}
}
This is just a GET
request, no params
Curl request example
curl --request GET \
--url http://localhost:6700/api/account/statement \
--header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6Ijk4YmNmMmY1LWRiY2ItNDk1NS04NTU0LTc0OWYxMTVhZjU5OCIsImVtYWlsIjoiIn0sImV4cCI6MTYwNDA2OTE0MywiaWF0IjoxNjA0MDQ3NTQzfQ.IYyclrC66aweehs_A4Sigmc83a27udmPofM2yOeut9Q'
Response example
{
"status": "success",
"message": "mini statement retrieved for the past 5 transactions",
"data": {
"message": "mini statement retrieved for the past 5 transactions",
"userID": "cf9d8f28-357e-4ac7-9b5f-eaa8609e6c2f",
"transactions": [
{
"transactionId": "97c3ff6d-72d5-479d-8838-85a5c32985a2",
"transactionType": "DEPOSIT",
"createdAt": "2020-11-14T01:59:28.613007+03:00",
"creditedAmount": 400,
"debitedAmount": 0,
"userId": "cf9d8f28-357e-4ac7-9b5f-eaa8609e6c2f",
"accountId": "63978e26-9c0d-40eb-a24b-d1ae51e21942"
},
{
"transactionId": "4be4a008-b18e-4d4b-95d3-58b660d5b931",
"transactionType": "TRANSFER",
"createdAt": "2020-11-14T01:59:05.949066+03:00",
"creditedAmount": 0,
"debitedAmount": 30,
"userId": "cf9d8f28-357e-4ac7-9b5f-eaa8609e6c2f",
"accountId": "63978e26-9c0d-40eb-a24b-d1ae51e21942"
},
{
"transactionId": "45da6c6a-03d8-4d58-849a-fd80bbfabbb4",
"transactionType": "TRANSFER",
"createdAt": "2020-11-14T01:57:04.622507+03:00",
"creditedAmount": 0,
"debitedAmount": 40,
"userId": "cf9d8f28-357e-4ac7-9b5f-eaa8609e6c2f",
"accountId": "63978e26-9c0d-40eb-a24b-d1ae51e21942"
}
]
}
}
Tests have not been written for the application but i have very important talks i would share here that i cant recommend enough about how to go about testing the application.
An approach to testing this application would be something in the following lines.
- Test the code in the interactor files
- Test the code in the repository files
The above files carry the bulk of the behaviour of the whole application, they are the business logic of the application. The rest of the files are just implementation details that could change rapidly and the tests written for them would certainly fail after change.
e.g. the http handler functions in the application use gofiber, writing unit tests for them is good but not desired, because gofiber can be replaced with mux easily and that would break your tests.