PHPackages                             james-kabz/mpesapkg - 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. james-kabz/mpesapkg

ActiveLibrary

james-kabz/mpesapkg
===================

Laravel package for integrating Safaricom M-Pesa APIs

v0.0.6(2mo ago)020↓100%MITPHPPHP ^8.1CI passing

Since Feb 3Pushed 2mo agoCompare

[ Source](https://github.com/James-Kabz/mpesapkg)[ Packagist](https://packagist.org/packages/james-kabz/mpesapkg)[ RSS](/packages/james-kabz-mpesapkg/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (3)Versions (7)Used By (0)

MpesaPkg (Laravel)
==================

[](#mpesapkg-laravel)

Plug-and-play M-Pesa package with STK, B2C, C2B, and utility APIs. Includes request/response persistence, callback storage, and optional webhook validation.

Quick Start
-----------

[](#quick-start)

1. Publish config (optional):

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

2. Run migrations:

```
php artisan migrate
```

3. Place certificates:

```
storage/app/private/certs/SandboxCertificate.cer
storage/app/private/certs/ProductionCertificate.cer

```

4. Configure env (see template below).

Environment Variables (example)
-------------------------------

[](#environment-variables-example)

```
MPESA_ENV=sandbox
MPESA_BASE_URL=https://sandbox.safaricom.co.ke
MPESA_ROUTE_PREFIX=payments
MPESA_STORE_REQUESTS=true
MPESA_STORE_CALLBACKS=true

MPESA_CONSUMER_KEY=...
MPESA_CONSUMER_SECRET=...

# STK
MPESA_STK_SHORT_CODE=174379
MPESA_STK_PASSKEY=...
MPESA_STK_CALLBACK_URL=https://example.ngrok-free.app/payments/stk/callback

# B2C
MPESA_B2C_INITIATOR=testapi
MPESA_B2C_INITIATOR_PASSWORD=
MPESA_B2C_SECURITY_CREDENTIAL=...
MPESA_B2C_SHORT_CODE=600997
MPESA_B2C_COMMAND_ID=BusinessPayment
MPESA_B2C_RESULT_URL=https://example.ngrok-free.app/payments/b2c/result
MPESA_B2C_TIMEOUT_URL=https://example.ngrok-free.app/payments/b2c/timeout

# C2B
MPESA_C2B_SHORT_CODE=600997
MPESA_C2B_RESPONSE_TYPE=Completed
MPESA_C2B_VALIDATION_URL=https://example.ngrok-free.app/payments/c2b/validation
MPESA_C2B_CONFIRMATION_URL=https://example.ngrok-free.app/payments/c2b/confirmation

# Utility callbacks
MPESA_TRANSACTION_STATUS_RESULT_URL=https://example.ngrok-free.app/payments/transaction/status/result
MPESA_TRANSACTION_STATUS_TIMEOUT_URL=https://example.ngrok-free.app/payments/transaction/status/timeout
MPESA_ACCOUNT_BALANCE_RESULT_URL=https://example.ngrok-free.app/payments/account/balance/result
MPESA_ACCOUNT_BALANCE_TIMEOUT_URL=https://example.ngrok-free.app/payments/account/balance/timeout
MPESA_REVERSAL_RESULT_URL=https://example.ngrok-free.app/payments/reversal/result
MPESA_REVERSAL_TIMEOUT_URL=https://example.ngrok-free.app/payments/reversal/timeout

# Webhook validation (optional)
MPESA_WEBHOOK_VALIDATION=false
MPESA_WEBHOOK_HEADER=X-Mpesa-Token
MPESA_WEBHOOK_TOKEN=
MPESA_WEBHOOK_ALLOWED_IPS=
```

Note: some sandbox environments reject callback URLs containing the word `mpesa` in the path. If you see `Invalid ValidationURL`, use a different prefix such as `payments`.

Routes (under MPESA\_ROUTE\_PREFIX)
-----------------------------------

[](#routes-under-mpesa_route_prefix)

```
POST //stk/push
POST //stk/query
POST //stk/callback

POST //b2c/send
POST //b2c/validated
POST //b2c/result
POST //b2c/timeout

POST //c2b/register
POST //c2b/simulate
POST //c2b/validation
POST //c2b/confirmation

POST //transaction/status
POST //transaction/status/result
POST //transaction/status/timeout
POST //account/balance
POST //account/balance/result
POST //account/balance/timeout
POST //reversal
POST //reversal/result
POST //reversal/timeout

```

Response Format
---------------

[](#response-format)

All API responses return:

```
{
  "ok": true,
  "status": 200,
  "data": {},
  "error": null,
  "body": null
}
```

Generate Security Credential
----------------------------

[](#generate-security-credential)

```
php artisan mpesa:security-credential
```

Using the Config Service
------------------------

[](#using-the-config-service)

The package exposes a typed config service so you don't need to call `config('mpesa...')` repeatedly.

```
use JamesKabz\MpesaPkg\Services\MpesaConfig;

class Example
{
    public function __construct(private MpesaConfig $config) {}

    public function handle(): void
    {
        $prefix = $this->config->routePrefix();
        $stkShortCode = $this->config->stkShortCode();
    }
}
```

`MpesaClient` and `MpesaHelper` are resolved from the container, so you can type-hint them and let Laravel inject:

```
use JamesKabz\MpesaPkg\MpesaClient;

class PaymentsController
{
    public function __construct(private MpesaClient $mpesa) {}
}
```

Test Commands (examples)
------------------------

[](#test-commands-examples)

Set:

```
BASE=https://example.ngrok-free.app
PREFIX=payments

```

### STK Push

[](#stk-push)

```
curl -X POST "$BASE/$PREFIX/stk/push" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "254700000000",
    "amount": 1,
    "account_reference": "TEST-001",
    "transaction_desc": "STK Test",
    "callback_url": "'"$BASE"'/'"$PREFIX"'/stk/callback"
  }'
```

### STK Query

[](#stk-query)

```
curl -X POST "$BASE/$PREFIX/stk/query" \
  -H "Content-Type: application/json" \
  -d '{
    "checkout_request_id": "ws_CO_123456789"
  }'
```

### B2C Send

[](#b2c-send)

```
curl -X POST "$BASE/$PREFIX/b2c/send" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "254700000000",
    "amount": 10,
    "remarks": "B2C Test",
    "occasion": "Test"
  }'
```

### B2C Validated

[](#b2c-validated)

```
curl -X POST "$BASE/$PREFIX/b2c/validated" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "254700000000",
    "amount": 10,
    "remarks": "B2C Validate",
    "id_number": "12345678"
  }'
```

### C2B Register URLs

[](#c2b-register-urls)

```
curl -X POST "$BASE/$PREFIX/c2b/register" \
  -H "Content-Type: application/json" \
  -d '{
    "short_code": "600997",
    "confirmation_url": "'"$BASE"'/'"$PREFIX"'/c2b/confirmation",
    "validation_url": "'"$BASE"'/'"$PREFIX"'/c2b/validation",
    "response_type": "Completed"
  }'
```

### C2B Simulate

[](#c2b-simulate)

```
curl -X POST "$BASE/$PREFIX/c2b/simulate" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "254700000000",
    "amount": 10,
    "short_code": "600997",
    "command_id": "CustomerPayBillOnline",
    "bill_ref_number": "TEST-001"
  }'
```

### C2B Validation (manual test)

[](#c2b-validation-manual-test)

```
curl -X POST "$BASE/$PREFIX/c2b/validation" \
  -H "Content-Type: application/json" \
  -d '{
    "ResultCode": 0,
    "ResultDesc": "Accepted",
    "TransID": "TEST123",
    "TransAmount": "10",
    "MSISDN": "254700000000",
    "BusinessShortCode": "600997",
    "BillRefNumber": "TEST-001"
  }'
```

### C2B Confirmation (manual test)

[](#c2b-confirmation-manual-test)

```
curl -X POST "$BASE/$PREFIX/c2b/confirmation" \
  -H "Content-Type: application/json" \
  -d '{
    "ResultCode": 0,
    "ResultDesc": "Accepted",
    "TransID": "TEST123",
    "TransAmount": "10",
    "MSISDN": "254700000000",
    "BusinessShortCode": "600997",
    "BillRefNumber": "TEST-001"
  }'
```

### Transaction Status

[](#transaction-status)

```
curl -X POST "$BASE/$PREFIX/transaction/status" \
  -H "Content-Type: application/json" \
  -d '{
    "short_code": "600997",
    "transaction_id": "TEST123",
    "identifier_type": "4",
    "remarks": "Status Check"
  }'
```

### Transaction Status Result (callback test)

[](#transaction-status-result-callback-test)

```
curl -X POST "$BASE/$PREFIX/transaction/status/result" \
  -H "Content-Type: application/json" \
  -d '{
    "Result": {
      "ResultCode": 0,
      "ResultDesc": "Accepted",
      "OriginatorConversationID": "TEST123",
      "ConversationID": "TEST456",
      "TransactionID": "TEST789"
    }
  }'
```

### Transaction Status Timeout (callback test)

[](#transaction-status-timeout-callback-test)

```
curl -X POST "$BASE/$PREFIX/transaction/status/timeout" \
  -H "Content-Type: application/json" \
  -d '{
    "Result": {
      "ResultCode": 1,
      "ResultDesc": "Timeout",
      "OriginatorConversationID": "TEST123",
      "ConversationID": "TEST456"
    }
  }'
```

### Account Balance

[](#account-balance)

```
curl -X POST "$BASE/$PREFIX/account/balance" \
  -H "Content-Type: application/json" \
  -d '{
    "short_code": "600997",
    "identifier_type": "4",
    "remarks": "Balance Check"
  }'
```

### Account Balance Result (callback test)

[](#account-balance-result-callback-test)

```
curl -X POST "$BASE/$PREFIX/account/balance/result" \
  -H "Content-Type: application/json" \
  -d '{
    "Result": {
      "ResultCode": 0,
      "ResultDesc": "Accepted",
      "OriginatorConversationID": "TEST123",
      "ConversationID": "TEST456"
    }
  }'
```

### Account Balance Timeout (callback test)

[](#account-balance-timeout-callback-test)

```
curl -X POST "$BASE/$PREFIX/account/balance/timeout" \
  -H "Content-Type: application/json" \
  -d '{
    "Result": {
      "ResultCode": 1,
      "ResultDesc": "Timeout",
      "OriginatorConversationID": "TEST123",
      "ConversationID": "TEST456"
    }
  }'
```

### Reversal

[](#reversal)

```
curl -X POST "$BASE/$PREFIX/reversal" \
  -H "Content-Type: application/json" \
  -d '{
    "short_code": "600997",
    "transaction_id": "TEST123",
    "amount": 10,
    "remarks": "Reversal Test"
  }'
```

### Reversal Result (callback test)

[](#reversal-result-callback-test)

```
curl -X POST "$BASE/$PREFIX/reversal/result" \
  -H "Content-Type: application/json" \
  -d '{
    "Result": {
      "ResultCode": 0,
      "ResultDesc": "Accepted",
      "OriginatorConversationID": "TEST123",
      "ConversationID": "TEST456",
      "TransactionID": "TEST789"
    }
  }'
```

### Reversal Timeout (callback test)

[](#reversal-timeout-callback-test)

```
curl -X POST "$BASE/$PREFIX/reversal/timeout" \
  -H "Content-Type: application/json" \
  -d '{
    "Result": {
      "ResultCode": 1,
      "ResultDesc": "Timeout",
      "OriginatorConversationID": "TEST123",
      "ConversationID": "TEST456"
    }
  }'
```

Notes
-----

[](#notes)

- Requests and callbacks are persisted when `MPESA_STORE_REQUESTS=true` and `MPESA_STORE_CALLBACKS=true`.
- Callbacks can be protected using `MPESA_WEBHOOK_VALIDATION` with token/IP allow-listing.
- Account balance is for the merchant organization’s short code or BuyGoods till, not an end-customer balance check.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance85

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

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

Total

6

Last Release

77d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a23b4d45b221e84b2f9c7c3261d1d8dc7758105d8bd31849c7eb0e1df8a5c008?d=identicon)[james-kabz](/maintainers/james-kabz)

---

Top Contributors

[![James-Kabz](https://avatars.githubusercontent.com/u/146638563?v=4)](https://github.com/James-Kabz "James-Kabz (8 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/james-kabz-mpesapkg/health.svg)

```
[![Health](https://phpackages.com/badges/james-kabz-mpesapkg/health.svg)](https://phpackages.com/packages/james-kabz-mpesapkg)
```

###  Alternatives

[fumeapp/modeltyper

Generate TypeScript interfaces from Laravel Models

196277.9k](/packages/fumeapp-modeltyper)[aedart/athenaeum

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

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

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