PHPackages                             levizwannah/mpesa-sdk-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. levizwannah/mpesa-sdk-php

ActiveLibrary[Payment Processing](/categories/payments)

levizwannah/mpesa-sdk-php
=========================

An independent Mpesa SDK for PHP. This package provides a clean implementation of the Safaricom Mpesa API for PHP applications.

v1.2.1.4(1y ago)196295MITPHP

Since Aug 18Pushed 1y ago2 watchersCompare

[ Source](https://github.com/levizwannah/mpesa-sdk-php)[ Packagist](https://packagist.org/packages/levizwannah/mpesa-sdk-php)[ RSS](/packages/levizwannah-mpesa-sdk-php/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)DependenciesVersions (25)Used By (0)

[![Installs](https://camo.githubusercontent.com/4b00331d62ded21dc5847313123c1b876e4c8517ab6fed7e69f82cbd6de09259/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6c6576697a77616e6e61682f6d706573612d73646b2d706870)](https://camo.githubusercontent.com/4b00331d62ded21dc5847313123c1b876e4c8517ab6fed7e69f82cbd6de09259/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6c6576697a77616e6e61682f6d706573612d73646b2d706870)

Mpesa SDK for PHP
=================

[](#mpesa-sdk-for-php)

A **Clean, Elegant, and Independent** PHP SDK for integrating M-Pesa services with your application. The SDK is purely object-oriented and easy to understand and use. It has no dependency and hence can be used in any PHP project: plain or framework-based.

Requirement
===========

[](#requirement)

- PHP version &gt;= 7.4
- You have read the documentation of the Daraja API: This SDK mainly abstracts everything for you and makes it easy to integrate M-Pesa. So, ensure you have looked at the Daraja documentation. Not to understand it, but just know what data are being sent and received. That makes it easy to understand this SDK.

Get it with Composer
--------------------

[](#get-it-with-composer)

`composer require levizwannah/mpesa-sdk-php`

And then start using it.

not using composer
------------------

[](#not-using-composer)

Download the zip version of the code. Include the `self-autoload.php` file located in the downloaded `src` folder.

Documentation
=============

[](#documentation)

Setting Up
----------

[](#setting-up)

Firstly create an Mpesa object with the necessary configurations.

**The consumer key, consumer secret, and business short code are always required.**

*If you are using a till number, then the `till` key is required, otherwise only the business short code is required.*

> Note: *The business shortcode is the same as the Paybill number. For till numbers, it is different.*

```
require('path/to/vendor/autoload.php');

use LeviZwannah\MpesaSdk\Mpesa;

$config = [
  "key" => "consumer-key", // consumer key
  "secret" => "consumer-secret", // consumer secret
  "code" => "12345", // business short code

  "till" => "67891", // optional till number
  "passkey" => "xxxx", // optional passkey
  "initiator" => "levizwannah", // optional initiator name
  "credential" => "levi-cred++=="  // optional security credential
]

$mpesa = Mpesa::new()->configure($config);
```

### Method chaining vs configure($configure)

[](#method-chaining-vs-configureconfigure)

Every key in the `$config` array is a setter method for the object. For example, the below code is equivalent to the one above.

```
require('path/to/vendor/autoload.php');

use LeviZwannah\MpesaSdk\Mpesa;

$mpesa = Mpesa::new();

$mpesa->key("consumer-key")
      ->secret("consumer-secret")
      ->code(12345)
      ->till(67891) // optional
      ->passkey('stk-passkey') // optional
      ->initiator("levizwannah") // optional
      ->credential("levi-cred++=="); // optional
```

Every object for interacting with a specific API extends the parent Mpesa object. Therefore, the same rule applies to them. You can use a config array and the configure method or just use the individual setter methods.

> **Warning**: Do not directly create an object from a child class, always use the mpesa object to get the child object as you will see later in the doc.

### Environment

[](#environment)

You can use the `'env'` key in the `$config` array or the `env("env")` method to set the environment. The environment value can be `live` or `sandbox`. By default, the environment is `"live"`. There is a `Constant` class to save you from writing the literal strings.

```
//...other code
use LeviZwannah\MpesaSdk\Helpers\Constant;

$mpesa->configure([
    'env' => Constant::SANDBOX,
    // or
    'env' => Constant::LIVE, // default value
]);

// or
$mpesa->env(Constant::SANDBOX);
$mpesa->env(Constant::LIVE); // default value
```

> ***Remember***: Every setter method used below can be used as a key in the `$config` array and set using the `configure($config)` method.

### Phone Numbers

[](#phone-numbers)

The SDK adds 254 to the phone number. The below formats are supported

```
0746987654
+254746987654
+2540746987654
254746987654
746987654

```

### Exceptions

[](#exceptions)

The SDK throws an exception if a required data is missing during requests. For example, the initiator name and security credentials are required before making Reversal requests. If they are not found, an exception is thrown with a semantic message telling you what the problem is.

### Handling Responses

[](#handling-responses)

The SDK provides a uniform way to check the immediate Mpesa responses. Use the below snippet when interacting with any API using the SDK.

```
//... request made

if($client->accepted()) {
    // ... mpesa accepted the request for processing
    // This means the ResponseCode == 0
    $response = $client->response();
}
else {
    // The response code is not == 0 or there is an error.
    $error = $client->error(); // get an error object
    echo "error: $error->code, $error->message";
}

// you can always get the latest response using
$response = $client->response(); // returns an object

$conversationId = $response->ConversationID;
$responseCode = $response->ResponseCode;
// ...
```

> **Note:** The data in the response object is the same as it is in the Daraja Documentation. There is no additional formatting done.

Security in Handling Callbacks
------------------------------

[](#security-in-handling-callbacks)

The SDK has two static helper methods to help you when handling mpesa responses in your callbacks.

1. `verifyOrigin(): bool`: returns a `true` if the callback response is from Mpesa and `false` otherwise.
2. `data($asArray = false): object|array`: gets the callback data from mpesa and returns it as an object or array based on its argument.

```
require('path/to/vendor/autoload.php');

use LeviZwannah\MpesaSdk\Mpesa;

$isFromMpesa = Mpesa::verifyOrigin();
$result = Mpesa::data();

//...
```

Handling Callback Payloads
--------------------------

[](#handling-callback-payloads)

> Note: `Response` represents what you get from Mpesa when you make a request. `Result` is the payload sent to your callback URLs.

### Payload format

[](#payload-format)

Please refer to the Daraja documentation at  to see the expected payload. For forward compatibility, the SDK doesn't alter the responses or payload from Mpesa.

### The confusing part

[](#the-confusing-part)

In every `Response`, there will be unique keys. For example, the `MerchantRequestID` and `CheckoutRequestID` in the STK push response, and the `OriginatorConversationID` and `ConversationID` in the other APIs responses. These keys identify the transaction on Mpesa. Save these keys in your database or some storage alongside the pending transaction.

In the `Result` payload that will be sent to your callbacks, these keys will be present. Therefore, you can use them to update the corresponding transactions in your storage or database.

Mpesa Express (STK Push) API
----------------------------

[](#mpesa-express-stk-push-api)

Used for initiating STK Push requests.

### Requirements

[](#requirements)

Ensure these values were set as shown in the setup section:

- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);
- Till Number (`till`) *For till numbers*
- Passkey (`passkey`)

### Usage

[](#usage)

```
//..setup...

$stk = $mpesa->stk();

// for till numbers
// ensure that the till number is set during setup
// or set it up using $stk->till(123455)

$stk->phone('0724786543')
    ->amount(1)
    ->buygoods() // for till numbers
    ->paybill() // for paybill numbers
    ->callback('https://my-domain.com/path/to/callback')
    ->description('optional description') // optional
    ->push();

// check if the request was accepted by mpesa.
if(!$stk->accepted()) {
    $error = $stk->error();
    echo "error: $error->code, $error->message";
    // exit...
}

// accepted for processing
$response = $stk->response(); // returns an object

$merchantRequestId = $response->MerchantRequestID;
$checkoutRequestId = $response->checkoutRequestID;

//...
```

Notice that the difference between using Till number and paybill number is the use of `paybill()` and `buygoods()` methods before calling the `push()` method. Also, ensure that `till` value is set when using Till numbers.

Mpesa Express STK Query
-----------------------

[](#mpesa-express-stk-query)

Use for querying the status of STK push requests. This is different from normal transaction status queries.

### Requirements

[](#requirements-1)

Ensure these values were set as shown in the setup section:

- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);
- Passkey (`passkey`)
- Till (`till`) for till accounts

### Usage

[](#usage-1)

See the code snippet below

```
//...setup...

$query = $mpesa->stk()->query();

$query->checkoutId('checkout-request-id')
      ->paybill() // for paybill number
      ->buygoods() // for till numbers
      ->make();

// check if it's not accepted
if(!$query->accepted()){
  $error = $query->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $query->response();
$resultCode = $response->ResultCode;
$merchantId = $response->MerchantRequestID;
// ...
```

> **Note**: Use `paybill()` if you are querying an STK request made for a paybill number, otherwise use `buygoods()`. By default, it queries for STK requests made for paybill numbers.

C2B URLs Registration API
-------------------------

[](#c2b-urls-registration-api)

Enables you to register your C2B URLs. The SDK also provides an easy response method for your confirmation and validation scripts.

> The `validation url` is not required unless you explicitly ask the Mpesa team to enable it for you.

### Requirements

[](#requirements-2)

Ensure these values were set as shown in the setup section:

- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Registration

[](#registration)

Look at the code snippet below.

```
// ...setup...
// c2b url registration
$urls = $mpesa->urls();

$urls->confirmation('https://my.url/path/to/confirmation')
     ->validation('https://my.url/path/to/validation') // optional
     ->register();

if(!$urls->accepted()) {
  $error = $urls->error();
  echo "Error: $error->code - $error->message";
  // exit;
}
```

### Callback helpers

[](#callback-helpers)

When Mpesa sends a payload to your confirmation or validation URLs, you need to send a formatted confirmation or denial payload. You can use the SDKs static methods for that. See below.

```
require('path/to/vendor/autoload.php');

use LeviZwannah\MpesaSdk\Mpesa;

# confirmation.url

// your code ...
Mpesa::confirm(); // echos the formatted response
// your code...
// send SMS ... etc

#================#

# validation.url

// your code ...
if(!true) {
    Mpesa::deny();
}
else {
    Mpesa::confirm();
}

// your code ...
```

> Note: You can only use `Mpesa::deny()` in the validation handler.

Reversal API
------------

[](#reversal-api)

Enables you to make Reversals of Mpesa Transactions.

### Requirements

[](#requirements-3)

Ensure these values were set as shown in the setup section:

- Initiator name (`initiator`)
- Security credential (`credential`)
- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

See the code snippet on how to use this SDK.

```
// ...setup...

$reversal = $mpesa->reversal();

$reversal->timeoutUrl('https://my.url/path/to/reversal/timeout')
        ->resultUrl('https://my.url/path/to/reversal/result')
        ->transId('1X1Y1ZNME') // transaction ID to reverse
        ->amount(100) // amount paid
        ->remarks('optional remarks') // optional
        ->occasion('optional occasion') // optional
        ->make();

if(!$reversal->accepted()) {
  $error = $reversal->error();
  echo "$error->code - $error->message";
  // exit;
}

// reversal initiated
$response = $reversal->response();
$originatorId = $response->OriginatorConversationID;
$conversationId = $response->ConversationID;
//...
```

> In your callback scripts, please ensure to follow the recommendation in the **security section** of this doc.

Transaction Query API
---------------------

[](#transaction-query-api)

The Transaction query API enables you to check the statuses of transactions made to or by your business shortcode.

### Requirements

[](#requirements-4)

Ensure these values were set as shown in the setup section:

- Initiator name (`initiator`)
- Security credential (`credential`)
- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Usage

[](#usage-2)

See the code snippet below on how to use this SDK.

```
// Transaction query
$query = $mpesa->query();

$query->transId('1X1Y1ZNME')
      ->resultUrl('https://my.url/path/to/timeout')
      ->timeoutUrl('https://my.url/path/to/result')
      ->remarks('optional remarks') // optional
      ->occasion('optional occasion') // optional
      ->make();

if(!$query->accepted()) {
  $error = $query->error();
  echo "$error->code - $error->message";
  // exit;
}

$response = $query->response();
$originatorId = $response->OriginatorConversationID;
$conversationId = $response->ConversationID;
//...
```

Balance Query API
-----------------

[](#balance-query-api)

The balance Query API allows you to query the balance in a business account.

### Requirements

[](#requirements-5)

Ensure these values were set as shown in the setup section:

- Initiator name (`initiator`)
- Security credential (`credential`)
- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Usage

[](#usage-3)

See the code snippet

```
//...setup...

$balance = $mpesa->balance();

$balance->timeoutUrl('https://my.url/path/to/timeout')
        ->resultUrl('https://my.url/path/to/result')
        ->remarks('optional remarks') // optional
        ->check();

if(!$balance->accepted()){
  $error = $balance->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $balance->response();
$originatorId = $response->OriginatorConversationID;
$conversationId = $response->ConversationID;
//...
```

B2B API
-------

[](#b2b-api)

The B2B API allows you to make payments to paybill or till numbers from your business short code.

### Requirements

[](#requirements-6)

Ensure these values were set as shown in the setup section:

- Initiator name (`initiator`)
- Security credential (`credential`)
- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Usage

[](#usage-4)

Look at the code snippet below

```
// ...setup...
$b2b = $mpesa->b2b();
$b2b->amount(100)
    ->receiver('123456') // business you are paying to
    ->resultUrl('https://my.url/path/to/result')
    ->timeoutUrl('https://my.url/path/to/timeout');

# if receiver is a paybill number
$b2b->paybill()
    ->account('account-number');

# if receiver is a till number
$b2b->buygoods();

# optional
$b2b->remarks('optional remarks')
    ->occasion('optional occasion')
    ->requester('0712345678'); // the customer on
                               // whose behalf the money is
                               // being paid.

# make payment
$b2b->pay();

if(!$b2b->accepted()) {
  $error = $b2b->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $b2b->response();
$originatorId = $response->OriginatorConversationID;
$conversationId = $response->ConversationID;
//...

//... save to db, etc
```

B2C API
-------

[](#b2c-api)

The B2C API allows you to make payments to mobile numbers from your business short code.

### Requirements

[](#requirements-7)

Ensure these values were set as shown in the setup section:

- Initiator name (`initiator`)
- Security credential (`credential`)
- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Usage

[](#usage-5)

See the code snippet below:

```
$b2c = $mpesa->b2c();

$b2c->amount(100)
    ->phone('0712345678')
    ->resultUrl('https://my.url/path/to/result')
    ->timeoutUrl('https://my.url/path/to/timeout');

# set payment purpose
$b2c->salary() // for salary payment
    ->promotion() // for promotion payment
    ->payment(); // for business payment

# optional
$b2c->remarks('optional-remarks')
    ->occasion('optional-occasion');

# pay
$b2c->pay();

if(!$b2c->accepted()) {
  $error = $b2c->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $b2c->response();
$originatorId = $response->OriginatorConversationID;
$conversationId = $response->ConversationID;
//...

//... save to db, etc
```

Tax Remittance API
------------------

[](#tax-remittance-api)

The Tax Remittance API allows you to make Tax payment to KRA

### Requirements

[](#requirements-8)

Ensure these values were set as shown in the setup section:

- Initiator name (`initiator`)
- Security credential (`credential`)
- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Usage

[](#usage-6)

See the code snippet below

```
// ...setup...

$remit = $mpesa->remitTax();

$remit->amount(1000)
      ->resultUrl('https://my.url/path/to/result')
      ->timeoutUrl('https://my.url/path/to/timeout')
      ->remarks('optional-remarks') // optional
      ->pay();

if(!$remit->accepted()) {
  $error = $remit->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $remit->response();
$originatorId = $response->OriginatorConversationID;
$conversationId = $response->ConversationID;
//...

//... save to db, etc
```

Dynamic QR Code API
-------------------

[](#dynamic-qr-code-api)

Enables you to generate QR codes for different transactions. Please see the Daraja documentation

` Use this API to generate a Dynamic QR which enables Safaricom M-PESA customers who have My Safaricom App or M-PESA app, to scan and capture till number and amount and then authorize to pay for goods and services at select LIPA NA M-PESA (LNM) merchant outlets.` -- Daraja

### Requirements

[](#requirements-9)

Ensure these values were set as shown in the setup section:

- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`)

### Usage

[](#usage-7)

See the snippet below

```
//...setup...
$qr = $mpesa->qr();

$qr->size(300) // QR Code size (300x300)
   ->merchantName('Business Name')
   ->reference('account-number') // transaction reference
   ->amount(100);

# the receiver of the payment
# can be Till Number, Agent number, phone number
# paybill number, or business number
# Correspond to CPI in the Daraja doc
$qr->receiver('123457');

# sets the receiver type
# corresponds to TrxCode in the Daraja Doc
$qr->buygoods(); // receiver is a till number
$qr->paybill(); // receiver is a paybill number
$qr->sendMoney(); // receiver is a phone number
$qr->withdraw(); // receiver is an agent number
$qr->sendToBusiness(); // receiver is a business number

# generate code
$qr->generate();

if(!$qr->accepted()) {
  $error = $qr->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $qr->response();
$qrCode = $response->QRCode;
$requestId = $response->RequestID;
//...
```

Business To Bulk API
--------------------

[](#business-to-bulk-api)

This API enables you to load funds from a working account directly to a utility account for B2C payments.

### Requirements

[](#requirements-10)

Ensure these values were set as shown in the setup section:

- Initiator name (`initiator`)
- Security credential (`credential`)
- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Usage

[](#usage-8)

```
// ...setup...
$btb = $mpesa->btb();
$btb->amount(100)
    ->receiver('123456') // short code of the receiver
    ->resultUrl('https://my.url/path/to/result')
    ->timeoutUrl('https://my.url/path/to/timeout');

# if in the same organization use
$btb->amount(100)
    ->toSelf() // accepts an optional short code param for sub-organizations
    ->resultUrl('https://my.url/path/to/result')
    ->timeoutUrl('https://my.url/path/to/timeout');

# optional
$btb->remarks('optional remarks') // optional
    ->requester('0712345678'); // optional - the customer on
                               // whose behalf the transfer is
                               // being made.

# make the transfer
$btb->transfer();

if(!$btb->accepted()) {
  $error = $btb->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $btb->response();
$originatorId = $response->OriginatorConversationID;
$conversationId = $response->ConversationID;
//...

//... save to db, etc
```

Mpesa Ratiba (Subscription)
---------------------------

[](#mpesa-ratiba-subscription)

This API enables you to create Mpesa Standing Orders. Take it as a subscription API for Mpesa

### Requirements

[](#requirements-11)

Ensure these values were set as shown in the setup section:

- Consumer Key (`key`)
- Consumer Secret(`secret`)
- Business Short Code (`code`);

### Usage

[](#usage-9)

```
// ...setup...
$subscription = $mpesa->subscription(); // same as $mpesa->ratiba();

$subscription->amount(100)
    ->plan("Gold Plan") // Standing order Name (same as $sub->name('Gold Plan'))
    ->phone('0740958756')
    ->startDate(10, 10, 2024) // month, day, year
    ->endDate(10, 11, 2024) // month, day, year
    ->frequency(Constant::FREQ_DAILY)
    ->callback('https://my.url/path/to/result');

# if your code is paybill number
$subscription->paybill() // if paybill number
    ->account('account-number');

# if using a till till number
$subscription->buygoods()
    ->till(1234567); // if till number

# optional
$subscription->description('optional description');

# create subscription
$subscription->create();

if(!$subscription->accepted()) {
  $error = $subscription->error();
  echo "$error->code $error->message";
  // exit;
}

$response = $subscription->response();
$refId = $response->responseRefID;
//...

//... save to db, etc
```

Quick Note
==========

[](#quick-note)

If you are confused on how to handle the results in the callback, please read the earlier sections of this README file.

Reporting Errors
================

[](#reporting-errors)

Please open an issue in case there is a bug found.

Show Love
=========

[](#show-love)

- Star this repository
- if you love it, buy me coffee.

###  Health Score

38

—

LowBetter than 84% of packages

Maintenance45

Moderate activity, may be stable

Popularity27

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~37 days

Recently: every ~21 days

Total

23

Last Release

533d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/792dbf4e6a5f6dac197f6567f0300fb08b82c7fbc21675f580a50741296f782c?d=identicon)[levizwannah](/maintainers/levizwannah)

---

Top Contributors

[![levizwannah](https://avatars.githubusercontent.com/u/56189552?v=4)](https://github.com/levizwannah "levizwannah (83 commits)")

---

Tags

daraja-phpmpesampesa-phpmpesa-php-sdkmpesa-sdksafaricom-mpesa-apissafaricom-mpesa-phpSafaricom M-PesaMpesa PHPIndependent Mpesa PackageMpesa Package

### Embed Badge

![Health badge](/badges/levizwannah-mpesa-sdk-php/health.svg)

```
[![Health](https://phpackages.com/badges/levizwannah-mpesa-sdk-php/health.svg)](https://phpackages.com/packages/levizwannah-mpesa-sdk-php)
```

###  Alternatives

[omnipay/paypal

PayPal gateway for Omnipay payment processing library

3156.8M53](/packages/omnipay-paypal)[eduardokum/laravel-boleto

Biblioteca com boletos para o laravel

626351.9k2](/packages/eduardokum-laravel-boleto)[tbbc/money-bundle

This is a Symfony bundle that integrates moneyphp/money library (Fowler pattern): https://github.com/moneyphp/money.

1961.9M](/packages/tbbc-money-bundle)[2checkout/2checkout-php

2Checkout PHP Library

83740.3k2](/packages/2checkout-2checkout-php)[smhg/sepa-qr-data

Generate QR code data for SEPA payments

61717.2k5](/packages/smhg-sepa-qr-data)[omnipay/dummy

Dummy driver for the Omnipay payment processing library

271.2M33](/packages/omnipay-dummy)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
