akika/laravel-mpesa-multivendor

A multivendor Laravel package for integrating Safaricom M-Pesa Daraja APIs. It allows seamless M-Pesa transactions for multiple vendors.

Maintainers

Package info

github.com/akikadigital/laravel-mpesa-multivendor

pkg:composer/akika/laravel-mpesa-multivendor

Statistics

Installs: 1 356

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.4.4 2026-06-10 10:57 UTC

README

Tests Coverage Laravel License

Laravel Mpesa Multivendor Package by Akika Digital

Laravel M-Pesa Multivendor is a fully tested Laravel package for integrating Mpesa Daraja API's. It supports multiple shortcodes within the same application and provides a clean service-based interface for STK Push, C2B, B2C, B2B, Account Balance, Reversals, Dynamic QR, Bill Manager, Tax Remittance, Ratiba Standing Orders, Transaction History, IMSI Lookup, Organization Verification, and more.

Installation

You can install the package via composer:

composer require akika/laravel-mpesa-multivendor

After installing the package, enable the package using the following command:

php artisan mpesa-multivendor:install

The above command will not only enable the package, but also publish the config file. You can use the below command to republish the config file.

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

This will generate a mpesa.php file in your config directory where you can set your Mpesa credentials and other configuration options.

Architecture

The package uses a service-based architecture.

Available services:

Mpesa::using($credentials)->stk();
Mpesa::using($credentials)->c2b();
Mpesa::using($credentials)->b2c();
Mpesa::using($credentials)->b2b();
Mpesa::using($credentials)->accountBalance();
Mpesa::using($credentials)->dynamicQr();
Mpesa::using($credentials)->billManager();
Mpesa::using($credentials)->taxRemittance();
Mpesa::using($credentials)->ratiba();
Mpesa::using($credentials)->transactionHistory();
Mpesa::using($credentials)->transactionStatus();
Mpesa::using($credentials)->pochi();
Mpesa::using($credentials)->org();
Mpesa::using($credentials)->imsi();
Mpesa::using($credentials)->reversal();

Credentials consists of the following array. The details are shortcode based to support multivendor.

$credentials = [
    'shortcode' => '600000',
    'consumer_key' => 'xxx',
    'consumer_secret' => 'xxx',
    'api_username' => 'testapi',
    'api_password' => 'xxx',
    'passkey' => 'xxx',
];

Details of the config file

return [
    // Define mpesa environment
    'env' => env('MPESA_ENV', 'sandbox'),
    'debug' => env('MPESA_DEBUG_MODE', true),
    'sandbox' => [
        'url' => 'https://sandbox.safaricom.co.ke',
    ],
    'production' => [
        'url' => 'https://api.safaricom.co.ke',
    ],
];

.env file Setup

Add the following configurations into the .env file

MPESA_ENV=
MPESA_DEBUG_MODE=

The value is either production or sandbox

NOTE: The mpesa.php config file sets the default MPESA_ENV value to sandbox. This will always load sandbox urls.

Function Responses

All responses, except the token generation response, conform to the responses documented on the daraja portal.

Usage

Initializing Mpesa

Mpesa can be initialized in two ways:

Using Default Configuration

use Akika\LaravelMpesaMultivendor\Mpesa;

$mpesa = Mpesa::default();

Using Vendor Credentials

use Akika\Mpesa\Facades\Mpesa;

$mpesa = Mpesa::using($credentials);

Important Urls

Daraja utilizes the two main urls for callbacks. Timeout Url and Result Url. The two urls will also be used in this package as follows:

  • $resultUrl or $callbackUrl : Endpoint to send the results in case of success
  • $timeoutUrl : Endpoint to send the results in case of operations timeout

Access Token Management

Access tokens are automatically generated and cached by the package.

No manual token management is required.

Getting Account Balance

You can fetch mpesa account balance as follows:

$response = Mpesa::using($credentials)
    ->accountBalance()
    ->query(
        resultUrl: $resultUrl,
        queueTimeoutUrl: $timeoutUrl
    );

C2B Transactions

Registering URLs for C2B Transactions

You can register validation and confirmation URLs for C2B transactions:

$response = Mpesa::using($credentials)
    ->c2b()
    ->registerUrls(
        confirmationUrl: $confirmationUrl, // The URL to receive payment confirmation notifications.
        validationUrl: $validationUrl, // The URL to receive payment validation requests.
        ResponseType: $ResponseType, // nullable | Either Cancelled or Completed, default is Completed
    );

Simulating C2B Transactions

You can simulate payment requests from clients:

$response = Mpesa::using($credentials)
    ->c2b()
    ->simulate(
        phoneNumber: $phoneNumber,
        amount: $amount,
        billRefNumber: $billRefNumber // Account reference for Customer paybills and null for customer buy goods
    );

Initiating STK Push

You can initiate online payment on behalf of a customer:

$response = Mpesa::using($credentials)
    ->stk()
    ->push(
        phoneNumber: $phoneNumber,
        amount: $amount,
        callbackUrl: $callbackUrl,
        accountReference: $accountReference
        receivingShortCode: $receivingShortCode // shortcode to receive funds, if null, the shortcode in credentials is used
    );

Querying STK Push Status

You can query the result of a STK Push transaction:

$response = Mpesa::using($credentials)
    ->stk()
    ->query($checkoutRequestId);

Reversing Transactions

You can reverse a C2B M-Pesa transaction:

$response = Mpesa::using($credentials)
    ->reversal()
    ->reverse(
        transactionId: $transactionId,
        amount: $amount,
        resultUrl: $resultUrl,
        queueTimeoutUrl: $timeoutUrl
    );

Business to Customer (B2C) Transactions

You can perform Business to Customer transactions:

Send to Customer

$response = Mpesa::using($credentials)
    ->b2c()
    ->send(
        phoneNumber: $phoneNumber,
        amount: $amount,
        resultUrl: $resultUrl,
        queueTimeoutUrl: $timeoutUrl,
        remarks: $remarks, // Comments that are sent along with the transaction.
        occasion: $occasion, // A reference for the transaction, such as an invoice number or account number. (Max 100 characters)
        commandId: $commandId, // The command ID for the transaction (default: 'BusinessPayment')
    );

B2C Topup

This API enables you to load funds to a B2C shortcode directly for disbursement. The transaction moves money from your MMF/Working account to the recipient’s utility account.

$response = Mpesa::using($credentials)
    ->b2c()
    ->topUp(
        receiverShortCode: $receiverShortCode,
        amount: $amount,
        resultUrl: $resultUrl,
        timeoutUrl: $timeoutUrl,
        remarks: $remarks,
        accountReference: $accountReference,
        requester: $requester
    );

Business to Business (B2B) Transactions

B2B Buy Goods

You can perform Business to Business transactions:

$response = Mpesa::using($credentials)
    ->b2b()
    ->buyGoods(
        receiverShortCode: $shortcode,
        amount: $amount,
        resultUrl: $resultUrl,
        queueTimeoutUrl: $timeoutUrl,
        remarks: $remarks,
        requester: $requester
    );

B2B Paybill

You can perform Business to Business transactions:

$response = Mpesa::using($credentials)
    ->b2b()
    ->paybill(
        receiverShortCode: $shortcode,
        amount: $amount,
        accountReference: $accountReference
        resultUrl: $resultUrl,
        queueTimeoutUrl: $timeoutUrl,
        remarks: $remarks,
        requester: $requester
    );

B2B Express Checkout

$response = Mpesa::using($credentials)
    ->b2b()
    ->expressCheckout(
        partnerName: $partnerName, // The name of the organization that receives the transaction
        destShortcode: $destShortcode,
        amount: $amount,
        paymentReference: $paymentReference, // The reference for the payment
        callbackUrl: $callbackUrl
        requestRefID: $requestRefID
    );

QR Code Generation

You can generate QR codes for making payments:

$response = Mpesa::using($credentials)
    ->dynamicQr()
    ->generate(
        merchantName: 'Akika Digital',
        reference: 'INV001',
        amount: 100
    );
  • $amount is an optional field

Bill Manager

You can optin to the bill manager service and send invoices:

Opt in

$response = Mpesa::using($credentials)
    ->billManager()
    ->optin(
        email: $email,
        officialContact: $officialContact,
        sendReminders: $sendReminders, // 1 or 0
        logo: $logo, // Optional logo URL for the bill manager account.
        callbackUrl: $callbackUrl
    );

Send Single Invoice

$response = Mpesa::using($credentials)
    ->billManager()
    ->singleInvoice(
        externalReference: $externalReference, // A unique reference for the invoice.
        billedFullName: $billedFullName, // The full name of the billed customer.
        billedPhoneNumber: $billedPhoneNumber, // The phone number of the billed customer.
        invoiceName: $invoiceName, // A descriptive name for the invoice (e.g., "Water Bill").
        amount: $amount,
        dueDate: $dueDate, // The due date for the invoice (in 'Y-m-d' format).
        accountReference: $accountReference, // A reference for the account being billed.
        billingPeriod: $billingPeriod, // The billing period (e.g., "June 2026").
        items: $items, // Optional additional billable items to include in the invoice.
    );

Send Bulk Invoices

$response = Mpesa::using($credentials)
    ->billManager()
    ->bulkInvoice(
        invoices: $invoices
    );

Below is an array showing sample invoices:

$invoices = [
    [
        "externalReference" => "#9932340",
        "billedFullName" => "John Doe",
        "billedPhoneNumber" => "0712345678",
        "billedPeriod" => "August 2021",
        "invoiceName" => "Jentrys",
        "dueDate" => "2021-10-12",
        "accountReference" => "1ASD678H",
        "amount" => "800",
        "invoiceItems" => [
            [
                "itemName" => "Food",
                "amount" => "700",
            ],
            [
                "itemName" => "Water",
                "amount" => "100",
            ],
        ],
    ],

    [
        "externalReference" => "#9932341",
        "billedFullName" => "Jane Smith",
        "billedPhoneNumber" => "0723456789",
        "billedPeriod" => "September 2021",
        "invoiceName" => "BlueWave Supplies",
        "dueDate" => "2021-11-15",
        "accountReference" => "2BSD789J",
        "amount" => "1500",
        "invoiceItems" => [
            [
                "itemName" => "Office Supplies",
                "amount" => "1200",
            ],
            [
                "itemName" => "Delivery Fee",
                "amount" => "300",
            ],
        ],
    ],

    [
        "externalReference" => "#9932342",
        "billedFullName" => "Peter Mwangi",
        "billedPhoneNumber" => "0734567890",
        "billedPeriod" => "October 2021",
        "invoiceName" => "Tech Solutions",
        "dueDate" => "2021-12-01",
        "accountReference" => "3CSD890K",
        "amount" => "2500",
        "invoiceItems" => [
            [
                "itemName" => "Laptop Repair",
                "amount" => "2000",
            ],
            [
                "itemName" => "Spare Parts",
                "amount" => "500",
            ],
        ],
    ],
];

Cancel Single Invoice

$response = Mpesa::using($credentials)
    ->billManager()
    ->cancelSingleInvoice(
        externalReference: $externalReference // The unique reference of the invoice to be cancelled.
    );

Cancel Bulk Invoices

$response = Mpesa::using($credentials)
    ->billManager()
    ->cancelBulkInvoices(
        externalReferences: $externalReferences // An array of unique references for the invoices to be cancelled.
    );

Tax Remittance

You can remit tax to the government using this package. To use this API, prior integration is required with KRA for tax declaration, payment registration number (PRN) generation, and exchange of other tax-related information.:

$response = Mpesa::using($credentials)
    ->taxRemmitance()
    ->remit(
        amount: $amount,
        accountReference: $accountReference, // The payment registration number (PRN) issued by KRA.
        resultUrl: $resultUrl,
        queueTimeoutUrl: $queueTimeoutUrl, // Optional remarks for the transaction (default: 'Tax remittance').
        remarks: $remarks,
        commandId: $commandId, // The due date for the invoice (in 'Y-m-d' format).
        partyA: $partyA,
    );

Mpesa Ratiba

The Standing Order APIs enable teams to integrate with the standing order solution by initiating a request to create a standing order on the customer profile.

Create Standing Order

$response = Mpesa::using($credentials)
    ->ratiba()
    ->createStandingOrder(
        name: $name,
        startDate: $startDate,
        endDate: $endDate,
        transactionType: $transactionType,
        amount: $amount,
        phoneNumber: $phoneNumber,
        callbackUrl: $callbackUrl,
        accountReference: $accountReference,
        frequency: $frequency,
        transactionDesc: $transactionDesc
    );

Query Standing Order

$response = Mpesa::using($credentials)
    ->ratiba()
    ->query(
        standingOrderId: $standingOrderId
    );

Cancel Standing Order

$response = Mpesa::using($credentials)
    ->ratiba()
    ->cancel(
        standingOrderId: $standingOrderId, // Standing order to cancel
        callbackUrl: $callbackUrl,
    );

Mpesa Transaction History

Register URL

The URL you register will be used to receive transaction hostories

$response = Mpesa::using($credentials)
    ->transactionHistory()
    ->registerCallbackUrl(
        nominatedNumber: $nominatedNumber, // The phone number to receive the transaction history callbacks (in international format, e.g., 2547XXXXXXXX).
        callbackUrl: $callbackUrl,
    );

Query History

The following API takes in the start and and end dates and returns the transactions between that period.

$response = Mpesa::using($credentials)
    ->transactionHistory()
    ->query(
        startDate: $startDate,
        endDate: $endDate,
        offset: $offset,
    );

IMSI

Get the IMSI number for a given phone number.

NOTE: This API is charged per request, so use it only when necessary (e.g., for fraud prevention or network analysis).

$response = Mpesa::using($credentials)
    ->imsi()
    ->query(
        phoneNumber: $phoneNumber
    );

Successful Response

{
    "ResponseRefID": "8bab-42cc-bb85-5056a6c01e6915928124",
    "ResponseCode": "1000",
    "ResponseMessage": "Success",
    "Response": [
        [
            {
                "transactionId": "XXXXXXXXX",
                "trxDate": "2025-01-24T10:56:11+03:00",
                "msisdn": 2547XXXXXXXX,
                "sender": "MPESA",
                "transactiontype": "c2b-buy-goods-debit",
                "billreference": "",
                "amount": "2697.0",
                "organizationname": "VENDOR"
            }
        ]
    ]
}

API Response Body

$response has the following as a json object

{
    "OriginatorConversationID": "5118-111210482-1",
    "ConversationID": "AG_20230420_2010759fd5662ef6d054",
    "ResponseCode": "0",
    "ResponseDescription": "Accept the service request successfully."
}

Succssful result body

A successful result body has the following structure

{
 "Result":
 {
   "ResultType": "0",
   "ResultCode":"0",
   "ResultDesc": "The service request is processed successfully",
   "OriginatorConversationID":"626f6ddf-ab37-4650-b882-b1de92ec9aa4",
   "ConversationID":"12345677dfdf89099B3",
   "TransactionID":"QKA81LK5CY",
   "ResultParameters":
     {
       "ResultParameter":
          [{
           "Key":"DebitAccountBalance",
           "Value":"{Amount={CurrencyCode=KES, MinimumAmount=618683, BasicAmount=6186.83}}"
          },
          {
          "Key":"Amount",
           "Value":"190.00"
          },
           {
          "Key":"DebitPartyAffectedAccountBalance",
           "Value":"Working Account|KES|346568.83|6186.83|340382.00|0.00"
          },
           {
          "Key":"TransCompletedTime",
           "Value":"20221110110717"
          },
           {
          "Key":"DebitPartyCharges",
           "Value":""
          },
           {
          "Key":"ReceiverPartyPublicName",
           "Value":000000– Biller Companty
          },
          {
          "Key":"Currency",
           "Value":"KES"
          },
          {
           "Key":"InitiatorAccountCurrentBalance",
           "Value":"{Amount={CurrencyCode=KES, MinimumAmount=618683, BasicAmount=6186.83}}"
          }]
       },
     "ReferenceData":
       {
        "ReferenceItem":[
           {"Key":"BillReferenceNumber", "Value":"19008"},
           {"Key":"QueueTimeoutURL", "Value":"https://mydomain.com/b2b/businessbuygoods/queue/"}
         ]
      }
 }
}

Unsuccessful reusult body

An unsuccessful result body has the following structure

{
 "Result":
 {
   "ResultType":0,
   "ResultCode":2001,
   "ResultDesc":"The initiator information is invalid.",
   "OriginatorConversationID":"12337-23509183-5",
   "ConversationID":"AG_20200120_0000657265d5fa9ae5c0",
   "TransactionID":"OAK0000000",
   "ResultParameters":{
     "ResultParameter":{
        "Key":"BillReferenceNumber",
        "Value":12323333
      }
   },
   "ReferenceData":{
     "ReferenceItem":[
      {
        "Key":"BillReferenceNumber",
        "Value":12323333
      },
      {
        "Key":"QueueTimeoutURL",
        "Value":"https://internalapi.safaricom.co.ke/mpesa/abresults/v1/submit"
      }
      {
        "Key":"Occassion"
      }
     ]
    }
 }
}

Quality Assurance

The package is fully tested using Pest PHP and PHPUnit.

Test Coverage

Current test coverage:

  • ✅ 100% Line Coverage
  • ✅ 100% Method Coverage
  • ✅ 100% Class Coverage

Main coverage report:

Coverage Report

Services coverage report:

Services Coverage Report

The test suite covers:

  • Authentication & Access Token Management
  • STK Push
  • C2B Transactions
  • B2C Transactions
  • B2B Transactions
  • Account Balance
  • Dynamic QR
  • Bill Manager
  • Tax Remittance
  • Ratiba (Standing Orders)
  • Transaction History
  • Transaction Status
  • IMSI Lookup
  • Organization Verification
  • Reversals
  • Pochi Payments
  • Security Credential Generation
  • Service Provider Registration
  • Console Commands
  • Facades
  • Exception Handling
  • URL Validation

Run the test suite:

composer test

Generate a coverage report:

XDEBUG_MODE=coverage ./vendor/bin/pest --coverage

License

The Laravel Mpesa package is open-sourced software licensed under the MIT license. See the LICENSE file for details.