Skip to main content

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

  1. Formulate Checkout Payload
  2. Encrypt Payload
  3. Redirect to Checkout UI
  4. 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: "johndoe@mail.com",
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.

KeyValue TypeRequiredDescription
msisdnStringYesThis is the mobile number of the customer making payment
account_numberStringYesThis is the reference number under which the payment should reflect
country_codeStringYesThe 3 digit ISO of the country from which we should charge the customer
currency_codeStringYesThe 3 digit ISO code of the currency which we should use to charge the customer
client_codeStringYesThe client code of the merchant initiating the request. Found in my profile section on dashboard under business information
service_codeStringYesThe 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_dateStringYesThe 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_emailStringYesThe email address of the customer making payment
customer_first_nameStringYesThe first name of the customer making payment
customer_last_nameStringYesThe last name of the customer making payment
merchant_transaction_idStringYesA unique transaction reference that you should generate for each transaction
preferred_payment_option_codeStringNoThe preferred code for the payment option which we should pre-select for the customer during the current checkout request
callback_urlStringYesThe webhook URL to which we shall send payment notifications.
success_redirect_urlStringYesThe URL where the customer will be redirected upon making successful payment.
fail_redirect_urlStringYesThe URL where the customer will be redirected to when their payment fails/ time expires.
request_amountStringYesThe amount to be paid by the customer
request_descriptionStringYesA brief description of the payment to be displayed to the customer
invoice_numberStringNoAnother internal referencing the merchant might want to use to track payments.
language_codeStringNoLanguage 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.

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;
}

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.


//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: "johndoe@mail.com",
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>



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.

Let's take a closer look at the parameters returned.

KeyValue TypeDescription
checkout_request_idStringA unique transaction reference generated by Lipad.
merchant_transaction_idStringA unique transaction reference generated by merchant.
eventStringEvent notification i.e
  • full_payment - Full payment received - Complete
  • partial_payment - Partial Payment received - Incomplete
  • unpaid - Unpaid i.e failed payments.
  • expired_with_no_payments - Expired with no payments.
  • expired_with_partial_payments - Expired with partial payment.
overall_payment_statusStringIndicates overall request code i.e
  • 801 - Full payment received - Complete
  • 802 - Partial Payment received - Incomplete
  • 803 - Unpaid i.e failed payments.
  • 820 - Expired with no payments.
  • 822 - Expired with partial payment.
request_amountStringAmount requested.
amount_paidStringAmount paid by customer.
outstanding_amountStringBalance remaining.
service_codeStringThe service for which the payment request was made.
currency_codeStringThe 3 digit ISO code of the currency the checkout was charged.
country_codeStringThe 3 digit ISO of the country which the checkout was made from.
account_numberStringReference number under which the payment reflected.
event_recordJSONArrayPayment which triggered event notification. i.e can be successful or failed payment. Described in the next section.
event_historyJSONArrayArray 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.

NameTypeDescription
eventStringIndicates payment event i.e
  • successful_payment - Payment received
  • failed_payment - Failed transaction
amountStringAmount paid by customer.
extra_dataStringExtra data passed during redirect
client_codeStringMerchant client code
country_codeStringThe 3 digit ISO of the country which the payment was made from.
payer_msisdnStringThis is the mobile number that will be debited. It will receive the USSD or STK push.
payment_dateStringDate which payment was made.
service_codeStringThe service for which the payment request was made.
currency_codeStringThis is the currency to be used to charge the client.
account_numberStringThis is the account number against which the payment is being made.
payment_statusStringIndicates payment status i.e
  • 700 - Successful payment received
  • 701 - Failed transaction
transaction_idStringUnique transaction id generated by Lipad.
payer_narrationStringRequest description initally sent by Merchant.
charge_request_idStringUnique charge id generated by Lipad.
external_referenceStringUnique charge id generated by Checkout.
receiver_narrationStringNarration generated by payment gateway.
payment_method_codeStringThe payment method to be used to charge customer.
payer_transaction_idStringId generated by payment gateway.

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.

StatusDescription
801Full payment received - Complete.
802Partial Payment received - Incomplete.
803Unpaid i.e failed payments, no payments.
820Expired with no payments.
840Payment reversed.
841Payment refunded.
850Jammed.
851Pending.

Payment Status Codes

This flag indicates the individual payment status i.e successful or failed.

StatusDescription
700Successful payment.
701Failed payment.
702Reversed payment.
705Refunded 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
NameType
consumerKeyBody Field
consumerSecretBody Field
Response
NameTypeDescriptionDescription
access_tokenJSON Response ItemAccess token to access other APIs
expiry_inJSON Response ItemToken expiry time in seconds
Error
NameTypeDescription
401HTTP STATUSUNAUTHORIZED

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
KeyValue TypeDescription
merchant_transaction_idQuery ParamA unique transaction reference that you generated when making checkout request
Response Body

Let's take a closer look at the parameters returned.

KeyValue TypeDescription
checkout_request_idStringA unique transaction reference generated by Lipad.
merchant_transaction_idStringA unique transaction reference generated by merchant.
eventStringEvent notification i.e
  • full_payment - Full payment received - Complete
  • partial_payment - Partial Payment received - Incomplete
  • unpaid - Unpaid i.e failed payments.
  • expired_with_no_payments - Expired with no payments.
  • expired_with_partial_payments - Expired with partial payment.
overall_payment_statusStringIndicates overall request code i.e
  • 801 - Full payment received - Complete
  • 802 - Partial Payment received - Incomplete
  • 803 - Unpaid i.e failed payments.
  • 820 - Expired with no payments.
  • 822 - Expired with partial payment.
request_amountStringAmount requested.
amount_paidStringAmount paid by customer.
outstanding_amountStringBalance remaining.
service_codeStringThe service for which the payment request was made.
currency_codeStringThe 3 digit ISO code of the currency the checkout was charged.
country_codeStringThe 3 digit ISO of the country which the checkout was made from.
account_numberStringReference number under which the payment reflected.
event_historyJSONArrayArray 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
NameTypeDescriptionDescription
400HTTP STATUSUNAUTHORIZED