DEV Community

Sospeter Mong'are
Sospeter Mong'are

Posted on

How to Integrate KCB M-PESA STK Push API

KCB BUNI M-PESA STK Push allows you to initiate payment requests directly to a customer’s phone. KCB provides a secure API that enables businesses and developers to seamlessly collect payments from their customers. This guide will walk you through the process of authenticating, sending STK push requests, handling callbacks, and interpreting possible responses.


1. Token Generation / Authentication

Before making any API request, you need to authenticate using your Consumer Key and Consumer Secret provided during onboarding.

Endpoint:

POST https://uat.buni.kcbgroup.com/token?grant_type=client_credentials
Enter fullscreen mode Exit fullscreen mode

Headers:

Authorization: Basic {Base64(consumerKey:consumerSecret)}
Enter fullscreen mode Exit fullscreen mode

Sample Request:

curl --location --request POST 'https://uat.buni.kcbgroup.com/token?grant_type=client_credentials' \
--header 'Authorization: Basic XENo...ZTdh' \
--data ''
Enter fullscreen mode Exit fullscreen mode

Success Response:

{
  "access_token": "eyJ4NyRqV5xTbuwcwdi7...",
  "token_type": "Bearer",
  "expires_in": 3599
}
Enter fullscreen mode Exit fullscreen mode

Token Parameters

Name Type Required Description
access_token String Yes The token you will use in all subsequent API requests as a Bearer token.
token_type String Yes Always "Bearer".
expires_in Number Yes Time in seconds before the token expires (typically 3600 seconds = 1 hour).

2. Initiating an STK Push Request

Once you have the token, you can initiate an STK push request.

Endpoint:

POST https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush
Enter fullscreen mode Exit fullscreen mode

Headers:

Authorization: Bearer {access_token}
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode

Sample Request:

{
  "phoneNumber": "254708920430",
  "amount": "1",
  "invoiceNumber": "1123412831049",
  "sharedShortCode": true,
  "orgShortCode": "522522",
  "orgPassKey": "",
  "callbackUrl": "https://example.com/callback",
  "transactionDescription": "school fee payment"
}
Enter fullscreen mode Exit fullscreen mode

Request Parameters

Name Type Required Description
phoneNumber String Yes Customer’s phone number in 2547XXXXXXXX format to which STK Push will be sent.
amount String Yes Transaction amount in Kenyan Shillings (KES) to be transferred.
invoiceNumber String Optional Unique invoice reference for your system. For the case of KCB Till, Unique code assigned to each invoice. Format:KCBTILLNO-YOURACCREF
sharedShortCode Boolean Yes Indicates whether a shared short code is being used. This is KCB’S short-code (Paybill or Till) used to receive the transaction. If provided value should be set to true.
orgShortCode String Optional Your assigned Paybill or Till number used to identify an organisation and receive the transactions.
orgPassKey String Optional Unique pass key for the organization assigned by KCB. Usually leave blank
callbackUrl String Yes Your server endpoint where payment results will be sent (must be HTTPS).
transactionDescription String Optional Short description of the transaction.

3. Success Response

If the request is valid, you will receive the following response:

{
  "response": {
    "MerchantRequestID": "0c43-471f-b0cb-f80bcaee5e9e242149",
    "ResponseCode": "0",
    "CustomerMessage": "Success. Request accepted for processing",
    "CheckoutRequestID": "ws_CO_17092025150413517708920430",
    "ResponseDescription": "Success. Request accepted for processing"
  },
  "header": {
    "statusDescription": "Success. Request accepted for processing",
    "statusCode": "0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Response Parameters

Name Type Description
MerchantRequestID String Unique ID for the request generated by the API.
CheckoutRequestID String Unique transaction ID for tracking the payment.
ResponseCode String 0 means request accepted; non-zero means an error.
CustomerMessage String Message displayed to customer confirming request acceptance.
ResponseDescription String Additional details on response status.
statusCode String API status code (0 = success).
statusDescription String Human-readable status description.

💻 Step 5: Real-World Code Examples

Below are working code snippets showing how to integrate KCB Buni STK Push in Node.js, PHP, and Python.


✅ Node.js Example

const axios = require('axios');

const consumerKey = "YOUR_CONSUMER_KEY";
const consumerSecret = "YOUR_CONSUMER_SECRET";

// Base64 encode key:secret
const basicAuth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString('base64');

async function getAccessToken() {
  const url = 'https://uat.buni.kcbgroup.com/token?grant_type=client_credentials';
  const resp = await axios.post(url, null, {
    headers: {
      'Authorization': `Basic ${basicAuth}`,
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });
  return resp.data.access_token;
}

async function initiateStkPush() {
  const token = await getAccessToken();
  const stkUrl = 'https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush';

  const payload = {
    phoneNumber: "254708920430",
    amount: "1",
    invoiceNumber: "1312831049",
    sharedShortCode: true,
    orgShortCode: "522522",
    orgPassKey: "YOUR_ORG_PASSKEY",
    callbackUrl: "https://yourdomain.com/webhook",
    transactionDescription: "school fee payment"
  };

  const resp = await axios.post(stkUrl, payload, {
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    }
  });

  console.log("STK Push Response:", resp.data);
}

initiateStkPush().catch(err => {
  console.error("Error:", err.response ? err.response.data : err.message);
});
Enter fullscreen mode Exit fullscreen mode

✅ PHP Example

<?php

$consumerKey = "YOUR_CONSUMER_KEY";
$consumerSecret = "YOUR_CONSUMER_SECRET";
$basicAuth = base64_encode($consumerKey . ":" . $consumerSecret);

function getAccessToken($basicAuth) {
    $tokenUrl = "https://uat.buni.kcbgroup.com/token?grant_type=client_credentials";
    $ch = curl_init($tokenUrl);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Authorization: Basic $basicAuth",
        "Content-Type: application/x-www-form-urlencoded"
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);
    $data = json_decode($response, true);
    return $data['access_token'];
}

function initiateStkPush($token) {
    $stkUrl = "https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush";
    $payload = [
        "phoneNumber" => "254708920430",
        "amount" => "1",
        "invoiceNumber" => "1312831049",
        "sharedShortCode" => true,
        "orgShortCode" => "522522",
        "orgPassKey" => "YOUR_ORG_PASSKEY",
        "callbackUrl" => "https://yourdomain.com/webhook",
        "transactionDescription" => "school fee payment"
    ];

    $ch = curl_init($stkUrl);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Authorization: Bearer $token",
        "Content-Type: application/json"
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true);
}

// Usage
$token = getAccessToken($basicAuth);
$response = initiateStkPush($token);
print_r($response);

?>
Enter fullscreen mode Exit fullscreen mode

✅ Python Example

import requests
import json

consumer_key = "YOUR_CONSUMER_KEY"
consumer_secret = "YOUR_CONSUMER_SECRET"

def get_access_token():
    url = "https://uat.buni.kcbgroup.com/token?grant_type=client_credentials"
    resp = requests.post(url, auth=(consumer_key, consumer_secret))
    resp.raise_for_status()
    return resp.json()["access_token"]

def initiate_stk_push(token):
    stk_url = "https://uat.buni.kcbgroup.com/mm/api/request/1.0.0/stkpush"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    payload = {
        "phoneNumber": "254708920430",
        "amount": "1",
        "invoiceNumber": "1312831049",
        "sharedShortCode": True,
        "orgShortCode": "522522",
        "orgPassKey": "YOUR_ORG_PASSKEY",
        "callbackUrl": "https://yourdomain.com/webhook",
        "transactionDescription": "school fee payment"
    }
    resp = requests.post(stk_url, headers=headers, json=payload)
    resp.raise_for_status()
    return resp.json()

if __name__ == "__main__":
    token = get_access_token()
    response = initiate_stk_push(token)
    print(json.dumps(response, indent=2))
Enter fullscreen mode Exit fullscreen mode

4. Callback Response

Once the customer enters their PIN and completes the transaction, the API will send a callback to your specified callbackUrl.

Sample Callback Payload:

{
  "Body": {
    "stkCallback": {
      "MerchantRequestID": "0c43-471f-b0cb-f80bcaee5e9e242149",
      "CheckoutRequestID": "ws_CO_17092025150413517708920430",
      "ResultCode": 0,
      "ResultDesc": "The service request is processed successfully.",
      "CallbackMetadata": {
        "Item": [
          {"Name": "Amount", "Value": 1},
          {"Name": "MpesaReceiptNumber", "Value": "TIH8BED1K4"},
          {"Name": "Balance"},
          {"Name": "TransactionDate", "Value": 20250917150425},
          {"Name": "PhoneNumber", "Value": 254708920430}
        ]
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Callback Parameters

Name Type Description
ResultCode Number 0 = Success, other codes indicate failure reasons.
ResultDesc String Description of transaction outcome.
Amount Number Amount paid by customer.
MpesaReceiptNumber String M-PESA transaction ID.
TransactionDate Number Date and time of transaction in YYYYMMDDHHMMSS format.
PhoneNumber Number Customer’s phone number that made the payment.

5. Possible Error Responses

Error Example Description
{ "fault": { "code": 900901, "message": "Invalid Credentials", "description": "Invalid token" }} Token provided is invalid.
{ "fault": { "code": 900902, "message": "Missing Credentials", "description": "Provide credentials" }} No authentication credentials provided.
{ "response": {}, "header": { "statusDescription": "Bad Request - Invalid Remarks", "statusCode": "1" }} Some parameters missing or invalid.
Successful: ResponseCode: "0" Request accepted; wait for callback.

6. Possible Callback Scenarios

  • Transaction successfulResultCode: 0
  • User unreachableResultCode: 1037 ("DS timeout user cannot be reached")
  • Invalid PINResultCode: 2001 ("Initiator information invalid")
  • User cancels promptResultCode: 1032 ("Request cancelled by user")

7. HTTP Response Codes

Code Description
200 Request processed successfully
400 Bad request (invalid parameters)
401 Unauthorized (invalid credentials)
403 Forbidden request
404 Resource not found
500 Internal server error
503 Service unavailable

8. Going Live

To go live:


For any assistance with Payment Integrations, Reach out!

Happy Coding!

Top comments (0)