Hosted Checkout
Introduction
The simplest way of collecting payments on your website/webapp is by using Lipad Online Checkout. Lipad will handle the payment and return back to you when done.
This guide will show you how to use Lipad Online Checkout on your website.
Refer to the endpoints section for the correct base url.
Kindly carefully complete the procedures below to start receiving payments on your site.
Steps
- Formulate Checkout Payload
- Encrypt Payload
- Redirect to Checkout UI
- Redirect to back to you
1. Formulate Checkout Payload
You will need to formulate a json payload that will be consumed. We will describe the parameters in detail in the next section.
let payload = {
msisdn: "+254700000000",
account_number: "oid39",
country_code: "KEN",
currency_code: "KES",
client_code: "ABCDEXAMPLE",
due_date: "2022-12-12T13:00:00Z",
customer_email: "[email protected]",
customer_first_name: "John",
customer_last_name: "Doe",
merchant_transaction_id: "txn_id_342",
preferred_payment_option_code: "",
callback_url: "https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6",
request_amount: "100",
request_description: "Dummy merchant transaction",
success_redirect_url:
"https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6",
fail_redirect_url:
"https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6",
invoice_number: "",
language_code: "en",
service_code: "<Client service code>",
};
Let's take a closer look at the parameters.
Key | Value Type | Required | Description |
---|---|---|---|
msisdn | String | Yes | This is the mobile number of the customer making payment |
account_number | String | Yes | This is the reference number under which the payment should reflect |
country_code | String | Yes | The 3 digit ISO of the country from which we should charge the customer |
currency_code | String | Yes | The 3 digit ISO code of the currency which we should use to charge the customer |
client_code | String | Yes | The client code of the merchant initiating the request. Found in my profile section on dashboard under business information |
service_code | String | Yes | The service code assigned to the merchant on the portal. You will first have to add or enable appropriate service on the dashboard under my profile section in services section i.e EATZ_CHECKOUT |
due_date | String | Yes | The date when the request expires with timezone i.e 2024-01-12T08:32:41.545Z or 2024-01-12 08:32:41.545+00 . We accept ISO 8601 date format. |
customer_email | String | Yes | The email address of the customer making payment |
customer_first_name | String | Yes | The first name of the customer making payment |
customer_last_name | String | Yes | The last name of the customer making payment |
merchant_transaction_id | String | Yes | A unique transaction reference that you should generate for each transaction |
preferred_payment_option_code | String | No | The preferred code for the payment option which we should pre-select for the customer during the current checkout request |
callback_url | String | Yes | The webhook URL to which we shall send payment notifications. |
success_redirect_url | String | Yes | The URL where the customer will be redirected upon making successful payment. |
fail_redirect_url | String | Yes | The URL where the customer will be redirected to when their payment fails/ time expires. |
request_amount | String | Yes | The amount to be paid by the customer |
request_description | String | Yes | A brief description of the payment to be displayed to the customer |
invoice_number | String | No | Another internal referencing the merchant might want to use to track payments. |
language_code | String | No | Language code you wish to display instructions for payment. We currently support English i.e "en" |
2. Encrypt Payload
In order to maintain the highest level of security, we only accept encrypted data.To encrypt the payload use the encryption steps described in the code snippets below.
You will be required to have an IV key, access key and consumer secret which can be easily obtained from settings section on the dashboard.
- NodeJS
- Vanilla Javascript
- Java
const crypto = require("crypto");
function LipadEncryption(payload) {
let jsonStringPayload = JSON.stringify(payload);
let key = crypto
.createHash("sha256")
.update(IVKey)
.digest("hex")
.substring(0, 16);
key = Buffer.from(key);
let secret = crypto
.createHash("sha256")
.update(consumerSecret)
.digest("hex")
.substring(0, 32);
secret = Buffer.from(secret);
const cipher = crypto.createCipheriv(algorithm, secret, key);
let encryptedData = cipher.update(jsonStringPayload, "utf-8", "hex");
encryptedData += cipher.final("hex");
let encryptedPayload = Buffer.from(encryptedData, "hex").toString("base64");
return encryptedPayload;
}
<!DOCTYPE html>
<html>
<head>
<title>Client-side Encryption using CryptoJS</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
</head>
<body>
<script>
var IVKey = "YOUR_IV_KEY";
var consumerSecret = "YOUR_CONSUMER_SECRET";
var jsonStringPayload = JSON.stringify("JSON_PAYLOAD"); //Add payload
var key = CryptoJS.SHA256(IVKey).toString().substring(0, 16);
key = CryptoJS.enc.Utf8.parse(key);
var secret = CryptoJS.SHA256(consumerSecret).toString().substring(0, 32);
secret = CryptoJS.enc.Utf8.parse(secret);
var cipher = CryptoJS.AES.encrypt(jsonStringPayload, secret, {
iv: key,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
sigBytes: 16,
});
var hexString = cipher.ciphertext.toString(CryptoJS.enc.Hex);
var hexWordArray = CryptoJS.enc.Hex.parse(hexString);
var encryptedPayload = hexWordArray.toString(CryptoJS.enc.Base64);
//Redirect
</script>
</body>
</html>
public class Encryption {
public static void main(String args[]) {
//The values below can be obtained from dashboard settings under Checkout section
String IVKey = "<Your IV Key>";
String consumerSecret = "<Your Consumer Secret>";
String accessKey = "<Your Access Key>";
String payload=getJsonStringPayload();// String JSON payload
try {
String encrytedPayload=encrypt(IVKey,consumerSecret,payload);
//You can now redirect to our Checkout Page i.e
//https://{{BASE_URL}}/?access_key=<YOUR_ACCESS_KEY>&payload=<ENCRYPTED_PAYLOAD>
//Alternatively
//You can import our lightweight javascript library and pass the encrypted payload to
//availed function Lipad.makePayment((encrytedPayload))
//It will redirect appropriately to our Checkout UI
} catch (Exception var7) {
System.err.println("Exception " + var7);
var7.printStackTrace();
}
}
static String encrypt(String IVKey,String consumerSecret,String payload) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashedIV = digest.digest(IVKey.getBytes(StandardCharsets.UTF_8));
byte[] hashedSecret = digest.digest(consumerSecret.getBytes(StandardCharsets.UTF_8));
IvParameterSpec key = new IvParameterSpec(bytesToHex(hashedIV).substring(0, 16).getBytes());
SecretKeySpec secret = new SecretKeySpec(bytesToHex(hashedSecret).substring(0, 32).getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, key);
byte[] encrypted = cipher.doFinal(payload.getBytes());
String encryptedBase64Payload = Base64.getEncoder().encodeToString(encrypted);
return encryptedBase64Payload;
}
static String getJsonStringPayload() {
//Sample string json object
String jsonStringPayload = "{\"msisdn\":\"2547XXXXXXXX\",\"account_number\":\"2547XXXXXXXX\",\"country_code\":\"KEN\",\"currency_code\":\"KES\",\"client_code\":\"DEMO\",\"customer_email\":\"[email protected]\",\"customer_first_name\":\"John\",\"customer_last_name\":\"Doe\",\"due_date\":\"2022-12-1213:00:00\",\"merchant_transaction_id\":\"1\",\"preferred_payment_option_code\":\"MPESA_KEN\",\"callback_url\":\"https://demo.lipad.io/api/payment-notification\",\"request_amount\":\"10\",\"request_description\":\"Dummymerchanttransaction\",\"success_redirect_url\":\"https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6\",\"fail_redirect_url\":\"https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6\",\"invoice_number\":\"1\",\"language_code\":\"en\",\"service_code\":\"EATZ_CHECKOUT\"}";
return jsonStringPayload;
}
static String bytesToHex(byte[] data) {
if (data == null) {
return null;
} else {
int len = data.length;
String string = "";
for(int i = 0; i < len; ++i) {
if ((data[i] & 255) < 16) {
string = string + "0" + Integer.toHexString(data[i] & 255);
} else {
string = string + Integer.toHexString(data[i] & 255);
}
}
return string;
}
}
}
3. Redirect to Checkout UI
There are two strategies you can use to redirect to our checkout user interface.
- Redirect using your own custom strategy.
- Embed Lipad Checkout Button
a) Custom Redirect (First Option)
After the payload has been encrypted you can redirect to our checkout user interface and pass the access key and encrypted payload as query params as shown below.
https://{{BASE_URL}}/?access_key=<YOUR_ACCESS_KEY>& payload=<ENCRYPTED_PAYLOAD>
b) Embed Lipad Checkout Button (Second Option)
Kindly carefully complete the procedures below to embed Lipad Checkout Button.
- Import Lipad Online Checkout JavaScript library
- Embed Button
- Setup an event listener on button
1. Import Lipad Online Checkout JavaScript library
First you will need to import our lightweight Javascript library on your checkout page.
<script src="https://{{BASE_URL}}/lipad-checkout-library.js"></script>
All done. All that is left to do is include a checkout button and submit a checkout request. See how by reading on.
2. Embed Button
You will need to avail a div with an id of "LipadPaySection" which we will use to render LipadPayButton.
<div id="LipadPaySection" />
The second thing you must do is include a button somewhere on your online store, ideally on the checkout page.You will need to assign an suitable id to the button. It will be used when calling the button in the following section.
Lipad.renderButton({
id: "LipadPayNowButtonID",
});
3. Setup an event listener on button
You will need to add an event listener to the button that we just created by calling it using get element by id and passing in the appropriate id. Then we can call our Lipad.makePayment("formulated_payload") with our formulated payload .
//Set up event listener and call makePaymentFunction
document
.getElementById("LipadPayNowButtonID")
.addEventListener('click', function() {
Lipad.makePayment({access_key:<YOUR_ACCESS_KEY>,payload:<ENCRYPTED_PAYLOAD>});
});
4. Redirect back to you
The customer will be redirected to the success or fail url after full payment has been made or checkout session has expired. When full payment is received, we will redirect the customer to the success_redirect_url passed in the initial payload with the params - merchant_transaction_id,checkout_request_id and overall_payment_status.
https://success-redirect-url.com?merchant_transaction_id=RWUFWNSXMM&checkout_request_id=23&overall_payment_status=801
If no payment or a partial was made and expired we will redirect to the fail redirect url with the params: merchant_transaction_id,checkout_request_id and overall_payment_status.
https://fail-redirect-url.com?merchant_transaction_id=RWUFWNSXMM&checkout_request_id=24&overall_payment_status=820
And thats it! Lipad will redirect the customer to our payment page and faciliate the transaction.
Full Example
The code snippets below summarize the processes described above.
- JavaScript
- Java
//Formulate Payload
let formulatedPayload = {
msisdn: "+254700000000",
account_number: "oid39",
country_code: "KEN",
currency_code: "KES",
client_code: "ABCDEXAMPLE",
due_date: "2022-12-12 13:00:00",
customer_email: "[email protected]",
customer_first_name: "John",
customer_last_name: "Doe",
merchant_transaction_id: "txn_id_342",
preferred_payment_option_code: "",
callback_url: "https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6",
request_amount: "100",
request_description: "Dummy merchant transaction",
success_redirect_url: "https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6",
fail_redirect_url: "https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6",
invoice_number: "",
language_code: "en",
service_code: "<Client service code>",
}
//Encrypt Payload
const crypto = require("crypto");
function LipadEncryption(payload){
let jsonStringPayload=JSON.stringify(payload);
let key = crypto.createHash("sha256").update(IVKey).digest("hex").substring(0, 16);
key = Buffer.from(key);
let secret = crypto.createHash("sha256").update(consumerSecret).digest("hex").substring(0, 32);
secret = Buffer.from(secret);
const cipher = crypto.createCipheriv(algorithm, secret, key);
let encryptedData = cipher.update(jsonStringPayload, "utf-8", "hex");
encryptedData += cipher.final("hex");
let encryptedPayload = Buffer.from(encryptedData, 'hex').toString('base64');
return encryptedPayload;
}
let encryptedPayload=LipadEncryption(formulatedPayload);
//Embed Button
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body style="text-align: center;">
<div id="LipadPaySection">
</div>
// Import checkout script
<script src="https://{{BASE_URL}}/lipad-checkout-library.js"></script>
<script>
let payNowButtonID="pay_now_button";
Lipad.renderButton({id:payNowButtonID});
document
.getElementById(payNowButtonID)
.addEventListener('click', function() {
Lipad.makePayment({payload:encryptedPayload,access_key:access_key});
});
</script>
</body>
</html>
public class Encryption {
public static void main(String args[]) {
//The values below can be obtained from dashboard settings under Checkout section
String IVKey = "<Your IV Key>";
String consumerSecret = "<Your Consumer Secret>";
String accessKey = "<Your Access Key>";
String payload=getJsonStringPayload();// String JSON payload
try {
String encrytedPayload=encrypt(IVKey,consumerSecret,payload);
//Once encrypted you will import our lightweight javascript library and pass the encrypted payload to
//availed function Lipad.makePayment((encrytedPayload))
//It will redirect appropriately to our Checkout UI
} catch (Exception var7) {
System.err.println("Exception " + var7);
var7.printStackTrace();
}
}
static String encrypt(String IVKey,String consumerSecret,String payload) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashedIV = digest.digest(IVKey.getBytes(StandardCharsets.UTF_8));
byte[] hashedSecret = digest.digest(consumerSecret.getBytes(StandardCharsets.UTF_8));
IvParameterSpec key = new IvParameterSpec(bytesToHex(hashedIV).substring(0, 16).getBytes());
SecretKeySpec secret = new SecretKeySpec(bytesToHex(hashedSecret).substring(0, 32).getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, key);
byte[] encrypted = cipher.doFinal(payload.getBytes());
String encryptedBase64Payload = Base64.getEncoder().encodeToString(encrypted);
return encryptedBase64Payload;
}
static String getJsonStringPayload() {
//Sample string json object
String jsonStringPayload = "{\"msisdn\":\"2547XXXXXXXX\",\"account_number\":\"2547XXXXXXXX\",\"country_code\":\"KEN\",\"currency_code\":\"KES\",\"client_code\":\"DEMO\",\"customer_email\":\"[email protected]\",\"customer_first_name\":\"John\",\"customer_last_name\":\"Doe\",\"due_date\":\"2022-12-1213:00:00\",\"merchant_transaction_id\":\"1\",\"preferred_payment_option_code\":\"MPESA_KEN\",\"callback_url\":\"https://demo.lipad.io/api/payment-notification\",\"request_amount\":\"10\",\"request_description\":\"Dummymerchanttransaction\",\"success_redirect_url\":\"https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6\",\"fail_redirect_url\":\"https://webhook.site/6c933f61-d6da-4f8e-8a44-bf0323eb8ad6\",\"invoice_number\":\"1\",\"language_code\":\"en\",\"service_code\":\"EATZ_CHECKOUT\"}";
return jsonStringPayload;
}
static String bytesToHex(byte[] data) {
if (data == null) {
return null;
} else {
int len = data.length;
String string = "";
for(int i = 0; i < len; ++i) {
if ((data[i] & 255) < 16) {
string = string + "0" + Integer.toHexString(data[i] & 255);
} else {
string = string + Integer.toHexString(data[i] & 255);
}
}
return string;
}
}
}
Callbacks
Webhooks are an important part of your payment integration. They allow us to notify you about events that happen on your checkout instance, such as a successful payment or a failed transaction.As none of these operations are controlled by your application and are all asynchronous, you won't know when they are finished unless we let you know or you check back later.
When payments are received, we will send payments notifications to the specified callback url. Keep in mind that a single checkout instance can have multiple payments made such as one could have one failed payment followed by a successful payment.
You will use the "overall_payment_status" flag to know whether full payment has been completed or not. The overall status flag serves to indicate the cumulative status of the checkout. In order to know the cause of the trigger i.e successful_payment or failed_payment you will use the event record child and see the payment status flag.
Be sure to check the overall payment status and payment status section section to see various statuses.
Incase we don't receive any payments, we will send you a callback when the checkout instance expires.
Additionally, we will require you to respond with an acknowledgment described in the RESPONSE tab.
- Callback
- Response
Let's take a closer look at the parameters returned.
Key | Value Type | Description |
---|---|---|
checkout_request_id | String | A unique transaction reference generated by Lipad. |
merchant_transaction_id | String | A unique transaction reference generated by merchant. |
event | String | Event notification i.e
|
overall_payment_status | String | Indicates overall request code i.e
|
request_amount | String | Amount requested. |
amount_paid | String | Amount paid by customer. |
outstanding_amount | String | Balance remaining. |
service_code | String | The service for which the payment request was made. |
currency_code | String | The 3 digit ISO code of the currency the checkout was charged. |
country_code | String | The 3 digit ISO of the country which the checkout was made from. |
account_number | String | Reference number under which the payment reflected. |
event_record | JSONArray | Payment which triggered event notification. i.e can be successful or failed payment. Described in the next section. |
event_history | JSONArray | Array of payments made to the checkout request. Described in the next section. |
Sample callback
{
"event": "full_payment",
"checkout_request_id": "1431",
"merchant_transaction_id": "1676974504058",
"request_amount": "2.00",
"amount_paid": "2.00",
"outstanding_amount": "2.00",
"currency_code": "KES",
"country_code": "KEN",
"service_code": "EATZ_CHECKOUT",
"account_number": "2547XXXXXXXX",
"overall_payment_status": 801,
"overall_payment_description": "full_payment",
"request_description": "Dummy merchant transaction",
"event_record": {
"event": "successful_payment",
"amount": "2.00",
"extra_data": "extraData",
"client_code": "ATL-6815O1F",
"country_code": "KEN",
"payer_msisdn": "2547XXXXXXXX",
"payment_date": "2023-02-21T10:15:47.862Z",
"service_code": "EATZ_CHECKOUT",
"currency_code": "KES",
"account_number": "2547XXXXXXXX",
"payment_status": 700,
"transaction_id": "2019",
"payer_narration": "2547XXXXXXXX",
"charge_request_id": "873",
"external_reference": "1431",
"receiver_narration": "The service request is processed successfully.",
"payment_method_code": "MPESA_KEN",
"payer_transaction_id": "RBL35TKMUH"
},
"event_history": [
{
"event": "failed_payment",
"amount": "2.00",
"extra_data": "extraData",
"client_code": "ATL-6815O1F",
"country_code": "KEN",
"payer_msisdn": "2547XXXXXXXX",
"payment_date": "2023-02-21T10:15:26.499Z",
"service_code": "EATZ_CHECKOUT",
"currency_code": "KES",
"account_number": "2547XXXXXXXX",
"payment_status": 701,
"transaction_id": "2018",
"merchant_ack_id": "p3huy",
"payer_narration": "2547XXXXXXXX",
"charge_request_id": "872",
"merchant_ack_code": "170",
"external_reference": "1431",
"receiver_narration": "Request cancelled by user",
"payment_method_code": "MPESA_KEN",
"payer_transaction_id": "NOT AVAILABLE",
"merchant_ack_description": "Payment Succesful"
},
{
"event": "successful_payment",
"amount": "2.00",
"extra_data": "extraData",
"client_code": "ATL-6815O1F",
"country_code": "KEN",
"payer_msisdn": "2547XXXXXXXX",
"payment_date": "2023-02-21T10:15:47.862Z",
"service_code": "EATZ_CHECKOUT",
"currency_code": "KES",
"account_number": "2547XXXXXXXX",
"payment_status": 700,
"transaction_id": "2019",
"payer_narration": "2547XXXXXXXX",
"charge_request_id": "873",
"external_reference": "1431",
"receiver_narration": "The service request is processed successfully.",
"payment_method_code": "MPESA_KEN",
"payer_transaction_id": "RBL35TKMUH"
}
]
}
Sample event record
Under a single checkout instance there can exist several payments i.e successful,failed. Each callback will consist of cumulative status in the parent body, an event record and an event history. The event record body is used to indicate if the single payment was successful or failed. The event history shows all the preceeding events sent inclusive of the current.
{
"event": "successful_payment",
"amount": "2.00",
"extra_data": "extraData",
"client_code": "ATL-6815O1F",
"country_code": "KEN",
"payer_msisdn": "2547XXXXXXXX",
"payment_date": "2023-02-21T10:15:47.862Z",
"service_code": "EATZ_CHECKOUT",
"currency_code": "KES",
"account_number": "2547XXXXXXXX",
"payment_status": "700",
"transaction_id": "2019",
"payer_narration": "2547XXXXXXXX",
"charge_request_id": "873",
"external_reference": "1431",
"receiver_narration": "The service request is processed successfully.",
"payment_method_code": "MPESA_KEN",
"payer_transaction_id": "RBL35TKMUH"
}
Taking a closer look at the parameters.
Name | Type | Description |
---|---|---|
event | String | Indicates payment event i.e
|
amount | String | Amount paid by customer. |
extra_data | String | Extra data passed during redirect |
client_code | String | Merchant client code |
country_code | String | The 3 digit ISO of the country which the payment was made from. |
payer_msisdn | String | This is the mobile number that will be debited. It will receive the USSD or STK push. |
payment_date | String | Date which payment was made. |
service_code | String | The service for which the payment request was made. |
currency_code | String | This is the currency to be used to charge the client. |
account_number | String | This is the account number against which the payment is being made. |
payment_status | String | Indicates payment status i.e
|
transaction_id | String | Unique transaction id generated by Lipad. |
payer_narration | String | Request description initally sent by Merchant. |
charge_request_id | String | Unique charge id generated by Lipad. |
external_reference | String | Unique charge id generated by Checkout. |
receiver_narration | String | Narration generated by payment gateway. |
payment_method_code | String | The payment method to be used to charge customer. |
payer_transaction_id | String | Id generated by payment gateway. |
Key | Value Type | Required | Description |
---|---|---|---|
checkout_request_id | Long | Yes | A unique transaction reference generated by Lipad. |
merchant_transaction_id | String | Yes | A unique transaction reference generated by merchant. |
acknowledgement_code | String | Yes | A status code that indicates whether a request was accepted or rejected.
|
acknowledgement_description | String | Yes | A description that indicates whether a request was accepted or rejected. |
merchant_ack_id | String | Yes | A unique transaction reference generated by merchant acknowledgement. |
{
"checkout_request_id": "1",
"merchant_transaction_id": "adsddsz",
"acknowledgement_code": "900",
"acknowledgement_description": "Received",
"merchant_ack_id": "WQHUDJSAKSJ"
}
Overall Payment Status Codes
A single checkout instance can have several payments. This flag indicates the cumulative status i.e when a partial payment is made it will indicate payment is incompelete.
Status | Description |
---|---|
801 | Full payment received - Complete. |
802 | Partial Payment received - Incomplete. |
803 | Unpaid i.e failed payments, no payments. |
820 | Expired with no payments. |
840 | Payment reversed. |
841 | Payment refunded. |
850 | Jammed. |
851 | Pending. |
Payment Status Codes
This flag indicates the individual payment status i.e successful or failed.
Status | Description |
---|---|
700 | Successful payment. |
701 | Failed payment. |
702 | Reversed payment. |
705 | Refunded payment. |
Authentication
An authorisation header is required for all calls made to the Checkout API. To generate an access token you will need to will need to obtain a consumer key and consumer secret from the dashboard under settings.
POST Endpoint:https://{{BASE_URL}}/api/v1/api-auth/access-token
Once an access_token is generated, it should be passed as a header param i.e Authorization = Bearer <ACCESS_TOKEN>
Request Body
Name | Type |
---|---|
consumerKey | Body Field |
consumerSecret | Body Field |
Response
Name | Type | Description | Description |
---|---|---|---|
access_token | JSON Response Item | Access token to access other APIs | |
expiry_in | JSON Response Item | Token expiry time in seconds |
Error
Name | Type | Description |
---|---|---|
401 | HTTP STATUS | UNAUTHORIZED |
Get Checkout Status
You can query the status of the checkout request by making a get request to the following end point with an access token. To generate an access token check out the authentication section.
GET Endpoint:https://{{BASE_URL}}/api/v1/checkout/request/status
Request Query Param
Key | Value Type | Description |
---|---|---|
merchant_transaction_id | Query Param | A unique transaction reference that you generated when making checkout request |
Response Body
Let's take a closer look at the parameters returned.
Key | Value Type | Description |
---|---|---|
checkout_request_id | String | A unique transaction reference generated by Lipad. |
merchant_transaction_id | String | A unique transaction reference generated by merchant. |
event | String | Event notification i.e
|
overall_payment_status | String | Indicates overall request code i.e
|
request_amount | String | Amount requested. |
amount_paid | String | Amount paid by customer. |
outstanding_amount | String | Balance remaining. |
service_code | String | The service for which the payment request was made. |
currency_code | String | The 3 digit ISO code of the currency the checkout was charged. |
country_code | String | The 3 digit ISO of the country which the checkout was made from. |
account_number | String | Reference number under which the payment reflected. |
event_history | JSONArray | Array of payments made to the checkout request. Described in the next section. |
Sample callback
{
"event": "full_payment",
"checkout_request_id": "1431",
"merchant_transaction_id": "1676974504058",
"request_amount": "2.00",
"amount_paid": "2.00",
"outstanding_amount": "2.00",
"currency_code": "KES",
"country_code": "KEN",
"service_code": "EATZ_CHECKOUT",
"account_number": "2547XXXXXXXX",
"overall_payment_status": 801,
"overall_payment_description": "full_payment",
"request_description": "Dummy merchant transaction",
"event_history": [
{
"event": "failed_payment",
"amount": "2.00",
"extra_data": "extraData",
"client_code": "ATL-6815O1F",
"country_code": "KEN",
"payer_msisdn": "2547XXXXXXXX",
"payment_date": "2023-02-21T10:15:26.499Z",
"service_code": "EATZ_CHECKOUT",
"currency_code": "KES",
"account_number": "2547XXXXXXXX",
"payment_status": 701,
"transaction_id": "2018",
"merchant_ack_id": "p3huy",
"payer_narration": "2547XXXXXXXX",
"charge_request_id": "872",
"merchant_ack_code": "170",
"external_reference": "1431",
"receiver_narration": "Request cancelled by user",
"payment_method_code": "MPESA_KEN",
"payer_transaction_id": "NOT AVAILABLE",
"merchant_ack_description": "Payment Succesful"
},
{
"event": "successful_payment",
"amount": "2.00",
"extra_data": "extraData",
"client_code": "ATL-6815O1F",
"country_code": "KEN",
"payer_msisdn": "2547XXXXXXXX",
"payment_date": "2023-02-21T10:15:47.862Z",
"service_code": "EATZ_CHECKOUT",
"currency_code": "KES",
"account_number": "2547XXXXXXXX",
"payment_status": 700,
"transaction_id": "2019",
"payer_narration": "2547XXXXXXXX",
"charge_request_id": "873",
"external_reference": "1431",
"receiver_narration": "The service request is processed successfully.",
"payment_method_code": "MPESA_KEN",
"payer_transaction_id": "RBL35TKMUH"
}
]
}
Error
Name | Type | Description | Description |
---|---|---|---|
400 | HTTP STATUS | UNAUTHORIZED |
Direct Charge API
You can conveniently collect payments through cards, banks, or any of our supported ways with just one integration using Lipad Checkout. It however comes with the Lipad checkout experience.
There are occasions when you want additional control that works with your app. Direct charge enters the picture here. We supply the APIs for clients to be charged, but you are responsible for gathering their payment information and bringing your own UI and payment flow. You can control and modify the customer experience however you like.
There are three main stages in direct charge:
- Initiate the payment: Send the charge request to the appropriate charge endpoints.
- Authorize the charge: Customer interacts with the payment prompts and completes the paymnent.
- Post callback payment verification: As a failsafe, you'll call our Status API to verify that the payment was successful before giving value.
Lipad Direct charge Transaction Flow
Direct Charge Transaction Flow Description
- The charge request originator (Client) captures and sets the API required parameters and sends the request to the respective charge endpoint.
- The API receives the request, validates it and sends an acknowledgement response back to the originator (Client).
- A charge request is initiated via the Payments Partner responsible eg Airtel Money or M-Pesa
- The Customer authorizes the transaction.
- The Payments Partner debits the customer and Credits LIPAD Wallet, sends payment result to LIPAD
- LIPAD processes the result, credits the Originator's LIPAD Wallet with funds from the payment.
- LIPAD Charge Service then sends Payment Notification webhook to the webhook URL registered on our system. (Type: Payments Notifications)
Authentication
All requests made to LIPAD Direct Charge APIs will require token authentication.
Authentication requests are send to:
POST https://dev.lipad.io/v1/auth
Authentication Request body
{
"consumer_key": "Xjpe948ixgdhlUrRNlOwc",
"consumer_secret": "hNjZK2JVm4Uc1whgskjsiY9G"
}
Response Body
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiIxNiIsImRlc2lnbmF0aW9uIjoiQXBpIFVzZXIiLCJhdXRob3JpemVkIjp0cnVlLCJpYXQiOjE2Nzc3MDY4ODgsImV4cCI6MTY3NzcxMjg4OH0.kADugWTjIrXokYYhDTXg4idmsLy6pUTv6N0Hi-ZMkrI",
"expiresIn": 6000
}
Use the token returned under access_token for all requests made to the Payouts APIs. The token should be passed as a Request Header x-access-token
. The token will be valid for up to 1 hour.
Supported Payment Methods
Broadly we support the following payment method types in our Direct Charge Api:
- Mobile Money
Mobile Money
We support several MNOs(mobile network providers) across the continent. You can find several supported MNOs in the next section
The following table provides information about the Mobile Network Operators currently supported by our Direct Mobile Money Charge API.
Kenya
Mobile Network Operator(MNO) | Lipad Short Code | Country | Currency |
---|---|---|---|
Safaricom MPESA | MPESA_KEN | KEN | KES |
Airtel Kenya | AIRTEL_KEN | KEN | KES |
MPESA
This payment method allows you to receive KES payments via Mpesa.
POST https://dev.charge.lipad.io/v1/mobile-money/charge
The following is a payload structure expected by the mobile money charge endpoint;
{
"external_reference": "123456",
"origin_channel_code": "API",
"originator_msisdn": "2547****56",
"payer_msisdn": "254******56",
"service_code": "<client service code>",
"account_number": "123456789",
"invoice_number": "",
"currency_code": "KES",
"amount": "100",
"add_transaction_charge": false,
"transaction_charge": "0",
"payment_method_code": "<payment method code>",
"paybill": "123456",
"extra_data": {
},
"description": "Payment by 254700123456",
"notify_client": false,
"notify_originator": false
}
Request Body Parameters description
Parameter | Data Type | Null | Description |
---|---|---|---|
external_reference | string | NO | This is the unique reference generated by the external/internal party requesting charge. It will be used to send a callback once charge is successful. |
origin_channel_code | string | YES | This is an optional parameter indicating where the request to charge originated from. It has to be a valid channel code (API, WEB, USSD, APP) |
originator_msisdn | string | NO | This is the mobile number of the individual initiating the request for charge. |
payer_msisdn | string | NO | This is the mobile number that will be debited. It will receive the USSD or STK push. |
service_code | string | NO | This is the service against which charge is being made. Once successful, a payment will be logged against this service. |
account_number | string | NO | This is the account number against which the payment is being made. |
invoice_number | string | YES | This is an internal invoice number to be used by the originating system. |
currency_code | string | YES | This is the currency to be used to charge the client. |
amount | string | NO | This is the service amount to be charged from the client. |
add_transaction_charge | boolean | NO | Indicates whether the customer should be charged extra fees. |
transaction_charge | string | NO | The transaction charge amount to be applied if the addTransactionCharge parameter is set to true. |
payment_method_code | string | NO | The code to be used to identify the payment method. See LIPAD Short Codes on Supported MNOs section |
paybill | string | NO | Parameter to indicate the paybill or till number or business name/code to be used when invoking the respective telcos. Use LIPAD_DEFAULT if none is configured |
extra_data | json | YES | JSON array of any additional information to be captured during charge. The same information will be relayed back when sending payment callbacks. |
description | string | YES | Description of the payment. |
notify_client | boolean | NO | Tells charge whether to send an IPN to the owner client of the service using the callback URL configured under the client. |
notify_originator | boolean | NO | Tells charge whether to send an IPN to the client who originated the charge request (Identified by credentials used to invoke charge). The IPN is sent using the callback URL configured under the client. |
Request Response Body
Once the mobile money charge is requested, an acknowledgement is sent back to the initiating channel or customer. The response could be successful or failed.
*Note: Be sure to checkout the status codes section to see the various statusCodes that can be returned.*
The payload structure shown below demonstrates a successful charge response;
{
"status_code": 102,
"status_description": "Request to charge customer was successfully placed",
"total_amount": "1900",
"service_amount": "1800",
"transaction_charge": "100",
"charge_request_id": 123456789,
"external_reference": “123456”
}
The following table describes the response payload above.
Parameter | Data Type | Null | Description |
---|---|---|---|
status_code | int | NO | Status of the charge request. |
status_description | string | NO | Description of the charge status. |
total_amount | string | NO | Total amount charged from the customer. |
service_amount | string | NO | Service amount from the total amount paid. |
transaction_charge | string | NO | Charge amount from the total amount paid. |
charge_request_id | int | NO | Internal Charge request ID. |
external_reference | string | NO | This is the unique reference generated by the external/internal party that requested the charge. |
Error Response Body
{
"status_code": 172,
"status_description": "Could not initiate Charge Request",
"message": "Missing or Invalid access token"
}
Error Response Parameters Description
Parameter | Description |
---|---|
status_code | Status of the charge request. See Status Codes Section |
status_description | Description of the charge status |
message | Error specific description |
Callbacks
Once payment is made against a charge request by the customer, a callback shall be sent back to the client of the service or originator of the payment request. This payment notification is sent back to the client depending on the notify_client or notify_originator parameters that were sent during charge initiation.
NOTE:
- All callbacks from LIPAD employ Basic authentication with the username and password registered alongside the callback url. See Basic Authentication rfc for guidance.
- Do not rely entirely on webhooks for Charge request status. Use the LIPAD Charge Status API to verify a Payout Status
- Acknowledge webhook receipt with the following response
Payment result acknowledgement Response Body
{
"charge_request_id": "123",
"acknowledgement_code": "900",
"acknowledgement_description": "payment accepted",
"payment_id": "12345"
}
Payment result acknowledgement Response Body Parameters description
Parameter | Description |
---|---|
charge_request_id | The incoming charge request ID |
acknowledgement_code | A status code that indicates whether a request was accepted or rejected.
|
acknowledgement_description | Callback receipt acknowledgement status description |
payment_id | The incoming IPN payment ID |
NOTE: Unacknowledged callbacks will be retried 3 times over the next 48 hours after which no further webhooks will be sent.
Payment Result Body
{
"event": "successful_payment",
"amount": "2.00",
"extra_data": "extraData",
"client_code": "ATL-6815O1F",
"country_code": "KEN",
"payer_msisdn": "2547XXXXXXXX",
"payment_date": "2023-02-21T10:15:47.862Z",
"service_code": "EATZ_CHECKOUT",
"currency_code": "KES",
"account_number": "2547XXXXXXXX",
"payment_status": 700,
"transaction_id": "2019",
"payer_narration": "2547XXXXXXXX",
"charge_request_id": "873",
"external_reference": "1431",
"receiver_narration": "The service request is processed successfully.",
"payment_method_code": "MPESA_KEN",
"payer_transaction_id": "RBL35TKMUH"
}
Result Parameters description
Name | Type | Description |
---|---|---|
event | String | Indicates payment event e.g failed_payment or successful_payment |
amount | String | Amount paid by customer. |
extra_data | String | Extra data passed during redirect |
client_code | String | Merchant client code |
country_code | String | The 3 digit ISO of the country which the payment was made from. |
payer_msisdn | String | This is the mobile number that will be debited. It will receive the USSD or STK push. |
payment_date | String | Date which payment was made. |
service_code | String | The service for which the payment request was made. |
currency_code | String | This is the currency to be used to charge the client. |
account_number | String | This is the account number against which the payment is being made. |
payment_status | String | Indicates payment status i.e
|
transaction_id | String | Unique transaction id generated by Lipad. |
payer_narration | String | Request description initally sent by Merchant. |
charge_request_id | String | Unique charge id generated by Lipad. |
external_reference | String | Unique charge id generated by Checkout. |
receiver_narration | String | Narration generated by payment gateway. |
payment_method_code | String | The payment method to be used to charge customer. |
payer_transaction_id | String | Id generated by payment gateway. |
Charge Request Status
For status verification or in the event when result callbacks fail, the Charge Request Status API can be employed. Requests to check payout status are sent to:
GET https://dev.charge.lipad.io/v1/transaction/{charge_request_id}/status
NOTE: For charge status Response Body and Parameters description, see Callbacks section.
Charge Request-Result Payment Status Codes
This flag indicates the individual payment status i.e successful or failed.
Status | Description |
---|---|
700 | Successful payment made. |
701 | Failed payment made |
702 | Payment was Reversed |
703 | Payment is Pending Processing |
704 | Payment is Jammed. Requires manual intervention to continue processing |
Charge Request-Response Status Codes
The status codes below will be used to indicate the status of the charge request made.
Status Code | Status Message | Comment |
---|---|---|
176 | CHARGE POSTED SUCCESSFULLY | This is the success code for posting a charge request. |
173 | GENERIC SUCCESS | GENERIC SUCCESS |
174 | GENERIC FAILURE | GENERIC FAILURE |
172 | GENERIC VALIDATION FAILURE | This is the generic code to be returned for generic validations. |
132 | Invalid Credentials. Authentication failed | Invalid Credentials. Authentication failed |
131 | Authentication was a success | Authentication was a success |
120 | INVOICE ACCOUNT NUMBER NOT SPECIFIED | This is the error code for when the invoice account number is not specified. |
110 | INVALID CUSTOMER MSISDN | This is the error code for a failed msisdn |
109 | CUSTOMER MSISDN MISSING | Missing Payer MSISDN |
115 | INVALID CURRENCY CODE | Currency error |
Split Settlements
Split Settlements enables merchants to allocate portions of a transaction amount to different partner accounts or entities. This is particularly useful for platforms with multiple service providers, affiliate programs or shared revenue agreements. Each partner receives a predefined share of the transaction amount directly. Additionally, some partner accounts are dynamic and must be included in every request.
Charge Requests with Split Settlements
All charge requests, whether through Checkout Redirect or Direct Charge API, include an extra_data
property containing metadata about the specific transaction. For transactions involving split settlements, the following should be added to extra_data
{
"extra_data": {
"split_settlements": [
{
"account": "100008898",
"amount": "100",
"reason": "LPG cylinder delivery costs"
}
]
}
}
Example Request
{
"external_reference": "testref12345567",
"origin_channel_code": "API",
"originator_msisdn": "25410000000",
"payer_msisdn": "25410000000",
"payer_email": "[email protected]",
"payer_name": "John",
"client_code": "Doe",
"service_code": "TESTCHECKOUT",
"account_number": "01000000000",
"invoice_number": "test",
"currency_code": "KES",
"country_code": "KEN",
"amount": 1000,
"add_transaction_charge": false,
"transaction_charge": 0,
"payment_method_code": "MPESA",
"paybill": "111111",
"extra_data": {
"split_settlements": [
{
"account": "10000191",
"amount": 500,
"reason": "LPG cylinder delivery costs"
}
]
},
"description": "Payment by 254700123456",
"notify_client": true,
"notify_originator": true
}
Payment Service Processing Confirmation
Endpoints:
- POST
https://api.lipad.io/transactions/acknowledge
(production) - POST
https://dev.lipad.io/transactions/acknowledge
(sandbox)
Split settlements follow the service processing confirmation step. Once the merchant confirms the transaction service, the system validates and allocates the split settlements. This ensures all partner accounts receive their respective amounts as per the transaction details.
Confirmation Workflow
- Transaction Processing: The payment is processed through the designated payment method.
- Service Confirmation: The system receives a confirmation from the payment service, indicating the success or failure of the transaction.
- Settlement Allocation: Upon successful confirmation, the split settlements are processed, and funds are distributed to the specified partner accounts.
Transaction Confirmation Payload
{
"payment_id":"1234",
"status":"SUCCESSFUL",
"narration":"Service processed successfully"
}
Transaction processing status:
- FAILED
- SUCCESSFUL
Confirmation Response
{
"payment_id": "1234",
"message": "Success"
}