WP Manifestindependent plugin directory
manifest / ecommerce / brifnet-mpesa-gateway

BrifNet M-Pesa Gateway

WordPress M-Pesa payment gateway with STK Push, C2B and webhook delivery

by Denis Irungu | CEO BrifNet Technologies LTD · github.com/denno88/brifnet-mpesa-gateway · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/denno88/brifnet-mpesa-gateway/archive/refs/heads/main.zip

Readme

BrifNet M-Pesa Gateway

A WordPress plugin that provides an application-facing API for initiating M-Pesa STK Push payments and receiving asynchronous payment completion notifications through webhooks.

The gateway separates payment initiation from payment confirmation and provides signed, idempotent webhook delivery for client applications.


What It Does

The gateway allows your application to:

  • Initiate an M-Pesa STK Push.
  • Register webhook endpoints for payment events.
  • Activate and deactivate registered webhook endpoints.
  • Receive notifications when payments are completed.
  • Verify webhook authenticity using HMAC-SHA256 signatures.
  • Handle webhook delivery using an at-least-once delivery model.
  • Safely process duplicate webhook deliveries using an event ID.

Requirements

  • WordPress
  • PHP 8.4+
  • Composer
  • M-Pesa/Daraja credentials
  • A publicly accessible HTTPS URL for webhook delivery

The plugin uses Composer dependencies and should be installed in a WordPress environment where PHP and Composer are available during development or deployment.


Installation

1. Clone or Download the Plugin

Place the plugin inside the WordPress plugins directory:

wp-content/plugins/brifnet-mpesa-gateway

For example:

wp-content/
└── plugins/
    └── brifnet-mpesa-gateway/

2. Install PHP Dependencies

From the plugin directory, run:

composer install

This installs the dependencies defined in composer.lock and creates the vendor/ directory.

The vendor/ directory is generated by Composer and should not be committed to the repository.

3. Activate the Plugin

From the WordPress administration dashboard:

Plugins → Installed Plugins → BrifNet M-Pesa Gateway → Activate

The plugin registers its REST API routes when WordPress loads the plugin.


Configuration

The plugin loads M-Pesa credentials from environment variables.

For security, the environment file should be stored outside the WordPress web root.

The current local-development configuration expects the environment file at:

C:/xampp/brifnet-secrets/.env

This location is configured in:

src/WordPress/DarajaConfigLoader.php

The path can be changed to match the deployment environment.

Local Development

Create the secrets directory:

C:\xampp\brifnet-secrets

Then create:

C:\xampp\brifnet-secrets\.env

Add the required configuration:

# M-Pesa / Daraja credentials
DARAJA_CONSUMER_KEY=
DARAJA_CONSUMER_SECRET=
DARAJA_BUSINESS_SHORTCODE=
DARAJA_PASSKEY=

# Daraja API endpoints
DARAJA_AUTH_URL=https://sandbox.safaricom.co.ke/oauth/v1/generate
DARAJA_STK_PUSH_URL=https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest
DARAJA_STK_QUERY_URL=https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query

# Public URL that receives M-Pesa callbacks
DARAJA_CALLBACK_URL=

Replace the empty values with your Daraja credentials.

For production, use the appropriate production Daraja credentials and production API endpoints.

Required Environment Variables

Variable Description
DARAJA_CONSUMER_KEY Daraja consumer key
DARAJA_CONSUMER_SECRET Daraja consumer secret
DARAJA_BUSINESS_SHORTCODE M-Pesa business shortcode
DARAJA_PASSKEY M-Pesa passkey
DARAJA_AUTH_URL Daraja OAuth endpoint
DARAJA_STK_PUSH_URL Daraja STK Push endpoint
DARAJA_STK_QUERY_URL Daraja STK Query endpoint
DARAJA_CALLBACK_URL Public URL used for M-Pesa callbacks

Daraja Callback URLs

The plugin provides the callback endpoints used to receive payment notifications from Safaricom Daraja.

These routes are created automatically by the plugin. Clients do not need to create separate WordPress routes.

For a WordPress site running at:

https://example.com

the callback URLs are:

STK Push Callback

https://example.com/wp-json/brifnet/v1/mpesa/callback

This URL receives the asynchronous result of an STK Push request from Safaricom Daraja.

Set this URL as the callback URL when configuring the STK Push integration.

The URL used by the plugin is also configured through:

DARAJA_CALLBACK_URL=https://example.com/wp-json/brifnet/v1/mpesa/callback

C2B Callback

https://example.com/wp-json/brifnet/v1/mpesa/c2b

This endpoint receives C2B payment notifications from Safaricom Daraja.

Use this URL when registering the C2B confirmation and validation URLs required by the Daraja C2B configuration.

Current Registration Process

At this stage, the plugin does not register the callback URLs with Safaricom automatically.

The URLs are available from the plugin's REST routes and must currently be entered manually when setting up the corresponding Daraja integration.

For example:

STK Callback: https://example.com/wp-json/brifnet/v1/mpesa/callback

C2B: https://example.com/wp-json/brifnet/v1/mpesa/c2b

The planned implementation will provide an API for registering the required URLs with Daraja directly from the plugin.

Until that API is implemented, Daraja URL registration remains a manual setup step.

Callback Requirements

The WordPress site receiving Daraja callbacks must be publicly accessible.

For production use:

  • Use HTTPS.
  • Do not require normal WordPress user authentication on these callback routes.
  • Make sure the URLs are reachable from Safaricom's infrastructure.
  • Do not use localhost for Daraja callbacks.

The plugin will throw a configuration error if a required environment variable is missing or empty.

Webhook Secret

Webhook deliveries are signed using HMAC-SHA256.

The client application and the gateway must therefore share a secret used to generate and verify the webhook signature.

The secret should be stored securely outside the web root and must never be committed to Git.


Security

Never commit real credentials or webhook secrets to Git.

Do not place production credentials in:

  • README.md
  • Source code
  • JavaScript files
  • Public WordPress directories
  • GitHub
  • Screenshots
  • Example configuration files

Use .env.example as a template only. It must contain placeholders and must not contain real credentials.

Production secrets should be supplied through environment-specific configuration or a secure secrets-management mechanism.


API Base URL

The public API is exposed through the WordPress REST API:

https://your-domain.com/wp-json/brifnet/v1

Replace your-domain.com with your WordPress site's domain.


1. Initiate an STK Push

Initiates an M-Pesa STK Push request.

POST /wp-json/brifnet/v1/mpesa/stk
Content-Type: application/json

Request

{
  "reference": "PAY-CB-003",
  "phone": "0729633304",
  "amount": 1
}

Fields

Field Type Description
reference string Unique payment reference supplied by the client application
phone string Customer's Kenyan phone number
amount integer Amount to request from the customer

The phone number must use the Kenyan local format:

07XXXXXXXX

The amount must be a positive integer.

The payment reference represents the business payment identity supplied by the client application.

Successful Response

If the STK Push is accepted for processing:

200 OK
{
  "message": "STK Push initiated.",
  "merchant_request_id": "3b8d-40c5-817d-9beb9d179fd433836",
  "checkout_request_id": "ws_CO_050920260057144729633304"
}

The customer should then receive the M-Pesa payment prompt on their phone.

The checkout_request_id and merchant_request_id identify the STK request.

Important: A successful STK Push response only confirms that the request was accepted for processing. It does not mean that the customer has successfully paid.

Payment completion is communicated asynchronously through the payment.completed webhook.

Possible Errors

Invalid Request

400 Bad Request
{
  "message": "Invalid request."
}

M-Pesa Rejected the Request

502 Bad Gateway
{
  "message": "M-Pesa rejected the STK Push.",
  "error": "..."
}

Internal Error

500 Internal Server Error
{
  "message": "Unable to initiate payment."
}

2. Webhooks

Webhooks allow client applications to receive asynchronous notifications when payment events occur.

A webhook endpoint can subscribe to one or more payment events.

Webhook Events

Currently Supported Events

Event Description
payment.completed Fired when a payment has been successfully completed and recorded by the payment domain.

At present, payment.completed is the only supported webhook event.

Planned Payment Events

The webhook architecture is designed to support additional payment lifecycle events as the payment domain grows.

Potential future events include:

Event Description
payment.failed Payment processing definitively failed.
payment.cancelled Payment was cancelled before completion.
payment.reversed A previously completed payment was reversed.

These events are not currently supported and should not be registered until their corresponding domain events and delivery workflows are implemented.


3. Register a Webhook

Register a webhook endpoint for one or more supported payment events.

POST /wp-json/brifnet/v1/webhooks
Content-Type: application/json

Request

{
  "url": "https://example.com/webhook",
  "events": [
    "payment.completed"
  ]
}

The events field is an array, allowing an endpoint to subscribe to multiple supported events.

Successful Response

200 OK
{
  "url": "https://example.com/webhook",
  "active": true,
  "events": [
    "payment.completed"
  ]
}

Newly registered webhook endpoints are active immediately.


4. Deactivate a Webhook

A webhook endpoint can be temporarily disabled without deleting its registration.

POST /wp-json/brifnet/v1/webhooks/deactivate
Content-Type: application/json

Request

{
  "url": "https://example.com/webhook"
}

Successful Response

{
  "url": "https://example.com/webhook",
  "active": false,
  "events": [
    "payment.completed"
  ]
}

A deactivated endpoint remains registered but will not receive webhook deliveries.


5. Activate a Webhook

A previously deactivated endpoint can be activated again.

POST /wp-json/brifnet/v1/webhooks/activate
Content-Type: application/json

Request

{
  "url": "https://example.com/webhook"
}

Successful Response

{
  "url": "https://example.com/webhook",
  "active": true,
  "events": [
    "payment.completed"
  ]
}

6. Webhook Registration Errors

Invalid JSON

400 Bad Request
{
  "message": "Invalid JSON payload."
}

Events Must Be an Array

{
  "message": "Webhook events must be an array."
}

Event Names Must Be Strings

{
  "message": "Webhook event names must be strings."
}

Invalid Webhook URL

{
  "message": "Webhook URL is invalid."
}

Empty Webhook URL

{
  "message": "Webhook URL cannot be empty."
}

Duplicate Webhook

{
  "message": "Webhook endpoint already exists."
}

7. Payment Completed Webhook

After a payment is successfully completed, BrifNet sends an HTTP POST request to every active registered endpoint subscribed to payment.completed.

Example request:

POST https://example.com/webhook
Content-Type: application/json
X-Webhook-Signature: <signature>

Webhook Payload

{
  "event_id": "evt-123",
  "event": "payment.completed",
  "occurred_at": "2026-09-04T12:30:00+03:00",
  "data": {
    "reference": "PAY-123",
    "phone": "0712345678",
    "amount": 500,
    "channel": "STK"
  }
}

Payload Fields

Field Description
event_id Unique identifier for the payment event
event Event name
occurred_at Time the event occurred
data.reference Payment reference supplied when initiating the payment
data.phone Customer phone number
data.amount Payment amount
data.channel Payment channel

For an STK payment, the channel is:

STK

Read the full README on GitHub →