Hosted Checkout
Introduction
The simplest way of collecting payments on your website/webapp is by using Kasapay Online Checkout. Kasapay will handle the payment and return back to you when done. This guide will show you how to use Kasapay Online Checkout on your website.
A simple four step process
- Formulate Checkout Payload
- Encrypt Payload
- Redirect to Checkout UI
- Redirect to back to you
NOTE:
- You must be registered as a client and have access to the Dashboard.
- In the Dashboard, go to Settings to find your:
- Client Code
- Service Code
- Country Code
- Currency Code
- Refer to the Endpoints section for the correct Base URL.
- For any challenges, be sure to check the Frequently Asked Questions section.
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 = {
client_code: "<YOUR_CLIENT_CODE>",
service_code: "<YOUR_CLIENT_SERVICE_CODE>",
country_code: "<COUNTRY_CODE>", // Country code attached to client service
currency_code: "<CURRENCY_CODE>", // Currency code attached to client service
merchant_transaction_id: "<YOUR_UNIQUE_TRANSACTION_IDENTIFIER>",
msisdn: "+254700000000",
account_number: "oid39",
due_date: "2022-12-12T13:00:00Z",
customer_email: "[email protected]",
customer_first_name: "John",
customer_last_name: "Doe",
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",
};
Let's take a closer look at the parameters.
Key | Value Type | Required | Description | |
---|---|---|---|---|
client_code | String | Yes | The client code of the merchant initiating the request. You can find this by going to the Dashboard > Settings > Profile > Business Information. | |
service_code | String | Yes | The service code is a unique identifier for a specific product (e.g., Checkout) enabled on your client. Services allow merchants to perform specific functions like accepting payments. To find the service code, go to Dashboard > Settings > Services. If there are no services please contact Administrator to create a service for you. | |
country_code | String | Yes | The country which the service is attached. You can find this in Dashboard > Settings > Services under country code column. | |
currency_code | String | Yes | The currency which the service is attached. You can find this in Dashboard > Settings > Services under currency code column. | |
merchant_transaction_id | String | Yes | A unique transaction reference that you should generate for each transaction. You can use this to reconcile with your system. | |
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. | |
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. | |
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.
NOTE: You will be required to have an IV key, access key and consumer secret which can be easily obtained from Dashboard > Settings > Api Keys.
You will have to encrypt the payload using the utility functions/classes defined below.
- NodeJS
- Vanilla Javascript
- PHP
- Java
const crypto = require("crypto");
function KasapayEncryption(payload, IVKey, consumerSecret) {
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("aes-256-cbc", secret, key);
let encryptedData = cipher.update(jsonStringPayload, "utf-8", "hex");
encryptedData += cipher.final("hex");
let encryptedPayload = Buffer.from(encryptedData, "hex").toString("base64");
return encryptedPayload;
//You can now redirect to our Checkout Page
//{{CHECKOUT_ENDPOINT}}/?access_key=<YOUR_ACCESS_KEY>&payload=<ENCRYPTED_PAYLOAD>
}
<!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);
//You can now redirect to our Checkout Page
//{{CHECKOUT_ENDPOINT}}/?access_key=<YOUR_ACCESS_KEY>&payload=<ENCRYPTED_PAYLOAD>
</script>
</body>
</html>
function encrypt($payload, $IVKey, $consumerSecret): string
{
$key = substr(hash('sha256', $IVKey), 0, 16);
$secret = substr(hash('sha256', $consumerSecret), 0, 32);
$cipher = openssl_encrypt(json_encode($payload), "AES-256-CBC", $secret, OPENSSL_RAW_DATA, $key);
return base64_encode($cipher);
}
$payload = [];// payload
$ivKey = 'your-iv-key-here';
$consumerSecret = 'your-consumer-secret-here';
$encrypted = encrypt($payload, $ivKey, $consumerSecret);
//You can now redirect to our Checkout Page
//{{CHECKOUT_ENDPOINT}}/?access_key=<YOUR_ACCESS_KEY>&payload=<ENCRYPTED_PAYLOAD>
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
//{{CHECKOUT_ENDPOINT}}/?access_key=<YOUR_ACCESS_KEY>&payload=<ENCRYPTED_PAYLOAD>
} 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.kasapay.com/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
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.
CHECKOUT_ENDPOINT/?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! Kasapay 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 KasapayEncryption(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=KasapayEncryption(formulatedPayload);
//Embed Button
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body style="text-align: center;">
<div id="KasapayPaySection">
</div>
// Import checkout script
<script src="{{CHECKOUT_ENDPOINT}}/kasapay-checkout-library.js"></script>
<script>
let payNowButtonID="pay_now_button";
Kasapay.renderButton({id:payNowButtonID});
document
.getElementById(payNowButtonID)
.addEventListener('click', function() {
Kasapay.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 Kasapay.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.kasapay.com/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 Kasapay. |
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 Kasapay. |
payer_narration | String | Request description initally sent by Merchant. |
charge_request_id | String | Unique charge id generated by Kasapay. |
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 Kasapay. |
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"
}
Get Checkout Status
First, you need to generate an access token in order to access the get status endpoint. To generate an access token, you need to obtain a consumer key and consumer secret found in the Dashboard > Settings > Api Keys.
POST Endpoint:CHECKOUT_ENDPOINT/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:CHECKOUT_ENDPOINT/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 Kasapay. |
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 | Invalid token |
Overall Checkout 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. |
End Points
Sandbox
SANDBOX ENDPOINT = https://sandbox.checkout.kasapay.com
Production
PRODUCTION ENDPOINT = https://checkout.kasapay.com
📘 Frequently Asked Questions (FAQ)
1. Where can I find my client code, service code, country code and currency code?
You can find your client code, service code,country code, and currency code in the Dashboard under the My Settings section.
- Log in to your dashboard.
- Navigate to My Settings.
- Look for the identifiers listed under your account or profile information.
If you don't see this section or need access, please contact your administrator or support.
2. Why do I get an [object Object] error when redirecting?
The [object Object]
error is most often caused by an issue during the encryption process indicating we could not decrypt the payload.
Common causes include:
- Using an incorrect encryption algorithm.
- Using the wrong keys for the environment (e.g., production vs. sandbox).
- Flipping keys by mistake, such as:
- Using the IV key in place of the consumer secret.
- Using the consumer key where the secret is expected.
✅ How to fix it:
- Double-check that you’re using the correct key for each parameter.
- Ensure the keys match the current environment you're targeting.
3. Why do I get the error: Client not allowed to consume service, country, currency combination
?
This error means the combination of service code, country code, and currency code in your request is not authorized or valid for your client configuration.
The most common causes are:
- Using the wrong service code.
- The service has been disabled for your client account.
- Providing an incorrect or unsupported country code for the service.
- Providing an incorrect or unsupported currency code for the service.
✅ How to fix it:
- Log in to your Dashboard and go to the My Settings section to confirm your assigned:
- Client Code
- Service Code(s)
- Country and Currency Codes
- Make sure the values in your request match exactly what is configured for your account.
- Confirm that the service you're trying to access is active.
4. Why did the due date default to 30 minutes?
If the due date defaults to 30 minutes from the current time, it's usually because the date value was either:
- Not provided, or
- Incorrectly formatted in the request.
✅ Expected format:
We accept due dates in ISO 8601 format — this is the default datetime format in most programming languages and libraries.
Example:
"due_date": "2025-05-21T15:30:00Z"