PHPackages                             botnetdobbs/laravel-mpesa-sdk - 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. [API Development](/categories/api)
4. /
5. botnetdobbs/laravel-mpesa-sdk

ActiveLibrary[API Development](/categories/api)

botnetdobbs/laravel-mpesa-sdk
=============================

Laravel M-Pesa Integration Package

2.0.0(1mo ago)64832MITPHPPHP ^8.2|^8.3|^8.4CI passing

Since Dec 7Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/botnet-dobbs/laravel-mpesa-sdk)[ Packagist](https://packagist.org/packages/botnetdobbs/laravel-mpesa-sdk)[ RSS](/packages/botnetdobbs-laravel-mpesa-sdk/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (5)Dependencies (29)Versions (9)Used By (0)

Laravel M-Pesa Integration Package
==================================

[](#laravel-m-pesa-integration-package)

[![build](https://github.com/botnet-dobbs/laravel-mpesa-sdk/actions/workflows/main.yml/badge.svg)](https://github.com/botnet-dobbs/laravel-mpesa-sdk/actions/workflows/main.yml) [![Packagist Downloads](https://camo.githubusercontent.com/e338e0634c72734def6d4b7e4634169a7cc43bd1255f391d23401c603f276258/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626f746e6574646f6262732f6c61726176656c2d6d706573612d73646b)](https://camo.githubusercontent.com/e338e0634c72734def6d4b7e4634169a7cc43bd1255f391d23401c603f276258/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626f746e6574646f6262732f6c61726176656c2d6d706573612d73646b)

A thin, dependency-injection friendly SDK for Safaricom's M-Pesa Daraja API. STK Push, B2C, B2B Express Checkout, C2B, account balance, transaction status, and reversals.

No models, migrations, or routes are forced on you. The package handles the integration layer: OAuth tokens and caching, the STK password and timestamp, initiator credential encryption, request validation, and callback parsing. Your app owns the payment records, queues, and business logic.

For upstream API changes, always refer to the [Safaricom Developer Portal](https://developer.safaricom.co.ke/APIs).

Requirements
------------

[](#requirements)

- PHP 8.2+

Laravel VersionLaravel 11.xLaravel 12.xLaravel 13.x---

Installation
------------

[](#installation)

```
composer require botnetdobbs/laravel-mpesa-sdk
```

Publish the config file:

```
php artisan vendor:publish --tag=mpesa-config
```

Set your credentials in `.env`. Get them from the My Apps page on the Daraja portal:

```
MPESA_ENV=sandbox
MPESA_CONSUMER_KEY=your_consumer_key
MPESA_CONSUMER_SECRET=your_consumer_secret
```

That is enough for STK Push in sandbox. B2C, balance, status, and reversal also need initiator credentials and a certificate. Details:

- [Getting Started](docs/getting-started.md): first call, how authentication works
- [Configuration](docs/configuration.md): every option and the go-live checklist

---

Quick Look
----------

[](#quick-look)

### Initiate a payment

[](#initiate-a-payment)

Inject the `Client` contract. There is no facade.

```
use Botnetdobbs\Mpesa\Contracts\Client;
use Botnetdobbs\Mpesa\Exceptions\MpesaException;

class PaymentController extends Controller
{
    public function __construct(private Client $mpesa)
    {
    }

    public function checkout(Order $order)
    {
        try {
            $response = $this->mpesa->stkPush([
                'BusinessShortCode' => config('mpesa.business.short_codes.paybill'),
                'Amount' => $order->total,
                'PhoneNumber' => $order->customer_phone,    // 2547XXXXXXXX
                'CallBackURL' => route('mpesa.stk.callback'),
                'AccountReference' => $order->reference,    // shown on the customer's prompt
            ]);
        } catch (MpesaException $e) {
            logger()->error('mpesa.stk_push.error', ['error' => $e->getMessage(), 'status' => $e->status]);

            return response()->json(['message' => 'Payment could not be started. Please try again.'], 502);
        }

        if (! $response->isSuccessful()) {
            // Daraja rejected the request. Log it, return a safe message.
            logger()->warning('mpesa.stk_push.rejected', ['body' => (array) $response->getData()]);

            return response()->json(['message' => 'Payment could not be started. Please try again.'], 422);
        }

        // Save this ID. It links the callback back to the order.
        $order->update([
            'checkout_request_id' => $response->getData()->CheckoutRequestID,
            'status' => 'awaiting_payment',
        ]);

        return response()->json([
            'message' => 'Check your phone and enter your M-PESA PIN.',
            'checkout_request_id' => $order->checkout_request_id,
        ], 202);
    }
}
```

The package generates the Daraja `Password` and `Timestamp` fields for you and refreshes OAuth tokens behind the scenes.

### Receive the result

[](#receive-the-result)

M-Pesa posts the payment outcome to your callback URL. Save first, respond fast, process in a job:

```
use Botnetdobbs\Mpesa\Contracts\CallbackProcessor;
use Botnetdobbs\Mpesa\Contracts\CallbackResponder;

class MpesaCallbackController extends Controller
{
    public function __construct(
        private CallbackProcessor $processor,
        private CallbackResponder $responder,
    ) {
    }

    public function stk(Request $request)
    {
        MpesaRawCallback::create(['payload' => $request->getContent()]); // save first

        ProcessStkCallback::dispatch($request->all());                   // work in a job

        return $this->responder->success();                              // release Safaricom
    }
}
```

That is the whole loop. The guides below cover every field, payload, and production rule.

---

Documentation
-------------

[](#documentation)

- [Getting Started](docs/getting-started.md)
- [Configuration](docs/configuration.md)
- [STK Push (M-Pesa Express)](docs/stk-push.md)
- [B2C Payments](docs/b2c.md)
- [B2B Express Checkout](docs/b2b-express-checkout.md)
- [C2B (Customer to Business)](docs/c2b.md)
- [Account Balance](docs/account-balance.md)
- [Transaction Status](docs/transaction-status.md)
- [Transaction Reversal](docs/reversal.md)
- [Callbacks](docs/callbacks.md)
- [Responses and Errors](docs/responses-and-errors.md)

---

Client Method Reference
-----------------------

[](#client-method-reference)

All methods live on `Botnetdobbs\Mpesa\Contracts\Client` and return a response object. See [Responses and Errors](docs/responses-and-errors.md) for the response helpers.

MethodDaraja APIWhat it does`stkPush($data)`M-Pesa ExpressSends a payment prompt to a customer's phone ([guide](docs/stk-push.md))`stkQuery($data)`M-Pesa Express QueryChecks the status of a prompt ([guide](docs/stk-push.md#query-a-payment-status))`b2c($data)`B2CPays a customer from your business account ([guide](docs/b2c.md))`b2b($data)`B2B Express CheckoutPrompts another merchant to pay you from their till ([guide](docs/b2b-express-checkout.md))`c2bRegister($data)`C2BRegisters your confirmation and validation URLs ([guide](docs/c2b.md))`c2bSimulate($data)`C2BSimulates a customer payment, sandbox only ([guide](docs/c2b.md#simulate-a-payment-sandbox-only))`accountBalance($data)`Account BalanceQueries your shortcode account balances ([guide](docs/account-balance.md))`transactionStatus($data)`Transaction StatusLooks up the state of any transaction ([guide](docs/transaction-status.md))`reversal($data)`ReversalReverses a completed transaction ([guide](docs/reversal.md))For inbound callbacks, use `CallbackProcessor` and `CallbackResponder`. See [Callbacks](docs/callbacks.md).

---

Common Questions
----------------

[](#common-questions)

**Is there a facade?**

No. Inject `Botnetdobbs\Mpesa\Contracts\Client` through the container. This keeps your code testable and swappable.

**My callback never arrived. Now what?**

M-Pesa does not retry failed callbacks. Recover with `stkQuery()` for STK payments or `transactionStatus()` for everything else. See [Callbacks](docs/callbacks.md) for the full recovery pattern.

**Why does `isSuccessful()` return false for a B2B request that worked?**

The B2B Express Checkout acknowledgement has a different shape (`code` instead of `ResponseCode`). Read it with `getData()`. See [B2B Express Checkout](docs/b2b-express-checkout.md).

**Do I need to protect my callback URLs?**

Yes. They are public endpoints. Restrict them to Safaricom's published IPs with middleware and match every callback against a transaction you actually initiated. See [Callbacks](docs/callbacks.md#verify-the-callback-source-ip-whitelisting).

**How do I test without touching real money?**

Use `MPESA_ENV=sandbox` with the test credentials from the Daraja portal. In your own test suite, fake the HTTP layer with Laravel's `Http::fake()`.

**Can one app use multiple shortcodes or both environments at once?**

Not out of the box. The client reads one set of credentials from config. If you need more, rebind the `Client` contract with different config per tenant or context.

---

For Contributors
----------------

[](#for-contributors)

Run the tests:

```
composer test
```

Generate an HTML coverage report, then open `coverage/index.html`:

```
composer test:coverage
```

Code quality:

```
composer check-style   # check code style
composer fix-style     # fix code style issues
composer analyse       # static analysis
```

---

Credits
-------

[](#credits)

- [Lazarus Odhiambo](https://github.com/botnetdobbs)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance92

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity62

Established project with proven stability

 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 ~82 days

Recently: every ~73 days

Total

8

Last Release

41d ago

Major Versions

1.1.1 → 2.0.02026-07-11

PHP version history (2 changes)1.0.0PHP ^8.2

1.0.2PHP ^8.2|^8.3|^8.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/35170812?v=4)[Lazarus Odhiambo](/maintainers/botnetdobbs)[@botnetdobbs](https://github.com/botnetdobbs)

---

Top Contributors

[![botnetdobbs](https://avatars.githubusercontent.com/u/35170812?v=4)](https://github.com/botnetdobbs "botnetdobbs (32 commits)")

---

Tags

daraja-apilaravelmpesapayment-gatewaypayment-integrationphp8stkpush

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/botnetdobbs-laravel-mpesa-sdk/health.svg)

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

###  Alternatives

[mike-bronner/laravel-model-caching

Automatic caching for Eloquent models.

2.4k161.4k2](/packages/mike-bronner-laravel-model-caching)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

818355.4k3](/packages/defstudio-telegraph)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

265.2k](/packages/aedart-athenaeum)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5226.7k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

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