How to integrate Mpesa Daraja API with Node.js

In this article, we will look into integrating Mpesa STK push API with Node and Typescript.

April 29, 20246 min read

Overview

In today's digital landscape, most businesses have embraced mobile money for digital transactions. Most customers also find paying for goods and services convenient using mobile money. It is crucial for companies that engage in monetary transactions to integrate seamless payment features to enhance the overall user experience. Safaricom a leading telecom company has exposed a range of MPESA APIs, such as B2B, C2B, and Mpesa Express, enabling access to MPESA services.

In this guide, we will explore the process of integrating the MPesa Express API into a Node.js and Express application.

Prerequisites

Before proceeding with the integration process, ensure you have the following:

  1. Nodejs installed
  2. Postman to test the APIs
  3. Daraja API credentials. You can obtain the credentials from the Safaricom developer portal.

Create your Nodejs and Typescript project

In this guide, we will create a Nodejs project with Typescript. Create a new directory for your project and initialize it with a package manager of your preference. We will use npm.

mkdir my-project
cd my-project
npm init -y

Install Typescript

Install Typescript as a development dependency

npm install --save-dev typescript ts-node @types/node

Initialize TypeScript

Create a tsconfig.json file to configure TypeScript options:

npx tsc --int

Ensure the following configurations are set for the tsconfig.json file

{
  "compilerOptions": {
    "target": "es6",         
    "module": "commonjs", 
    "moduleResolution": "node", 
     "allowJs": true, 
    "outDir": "./dist",            
    "strict": true,           
    "esModuleInterop": true,  
    "skipLibCheck": true,       
  },
  
}

Install Required packages

  • express: A minimalist web framework for Node.js
  • cors: Allows you to enable CORS in your Node.js app
  • dotenv: Manages environment variables configuration
  • axios: Used for making HTTP requests
  • nodemon: Restarts your Nodejs application whenever it detects changes.

To compile your Typescript file to JavaScript. Run the following command.

npx tsc

Create an src directory and add the index.ts file, middleware, controller, and routes.

Image

Automate the process of starting the server

Optionally, you can add scripts to your package.json file to make running and building the server easier.

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "npx tsc",
    "start": "node dist/index.js",
    "dev": "npx nodemon src/index.ts"
  },

To start the project run: npm run dev

Try Kodaschool for free

Click below to sign up and get access to free web, android and iOs challenges.

Sign Up

Safaricom developer portal

Sign up on the developer portal and create an app under my apps tab.

Image

Once the application is created, the consumer secret and consumer key are generated. Copy the credentials and store them in the .env file.

Image

Navigate to the APIs tab that has various endpoints exposed. Click the Mpesa Express simulate button.

On the Mpesa Express simulator select the app that you have created. The business short code(Party B) and the passkey will be accessed here.

Image

Save all the credentials in a .env file for security concerns.

//.env

MPESA_CONSUMER_SECRET=PASTE_CONSUMER_SECRET
MPESA_CONSUMER_KEY=PASTE_CONSUMER_KEY
MPESA_PASS_KEY=PASTE_PASS_KEY
MPESA_BUSINESS_SHORT_CODE=PASTE_BUSINESS_SHORT_CODE

PORT=YOUR_SERVER_PORT //The server port

Let's delve into the code. Show me the code!! Whoop!! Whoop!!

The index.ts is our entry point for the application. We will set up an Express server with middleware for JSON parsing and CORS. Define route handlers for the "/lipa" endpoint, and start the server listening on the specified port.

The route handler logic will be explained as we proceed.

//index.ts

import express, { Request, Response } from "express";
import cors from "cors";
import dotenv from "dotenv";
import router from "./routes/lipaRoute";

dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;

//middlewares
app.use(express.json());
app.use(cors());

app.get("/", (req: Request, res: Response) => {
  res.send("Daraja API payment gateway");
});
app.use("/lipa", router);

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Generating the access token

The Authorization API generates tokens for authenticating your api calls.

By extending the request type, the token is added as an additional property, to the request object in a type-safety manner. The property will later be used to store the access token.

export type RequestExtended = Request & { token?: string };
  • Once the API request is successful, the response will contain an access token in response.data.access_token.
  • req.token stores the access token generated in the req(request) object to be used later in the request lifecycle.
  • The next() function is used to pass control to the next middleware function in the stack.
  • generateToken calls next(), allowing the request to proceed to the next function, in our case it is the handleStkPush function.
//generateToken.ts

import axios from "axios";
import { NextFunction, Request, Response } from "express";

export type RequestExtended = Request & { token?: string };

export const generateToken = async (
  req: RequestExtended,
  _res: Response,
  next: NextFunction
) => {
  const CONSUMER_KEY = process.env.MPESA_CONSUMER_KEY as string;
  const CONSUMER_SECRET = process.env.MPESA_CONSUMER_SECRET as string;
  const URL =
    "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials";

  const auth = Buffer.from(`${CONSUMER_KEY}:${CONSUMER_SECRET}`).toString(
    "base64"
  );

  try {
    const response = await axios(URL, {
      headers: {
        Authorization: `Basic ${auth}`,
      },
    });
    req.token = response.data.access_token;
    next();
  } catch (error: any) {
    throw new Error(`Failed to generate access token: ${error.message}`);
  }
};

TimeStamp format

TimeStamp ensures that each transaction request is time-specific and unique. This prevents resending a valid request to perform unauthorized transactions.

We will need a utility function that converts Date to a string formatted as, YYYYMMDDHHMMSS.

The timestamp is included in the payload when initiating the stk push and represents the exact time when the request is generated.

//timeStamp.ts

const date = new Date();
export const timestamp =
  date.getFullYear() +
  ("0" + (date.getMonth() + 1)).slice(-2) +
  ("0" + date.getDate()).slice(-2) +
  ("0" + date.getHours()).slice(-2) +
  ("0" + date.getMinutes()).slice(-2) +
  ("0" + date.getSeconds()).slice(-2);

Controllers

This function initiates the business logic to handle the Mpesa STK push (Sim Toolkit Push) request.

The payload construction includes all the required data for the STK push request. The password is generated by concatenating the M-Pesa pass key business short code, and the timestamp, then encoding the result in Base64.

The payload definition

  • BusinessShortCode: The business shortcode.
  • Password: The generated password.
  • Timestamp: The current timestamp.
  • TransactionType: The type of transaction (CustomerPayBillOnline).
  • Amount: The amount to be charged.
  • PartyA: The customer's phone number.
  • PartyB: The business shortcode.
  • PhoneNumber: The customer's phone number again.
  • CallBackURL: A URL where M-Pesa will send transaction status updates.
  • AccountReference: A reference for the account.
  • TransactionDesc: A description of the transaction.

The API call must be authenticated by including an authorization header in the request, which contains the token generated earlier by the generateToken function.

If the request is successful, the user receives an STK push on their phone. This user is prompted to enter their PIN to authorize the transaction amount and the amount charged.

import axios from "axios";
import { Response } from "express";
import { timestamp } from "../../utils/timeStamp";
import { RequestExtended } from "../middlewares/generateToken";

const handleStkPush = async (req: RequestExtended, res: Response) => {
  const { phone, amount } = req.body;

  const BUSINESS_SHORT_CODE = process.env.MPESA_BUSINESS_SHORT_CODE as string;

  const password = Buffer.from(
    BUSINESS_SHORT_CODE + process.env.MPESA_PASS_KEY + timestamp
  ).toString("base64");

  const payload = {
    BusinessShortCode: BUSINESS_SHORT_CODE,
    Password: password,
    Timestamp: timestamp,
    TransactionType: "CustomerPayBillOnline",
    Amount: amount,
    PartyA: phone,
    PartyB: process.env.MPESA_BUSINESS_SHORT_CODE,
    PhoneNumber: phone,
    CallBackURL: "https://buysasaOnline.com/",
    AccountReference: "BuySasa online shop",
    TransactionDesc: "Payment",
  };

  try {
    const response = await axios.post(
      "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest",
      payload,
      {
        headers: {
          Authorization: `Bearer ${req.token}`,
        },
      }
    );
    res.status(201).json({
      message: true,
      data: response.data,
    });
  } catch (error: any) {
    console.log(error);
    res.status(500).json({
      message: "Failed",
      error: error.message,
    });
  }
};
export { handleStkPush };

Routes

When a POST request to the 'stkpush/' endpoint is received the request first goes through the generateToken middleware, which handles the generation of an authentication token.

After the token has been verified the request is passed to the handleStkPush request handler function which requests the M-Pesa API.

//lipaRoute.ts
import express from "express";
import { handleStkPush } from "../controllers/stkController";
import { generateToken } from "../middlewares/generateToken";
const router = express.Router();
router.post("/stkpush", generateToken, handleStkPush);
export default router;

Test on Postman

Navigate to Postman and pass the amount and phone as part of the payload.

We will make an api request using the endpoint below.

Image

The phone number used will receive an STK push.

Image

Github Repo: https://github.com/kodaschool/nodejs-mpesa-integration

Mary Maina

About Mary Maina

I am a frontend devloper