Mekapesa Merchant API
Integrate Mekapesa payment gateway into your application with our comprehensive RESTful API. Accept payments from wallets, cards, and multiple payment methods with enterprise-grade security.
Fast Integration
Get started in minutes with our simple REST API
Secure Payments
Bank-grade security with HMAC signature verification
Multi-Platform
Works with any programming language or framework
Authentication
Mekapesa API uses API keys to authenticate requests. You can obtain your credentials from your merchant dashboard.
Environment-Aware API Integration
Mekapesa API supports both sandbox (testing) and production environments. Always test in sandbox first before going live.
Environment Configuration
Sandbox Mode
Use for: Development, testing, integration
X-Environment: sandbox
Credentials: Use test_* prefixed API keys
Production Mode
Use for: Live payments, real money
X-Environment: production
Credentials: Use production API keys (no prefix)
Required Credentials
Credential | Header | Description | Location |
---|---|---|---|
Merchant ID |
X-Merchant-Key |
Your unique merchant identifier | Dashboard → Merchant → CONFIG |
API Key |
X-API-Key |
API authentication key | Dashboard → Merchant → CONFIG |
Client Secret |
- | Used for webhook signature verification | Dashboard → Merchant → CONFIG |
Production API Key | X-API-Key |
Production API key (no prefix) | Merchant Dashboard > API Config > Production Mode |
Production Merchant Key | X-Merchant-Key |
Production merchant identifier (no prefix) | Merchant Dashboard > API Config > Production Mode |
Quick Start
Get up and running with Mekapesa API in just a few steps:
Step 1: Get Credentials
- Login to your Mekapesa dashboard
- Navigate to Merchant → CONFIG
- Copy your Merchant ID, API Key, and Client Secret
Step 2: Make Request
- Set required headers with your credentials
- POST to
/api/v1/initiate-payment
- Redirect user to returned payment URL
Try Mekapesa API endpoints directly in your browser
Initiate Payment
Create a new payment request and get a secure checkout URL for your customer. This endpoint works in both sandbox and production environments based on your X-Environment header.
/api/v1/initiate-payment
Request Headers
Header | Value | Required | Description |
---|---|---|---|
Content-Type |
application/json |
✅ | Request content type |
X-Environment |
sandbox | production |
✅ | API environment mode |
X-Merchant-Key |
{merchant_key} |
✅ | Your Merchant ID (sandbox: test_ prefix, production: no prefix) |
X-API-Key |
{api_key} |
✅ | Your API Key (sandbox: test_ prefix, production: no prefix) |
Request Parameters
Parameter | Type | Required | Description |
---|---|---|---|
payment_amount |
number | ✅ | Payment amount (minimum 1.00) |
currency_code |
string | ✅ | 3-letter currency code (USD, EUR, etc.) |
ref_trx |
string | ✅ | Your unique transaction reference |
description |
string | ❌ | Payment description |
success_redirect |
string | ✅ | Success redirect URL |
failure_url |
string | ✅ | Failure redirect URL |
cancel_redirect |
string | ✅ | Cancel redirect URL |
ipn_url |
string | ✅ | Webhook notification URL (same URL for both environments) |
Code Examples
curl -X POST "https://mekapesa.com/api/v1/initiate-payment" \
-H "Content-Type: application/json" \
-H "X-Environment: {environment}" \
-H "X-Merchant-Key: {merchant_key}" \
-H "X-API-Key: {api_key}" \
-d '{"payment_amount": 250.00, "currency_code": "USD", "ref_trx": "ORDER_12345", "description": "Premium Subscription", "success_redirect": "https://yoursite.com/payment/success", "failure_url": "https://yoursite.com/payment/failed", "cancel_redirect": "https://yoursite.com/payment/cancelled", "ipn_url": "https://yoursite.com/api/webhooks/Mekapesa"}'
<?php
use App\Enums\EnvironmentMode;
use Illuminate\Support\Facades\Http;
class MekapesaPaymentInitiator
{
private $environment;
private $merchantKey;
private $apiKey;
private $baseUrl = 'https://mekapesa.com/api/v1';
public function __construct(EnvironmentMode $environment, $merchantKey, $apiKey)
{
$this->environment = $environment;
$this->merchantKey = $merchantKey;
$this->apiKey = $apiKey;
}
// Factory methods for easy configuration
public static function sandbox($testMerchantKey, $testApiKey): self
{
return new self(EnvironmentMode::SANDBOX, $testMerchantKey, $testApiKey);
}
public static function production($merchantKey, $apiKey): self
{
return new self(EnvironmentMode::PRODUCTION, $merchantKey, $apiKey);
}
public function initiatePayment($paymentData)
{
try {
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'X-Environment' => $this->environment->value,
'X-Merchant-Key' => $this->merchantKey,
'X-API-Key' => $this->apiKey,
])->post("{$this->baseUrl}/initiate-payment", $paymentData);
if ($response->successful()) {
$data = $response->json();
if ($data['success']) {
return ['success' => true, 'data' => $data];
}
return ['success' => false, 'status' => $data['status'], 'message' => $data['message'] ?? 'Payment initiation failed'];
}
return ['success' => false, 'error' => 'API request failed'];
} catch (Exception $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
}
// Usage: Choose appropriate factory method based on your environment
$initiator = MekapesaPaymentInitiator::sandbox('test_merchant_key', 'test_api_key'); // For testing
// $initiator = MekapesaPaymentInitiator::production('merchant_key', 'api_key'); // For production
$paymentData = [
'payment_amount' => 250.00,
'currency_code' => 'USD',
'ref_trx' => 'ORDER_12345',
'description' => 'Premium Subscription',
'success_redirect' => 'https://yoursite.com/payment/success',
'failure_url' => 'https://yoursite.com/payment/failed',
'cancel_redirect' => 'https://yoursite.com/payment/cancelled',
'ipn_url' => 'https://yoursite.com/api/webhooks/Mekapesa',
];
$result = $initiator->initiatePayment($paymentData);
const axios = require('axios');
const EnvironmentMode = {
SANDBOX: 'sandbox',
PRODUCTION: 'production'
};
class MekapesaPaymentInitiator {
constructor(environment, merchantKey, apiKey) {
this.environment = environment;
this.merchantKey = merchantKey;
this.apiKey = apiKey;
this.baseUrl = 'https://mekapesa.com/api/v1';
}
// Factory methods
static sandbox(testMerchantKey, testApiKey) {
return new MekapesaPaymentInitiator(EnvironmentMode.SANDBOX, testMerchantKey, testApiKey);
}
static production(merchantKey, apiKey) {
return new MekapesaPaymentInitiator(EnvironmentMode.PRODUCTION, merchantKey, apiKey);
}
async initiatePayment(paymentData) {
try {
const response = await axios.post(`${this.baseUrl}/initiate-payment`, paymentData, {
headers: {
'Content-Type': 'application/json',
'X-Environment': this.environment,
'X-Merchant-Key': this.merchantKey,
'X-API-Key': this.apiKey
}
});
const data = response.data;
if (data.success) {
return { success: true, data };
}
return {
success: false,
status: data.status,
message: data.message || 'Payment initiation failed'
};
} catch (error) {
if (error.response) {
return {
success: false,
error: error.response.data.error || 'API request failed'
};
}
return { success: false, error: error.message };
}
}
}
// Usage: Choose appropriate factory method based on your environment
const initiator = MekapesaPaymentInitiator.sandbox('test_merchant_key', 'test_api_key'); // For testing
// const initiator = MekapesaPaymentInitiator.production('merchant_key', 'api_key'); // For production
const paymentData = {
payment_amount: 250.00,
currency_code: 'USD',
ref_trx: 'ORDER_12345',
description: 'Premium Subscription',
success_redirect: 'https://yoursite.com/payment/success',
failure_url: 'https://yoursite.com/payment/failed',
cancel_redirect: 'https://yoursite.com/payment/cancelled',
ipn_url: 'https://yoursite.com/api/webhooks/Mekapesa',
};
initiator.initiatePayment(paymentData)
.then(result => console.log(result))
.catch(error => console.error(error));
import requests
import logging
from enum import Enum
class EnvironmentMode(Enum):
SANDBOX = 'sandbox'
PRODUCTION = 'production'
class MekapesaPaymentInitiator:
def __init__(self, environment, merchant_key, api_key):
self.environment = environment
self.merchant_key = merchant_key
self.api_key = api_key
self.base_url = 'https://mekapesa.com/api/v1'
@classmethod
def sandbox(cls, test_merchant_key, test_api_key):
return cls(EnvironmentMode.SANDBOX, test_merchant_key, test_api_key)
@classmethod
def production(cls, merchant_key, api_key):
return cls(EnvironmentMode.PRODUCTION, merchant_key, api_key)
def initiate_payment(self, payment_data):
try:
headers = {
'Content-Type': 'application/json',
'X-Environment': self.environment.value,
'X-Merchant-Key': self.merchant_key,
'X-API-Key': self.api_key
}
response = requests.post(
f"{self.base_url}/initiate-payment",
headers=headers,
json=payment_data,
timeout=30
)
if response.status_code == 200:
data = response.json()
if data['success']:
return {'success': True, 'data': data}
return {
'success': False,
'status': data['status'],
'message': data.get('message', 'Payment initiation failed')
}
return {'success': False, 'error': f'HTTP {response.status_code}'}
except requests.RequestException as e:
return {'success': False, 'error': str(e)}
# Usage: Choose appropriate factory method based on your environment
initiator = MekapesaPaymentInitiator.sandbox('test_merchant_key', 'test_api_key') # For testing
# initiator = MekapesaPaymentInitiator.production('merchant_key', 'api_key') # For production
payment_data = {
'payment_amount': 250.00,
'currency_code': 'USD',
'ref_trx': 'ORDER_12345',
'description': 'Premium Subscription',
'success_redirect': 'https://yoursite.com/payment/success',
'failure_url': 'https://yoursite.com/payment/failed',
'cancel_redirect': 'https://yoursite.com/payment/cancelled',
'ipn_url': 'https://yoursite.com/api/webhooks/Mekapesa',
}
result = initiator.initiate_payment(payment_data)
print(result)
Success Response
{
"payment_url": "https://Mekapesa.test/payment/checkout?expires=1753724376&token=AmQvJdGIdGUVJUUMayJZZreBv2UcTyIHclk9Ps1s1pZhLpVlIqIBVPqGTRKQ3NUSehyM3qRUIf69IhLbNfJ1JqiMxlxNrnn22lNz1N01hZQn65r5VZnvhWmQPxQO8UX6rE4yfRUvT6bHdqLj7UDJhRPYRFSgCsG1b86sxSdKTZNOVJdWV5z8L6a5pNMZ2KlpG5e7bYa&signature=e9q7ea91456dcc167e7d498ea486f923570821957be8881566186655950f364",
"info": {
"ref_trx": "TXNT4AQFESTAG4F",
"description": "Order #1234",
"ipn_url": "https://webhook.site/5711b7d5-917a-4d94-bbb3-c28f4a37bea5",
"cancel_redirect": "https://merchant.com/cancel",
"success_redirect": "https://merchant.com/success",
"merchant_id": 1,
"merchant_name": "Mekapesa",
"amount": 200,
"currency_code": "USD",
"environment": "production",
"is_sandbox": false
}
}
Error Response
{
"success": false,
"message": "Validation failed",
"errors": {
"payment_amount": ["The payment amount field is required."],
"currency_code": ["The currency code field is required."]
}
}
Verify Payment
Verify the status of a payment using the Mekapesa transaction ID returned from the payment initiation.
/api/v1/verify-payment/{trxId}
Request Headers
Header | Value | Required | Description |
---|---|---|---|
Accept |
application/json |
✅ | Request content type |
X-Environment |
sandbox | production |
✅ | API environment mode |
X-Merchant-Key |
{merchant_key} |
✅ | Your Merchant ID (sandbox: test_ prefix, production: no prefix) |
X-API-Key |
{api_key} |
✅ | Your API Key (sandbox: test_ prefix, production: no prefix) |
Path Parameters
Parameter | Type | Required | Description |
---|---|---|---|
trxId |
string | ✅ | Mekapesa transaction ID (e.g., TXNQ5V8K2L9N3XM1) |
Code Examples
curl -X GET "https://mekapesa.com/api/v1/verify-payment/TXNQ5V8K2L9N3XM1" \
-H "Accept: application/json" \
-H "X-Environment: {environment}" \
-H "X-Merchant-Key: {merchant_key}" \
-H "X-API-Key: {api_key}"
<?php
use App\Enums\EnvironmentMode;
use Illuminate\Support\Facades\Http;
class MekapesaPaymentVerifier
{
private $environment;
private $merchantKey;
private $apiKey;
private $baseUrl = 'https://mekapesa.com/api/v1';
public function __construct(EnvironmentMode $environment, $merchantKey, $apiKey)
{
$this->environment = $environment;
$this->merchantKey = $merchantKey;
$this->apiKey = $apiKey;
}
// Factory methods for easy configuration
public static function sandbox($testMerchantKey, $testApiKey): self
{
return new self(EnvironmentMode::SANDBOX, $testMerchantKey, $testApiKey);
}
public static function production($merchantKey, $apiKey): self
{
return new self(EnvironmentMode::PRODUCTION, $merchantKey, $apiKey);
}
public function verifyPayment($trxId)
{
try {
$response = Http::withHeaders([
'Accept' => 'application/json',
'X-Environment' => $this->environment->value,
'X-Merchant-Key' => $this->merchantKey,
'X-API-Key' => $this->apiKey,
])->get("{$this->baseUrl}/verify-payment/{$trxId}");
if ($response->successful()) {
$data = $response->json();
if ($data['status'] === 'success') {
// Payment completed successfully
$this->fulfillOrder($data);
return ['success' => true, 'data' => $data];
}
return ['success' => false, 'status' => $data['status'], 'message' => $data['message'] ?? 'Payment not completed'];
}
return ['success' => false, 'error' => 'API request failed'];
} catch (Exception $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
private function fulfillOrder($paymentData)
{
// Your order fulfillment logic here
logger('Payment verified successfully', $paymentData);
}
}
// Usage: Choose appropriate factory method based on your environment
$verifier = MekapesaPaymentVerifier::sandbox('test_merchant_key', 'test_api_key'); // For testing
// $verifier = MekapesaPaymentVerifier::production('merchant_key', 'api_key'); // For production
$result = $verifier->verifyPayment('TXNQ5V8K2L9N3XM1');
const axios = require('axios');
const EnvironmentMode = {
SANDBOX: 'sandbox',
PRODUCTION: 'production'
};
class MekapesaPaymentVerifier {
constructor(environment, merchantKey, apiKey) {
this.environment = environment;
this.merchantKey = merchantKey;
this.apiKey = apiKey;
this.baseUrl = 'https://mekapesa.com/api/v1';
}
// Factory methods
static sandbox(testMerchantKey, testApiKey) {
return new MekapesaPaymentVerifier(EnvironmentMode.SANDBOX, testMerchantKey, testApiKey);
}
static production(merchantKey, apiKey) {
return new MekapesaPaymentVerifier(EnvironmentMode.PRODUCTION, merchantKey, apiKey);
}
async verifyPayment(trxId) {
try {
const response = await axios.get(`${this.baseUrl}/verify-payment/${trxId}`, {
headers: {
'Accept': 'application/json',
'X-Environment': this.environment,
'X-Merchant-Key': this.merchantKey,
'X-API-Key': this.apiKey
}
});
const data = response.data;
if (data.status === 'success') {
// Payment completed successfully
await this.fulfillOrder(data);
return { success: true, data };
}
return {
success: false,
status: data.status,
message: data.message || 'Payment not completed'
};
} catch (error) {
if (error.response) {
return {
success: false,
error: error.response.data.error || 'API request failed'
};
}
return { success: false, error: error.message };
}
}
async fulfillOrder(paymentData) {
// Your order fulfillment logic here
console.log('Payment verified successfully:', paymentData);
}
}
// Usage: Choose appropriate factory method based on your environment
const verifier = MekapesaPaymentVerifier.sandbox('test_merchant_key', 'test_api_key'); // For testing
// const verifier = MekapesaPaymentVerifier.production('merchant_key', 'api_key'); // For production
verifier.verifyPayment('TXNQ5V8K2L9N3XM1')
.then(result => console.log(result))
.catch(error => console.error(error));
import requests
import logging
from enum import Enum
class EnvironmentMode(Enum):
SANDBOX = 'sandbox'
PRODUCTION = 'production'
class MekapesaPaymentVerifier:
def __init__(self, environment, merchant_key, api_key):
self.environment = environment
self.merchant_key = merchant_key
self.api_key = api_key
self.base_url = 'https://mekapesa.com/api/v1'
@classmethod
def sandbox(cls, test_merchant_key, test_api_key):
return cls(EnvironmentMode.SANDBOX, test_merchant_key, test_api_key)
@classmethod
def production(cls, merchant_key, api_key):
return cls(EnvironmentMode.PRODUCTION, merchant_key, api_key)
def verify_payment(self, trx_id):
try:
headers = {
'Accept': 'application/json',
'X-Environment': self.environment.value,
'X-Merchant-Key': self.merchant_key,
'X-API-Key': self.api_key
}
response = requests.get(
f"{self.base_url}/verify-payment/{trx_id}",
headers=headers,
timeout=30
)
if response.status_code == 200:
data = response.json()
if data['status'] == 'success':
# Payment completed successfully
self.fulfill_order(data)
return {'success': True, 'data': data}
return {
'success': False,
'status': data['status'],
'message': data.get('message', 'Payment not completed')
}
return {'success': False, 'error': f'HTTP {response.status_code}'}
except requests.RequestException as e:
return {'success': False, 'error': str(e)}
def fulfill_order(self, payment_data):
"""Your order fulfillment logic here"""
logging.info(f"Payment verified successfully: {payment_data}")
# Usage: Choose appropriate factory method based on your environment
verifier = MekapesaPaymentVerifier.sandbox('test_merchant_key', 'test_api_key') # For testing
# verifier = MekapesaPaymentVerifier.production('merchant_key', 'api_key') # For production
result = verifier.verify_payment('TXNQ5V8K2L9N3XM1')
print(result)
Success Response
{
"status": "success",
"trx_id": "TXNQ5V8K2L9N3XM1",
"amount": 237.5,
"fee": 12.5,
"currency": "USD",
"net_amount": 237.5,
"customer": {
"name": "John Doe",
"email": "john@example.com"
},
"description": "Premium Subscription Payment",
"created_at": "2024-01-15T10:30:00.000000Z",
"updated_at": "2024-01-15T10:35:45.000000Z"
}
Failed/Canceled Transaction Response
{
"status": "failed",
"trx_id": "TXNQ5V8K2L9N3XM1",
"message": "Payment failed or canceled."
}
Pending Transaction Response
{
"status": "pending",
"trx_id": "TXNQ5V8K2L9N3XM1",
"message": "Payment is still pending."
}
Payment Status Values
Status | Description | Action Required |
---|---|---|
pending |
Payment is still processing | Wait for webhook notification |
completed |
Payment was successful | Fulfill order/service |
failed |
Payment failed | Handle failed payment |
cancelled |
Payment was cancelled by user | Handle cancellation |
expired |
Payment session expired | Create new payment |
Webhooks (IPN)
Mekapesa sends real-time notifications to your specified IPN URL when payment status changes. This ensures you're immediately notified of payment completions, failures, and other status updates. Webhooks work identically in both sandbox and production environments.
Environment-Aware Webhooks
Use the same webhook URL for both sandbox and production. Mekapesa will include environment context in webhook payloads to help you differentiate between test and live transactions.
Webhook Headers
Header | Description | Example |
---|---|---|
Content-Type |
Always application/json |
application/json |
X-Signature |
HMAC-SHA256 signature for verification | a8b9c2d1e5f3... |
Webhook Payload
All webhook payloads include environment information to help you differentiate between sandbox and production transactions:
environment
field will be
sandbox
for test transactions or production
for live transactions.
Transaction IDs are prefixed accordingly (SANDBOX_
or
PRODUCTION_
).
{
"data": {
"ref_trx": "TXNT4AQFESTAG4F",
"description": "Order #1234",
"ipn_url": "https://webhook.site/5711b7d5-917a-4d94-bbb3-c28f4a37bea5",
"cancel_redirect": "https://merchant.com/cancel",
"success_redirect": "https://merchant.com/success",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"merchant_name": "Xanthus Wiggins",
"amount": 200,
"currency_code": "USD",
"environment": "production",
"is_sandbox": false
},
"message": "Payment Completed",
"status": "completed",
"timestamp": 1705747245
}
Signature Verification
Always verify webhook signatures to ensure authenticity and prevent unauthorized requests. Use your API secret (environment-specific) to verify signatures:
test_webhook_secret
for
sandbox and webhook_secret
for production environments.
<?php
// Laravel Webhook Handler
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Enums\EnvironmentMode;
class MekapesaWebhookController extends Controller
{
public function handle(Request $request)
{
// Get webhook headers
$environment = $request->header('X-Environment', 'production');
$signature = $request->header('X-Signature');
$webhookId = $request->header('X-Webhook-ID');
// Get appropriate secret based on environment
$secret = $this->getSecretForEnvironment($environment);
// Verify signature
if (!$this->verifySignature($request->getContent(), $signature, $secret)) {
Log::warning('Mekapesa webhook signature verification failed', [
'webhook_id' => $webhookId,
'environment' => $environment
]);
return response()->json(['error' => 'Invalid signature'], 401);
}
$payload = $request->json()->all();
// Handle based on environment
if ($environment === EnvironmentMode::SANDBOX->value) {
return $this->handleSandboxWebhook($payload);
} else {
return $this->handleProductionWebhook($payload);
}
}
private function getSecretForEnvironment(string $environment): string
{
// Return test secret for sandbox, live secret for production
return $environment === 'sandbox'
? config('Mekapesa.test_webhook_secret')
: config('Mekapesa.webhook_secret');
}
private function verifySignature(string $payload, string $signature, string $secret): bool
{
$expectedSignature = hash_hmac('sha256', $payload, $secret);
return hash_equals($expectedSignature, $signature);
}
private function handleSandboxWebhook(array $payload): JsonResponse
{
Log::info('Processing sandbox webhook', $payload);
// Your sandbox-specific logic here
// Don't fulfill orders, don't send emails to real customers, etc.
return response()->json(['status' => 'sandbox_processed']);
}
private function handleProductionWebhook(array $payload): JsonResponse
{
Log::info('Processing production webhook', $payload);
// Your production logic here
// Fulfill orders, send confirmation emails, etc.
return response()->json(['status' => 'processed']);
}
}
const crypto = require('crypto');
const express = require('express');
const EnvironmentMode = {
SANDBOX: 'sandbox',
PRODUCTION: 'production'
};
// Webhook handler
app.post('/api/webhooks/Mekapesa', async (req, res) => {
const environment = req.headers['x-environment'] || 'production';
const signature = req.headers['x-signature'];
const webhookId = req.headers['x-webhook-id'];
// Get appropriate secret based on environment
const secret = getSecretForEnvironment(environment);
// Verify signature
if (!verifySignature(JSON.stringify(req.body), signature, secret)) {
console.warn('Mekapesa webhook signature verification failed', {
webhook_id: webhookId,
environment: environment
});
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = req.body;
try {
// Handle based on environment
if (environment === EnvironmentMode.SANDBOX) {
await handleSandboxWebhook(payload);
} else {
await handleProductionWebhook(payload);
}
res.json({ status: 'processed' });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
function getSecretForEnvironment(environment) {
// Return test secret for sandbox, live secret for production
return environment === 'sandbox'
? process.env.Mekapesa_TEST_WEBHOOK_SECRET
: process.env.mekapesa_webhook_SECRET;
}
function verifySignature(payload, signature, secret) {
if (!signature) {
return false;
}
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(signature)
);
}
async function handleSandboxWebhook(payload) {
console.log('Processing sandbox webhook:', payload);
// Your sandbox-specific logic here
// Don't fulfill orders, don't send emails to real customers, etc.
}
async function handleProductionWebhook(payload) {
console.log('Processing production webhook:', payload);
// Your production logic here
// Fulfill orders, send confirmation emails, etc.
}
import hmac
import hashlib
import json
import logging
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
logger = logging.getLogger(__name__)
ENVIRONMENT_MODE = {
'SANDBOX': 'sandbox',
'PRODUCTION': 'production'
}
@csrf_exempt
@require_http_methods(["POST"])
def mekapesa_webhook(request):
environment = request.headers.get('X-Environment', 'production')
signature = request.headers.get('X-Signature', '')
webhook_id = request.headers.get('X-Webhook-ID')
// Get appropriate secret based on environment
secret = get_secret_for_environment(environment)
// Verify signature
if not verify_signature(request.body, signature, secret):
logger.warning('Mekapesa webhook signature verification failed', extra={
'webhook_id': webhook_id,
'environment': environment
})
return JsonResponse({'error': 'Invalid signature'}, status=401)
try:
payload = json.loads(request.body)
// Handle based on environment
if environment == ENVIRONMENT_MODE['SANDBOX']:
handle_sandbox_webhook(payload)
else:
handle_production_webhook(payload)
return JsonResponse({'status': 'processed'})
except Exception as e:
logger.error(f'Webhook processing error:{str(e)}')
return JsonResponse({'error': 'Processing failed'}, status=500)
def get_secret_for_environment(environment):
from django.conf import settings
// Return test secret for sandbox, live secret for production
return (settings.Mekapesa_TEST_WEBHOOK_SECRET
if environment == 'sandbox'
else settings.mekapesa_webhook_SECRET)
def verify_signature(payload, signature, secret):
if not signature:
return False
expected_signature = 'sha256=' + hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_signature, signature)
def handle_sandbox_webhook(payload):
logger.info('Processing sandbox webhook', extra=payload)
// Your sandbox-specific logic here
// Don't fulfill orders, don't send emails to real customers, etc.
def handle_production_webhook(payload):
logger.info('Processing production webhook', extra=payload)
// Your production logic here
// Fulfill orders, send confirmation emails, etc.
Environment-Specific Best Practices
Sandbox Webhooks
- Use for testing webhook integration
- No real money transactions
- Don't fulfill actual orders
- Don't send emails to real customers
- Use test webhook secret for verification
Production Webhooks
- Process real customer orders
- Send confirmation emails
- Update inventory systems
- Trigger fulfillment processes
- Use production webhook secret for verification
Integration Examples
Complete integration examples for popular platforms and frameworks.
<?php
// Laravel Integration Service
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Exception;
class MekapesaService
{
private string $baseUrl;
private string $merchantKey;
private string $apiKey;
private string $environment;
public function __construct()
{
$this->baseUrl = config('Mekapesa.base_url');
$this->merchantKey = config('Mekapesa.merchant_key');
$this->apiKey = config('Mekapesa.api_key');
$this->environment = config('Mekapesa.environment'); // 'sandbox' or 'production'
}
public function initiatePayment(array $paymentData): array
{
try {
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'X-Environment' => $this->environment,
'X-Merchant-Key' => $this->merchantKey,
'X-API-Key' => $this->apiKey,
])->post("{$this->baseUrl}/api/v1/initiate-payment", $paymentData);
if ($response->successful()) {
return $response->json();
}
throw new Exception('Mekapesa API Error: Payment initiation failed');
} catch (Exception $e) {
throw new Exception('Mekapesa API Error: ' . $e->getMessage());
}
}
public function verifyPayment(string $transactionId): array
{
try {
$response = Http::withHeaders([
'Accept' => 'application/json',
'X-Environment' => $this->environment,
'X-Merchant-Key' => $this->merchantKey,
'X-API-Key' => $this->apiKey,
])->get("{$this->baseUrl}/api/v1/verify-payment/{$transactionId}");
if ($response->successful()) {
return $response->json();
}
throw new Exception('Mekapesa API Error: Payment verification failed');
} catch (Exception $e) {
throw new Exception('Mekapesa API Error: ' . $e->getMessage());
}
}
}
// Configuration (config/Mekapesa.php)
return [
'base_url' => env('Mekapesa_BASE_URL', 'https://mekapesa.com'),
'environment' => env('Mekapesa_ENVIRONMENT', 'sandbox'), // sandbox or production
'merchant_key' => env('Mekapesa_MERCHANT_KEY'), // Use appropriate prefix
'api_key' => env('Mekapesa_API_KEY'), // Use appropriate prefix
];
// Usage in Controller
class PaymentController extends Controller
{
public function initiatePayment(Request $request, MekapesaService $Mekapesa)
{
$paymentData = [
'payment_amount' => $request->amount,
'currency_code' => 'USD',
'ref_trx' => 'ORDER_' . time(),
'description' => $request->description,
'success_redirect' => route('payment.success'),
'failure_url' => route('payment.failed'),
'cancel_redirect' => route('payment.cancelled'),
'ipn_url' => route('webhooks.Mekapesa'),
];
try {
$result = $Mekapesa->initiatePayment($paymentData);
return redirect($result['payment_url']);
} catch (Exception $e) {
return back()->withErrors(['error' => $e->getMessage()]);
}
}
}
// Node.js Integration Service
const axios = require('axios');
class MekapesaService {
constructor() {
this.baseUrl = process.env.Mekapesa_BASE_URL || 'https://mekapesa.com';
this.environment = process.env.Mekapesa_ENVIRONMENT || 'sandbox'; // sandbox or production
this.merchantKey = process.env.Mekapesa_MERCHANT_KEY; // Use appropriate prefix
this.apiKey = process.env.Mekapesa_API_KEY; // Use appropriate prefix
}
async initiatePayment(paymentData) {
try {
const response = await axios.post(`${this.baseUrl}/api/v1/initiate-payment`, paymentData, {
headers: {
'Content-Type': 'application/json',
'X-Environment': this.environment,
'X-Merchant-Key': this.merchantKey,
'X-API-Key': this.apiKey
}
});
return response.data;
} catch (error) {
throw new Error(`Mekapesa API Error: ${error.message}`);
}
}
async verifyPayment(transactionId) {
try {
const response = await axios.get(`${this.baseUrl}/api/v1/verify-payment/${transactionId}`, {
headers: {
'Accept': 'application/json',
'X-Environment': this.environment,
'X-Merchant-Key': this.merchantKey,
'X-API-Key': this.apiKey
}
});
return response.data;
} catch (error) {
throw new Error(`Mekapesa API Error: ${error.message}`);
}
}
}
// Express.js Route Example
const express = require('express');
const app = express();
const Mekapesa = new MekapesaService();
app.post('/initiate-payment', async (req, res) => {
const paymentData = {
payment_amount: req.body.amount,
currency_code: 'USD',
ref_trx: `ORDER_${Date.now()}`,
description: req.body.description,
success_redirect: `${req.protocol}://${req.get('host')}/payment/success`,
failure_url: `${req.protocol}://${req.get('host')}/payment/failed`,
cancel_redirect: `${req.protocol}://${req.get('host')}/payment/cancelled`,
ipn_url: `${req.protocol}://${req.get('host')}/webhooks/Mekapesa`,
};
try {
const result = await Mekapesa.initiatePayment(paymentData);
res.redirect(result.payment_url);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = MekapesaService;
# Python/Django Integration Service
import os
import requests
from django.conf import settings
class MekapesaService:
def __init__(self):
self.base_url = getattr(settings, 'Mekapesa_BASE_URL', 'https://mekapesa.com')
self.environment = getattr(settings, 'Mekapesa_ENVIRONMENT', 'sandbox') # sandbox or production
self.merchant_key = getattr(settings, 'Mekapesa_MERCHANT_KEY') # Use appropriate prefix
self.api_key = getattr(settings, 'Mekapesa_API_KEY') # Use appropriate prefix
def initiate_payment(self, payment_data):
try:
headers = {
'Content-Type': 'application/json',
'X-Environment': self.environment,
'X-Merchant-Key': self.merchant_key,
'X-API-Key': self.api_key
}
response = requests.post(
f"{self.base_url}/api/v1/initiate-payment",
headers=headers,
json=payment_data,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
raise Exception(f'Mekapesa API Error: {str(e)}')
def verify_payment(self, transaction_id):
try:
headers = {
'Accept': 'application/json',
'X-Environment': self.environment,
'X-Merchant-Key': self.merchant_key,
'X-API-Key': self.api_key
}
response = requests.get(
f"{self.base_url}/api/v1/verify-payment/{transaction_id}",
headers=headers,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
raise Exception(f'Mekapesa API Error: {str(e)}')
# Django Settings Configuration
Mekapesa_BASE_URL = 'https://mekapesa.com'
Mekapesa_ENVIRONMENT = 'sandbox' # Change to 'production' for live
Mekapesa_MERCHANT_KEY = os.environ.get('Mekapesa_MERCHANT_KEY') # Use appropriate prefix
Mekapesa_API_KEY = os.environ.get('Mekapesa_API_KEY') # Use appropriate prefix
# Django View Example
from django.shortcuts import redirect
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
Mekapesa = MekapesaService()
@csrf_exempt
def initiate_payment(request):
if request.method == 'POST':
data = json.loads(request.body)
payment_data = {
'payment_amount': data['amount'],
'currency_code': 'USD',
'ref_trx': f'ORDER_{int(time.time())}',
'description': data['description'],
'success_redirect': request.build_absolute_uri('/payment/success/'),
'failure_url': request.build_absolute_uri('/payment/failed/'),
'cancel_redirect': request.build_absolute_uri('/payment/cancelled/'),
'ipn_url': request.build_absolute_uri('/webhooks/Mekapesa/'),
}
try:
result = Mekapesa.initiate_payment(payment_data)
return redirect(result['payment_url'])
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
# Environment Variables Setup
export Mekapesa_ENVIRONMENT="sandbox" # or "production"
export Mekapesa_MERCHANT_KEY="test_merchant_your_key" # or "merchant_your_key" for production
export Mekapesa_API_KEY="test_your_api_key" # or "your_api_key" for production
# Initiate Payment
curl -X POST "https://mekapesa.com/api/v1/initiate-payment" \
-H "Content-Type: application/json" \
-H "X-Environment: $Mekapesa_ENVIRONMENT" \
-H "X-Merchant-Key: $Mekapesa_MERCHANT_KEY" \
-H "X-API-Key: $Mekapesa_API_KEY" \
-d '{
"payment_amount": 250.00,
"currency_code": "USD",
"ref_trx": "ORDER_12345",
"description": "Premium Subscription",
"success_redirect": "https://yoursite.com/payment/success",
"failure_url": "https://yoursite.com/payment/failed",
"cancel_redirect": "https://yoursite.com/payment/cancelled",
"ipn_url": "https://yoursite.com/api/webhooks/Mekapesa"
}'
# Verify Payment
curl -X GET "https://mekapesa.com/api/v1/verify-payment/TXNQ5V8K2L9N3XM1" \
-H "Accept: application/json" \
-H "X-Environment: $Mekapesa_ENVIRONMENT" \
-H "X-Merchant-Key: $Mekapesa_MERCHANT_KEY" \
-H "X-API-Key: $Mekapesa_API_KEY"
# Environment-specific credential examples:
# Sandbox: test_merchant_xxxxx, test_api_key_xxxxx
# Production: merchant_xxxxx, api_key_xxxxx
WooCommerce Integration
Advanced Mekapesa payment gateway with modern WooCommerce Blocks support, dynamic branding, and enterprise-grade security features.
Advanced WooCommerce Integration
Production-ready payment gateway with WooCommerce Blocks (Gutenberg) support, dynamic branding, and secure webhook processing.
Latest Plugin Download
Enterprise-grade WooCommerce payment gateway with modern Blocks support and dynamic branding.
System Requirements
- WordPress 5.8+
- WooCommerce 6.0+
- PHP 8.1+ (8.2+ Recommended)
- SSL Certificate (Required)
- Mekapesa Merchant Account
- WooCommerce Blocks Support
Advanced Features
Enterprise-grade payment processing with modern architecture
WooCommerce Blocks
Full Gutenberg checkout compatibility with React-based UI
Dynamic Branding
Auto-fetch logo, colors, and branding from Mekapesa API
Secure Webhooks
HMAC-SHA256 signature verification for payment callbacks
Compact Mobile UI
Space-efficient, responsive design for all devices
Advanced Security
Multi-header authentication with environment isolation
Test/Live Mode
Seamless sandbox testing with production deployment
Quick Installation Guide
Get started with Mekapesa WooCommerce integration in minutes
Download Latest Plugin
Download Mekapesa WooCommerce Gateway v2.8.0 from the download section above. This includes all latest features and security updates.
Install via WordPress Admin
Navigate to Plugins → Add New → Upload Plugin
and select
the downloaded ZIP file. The plugin will auto-extract and install.
Activate & Configure
Activate the plugin and go to
WooCommerce → Settings → Payments → Mekapesa
. Enter
your API credentials and webhook secret.
Test Integration
Enable Test Mode, process a sandbox transaction to verify Blocks checkout, webhook delivery, and order completion.
Go Live
Disable test mode, ensure production API keys are configured, and start accepting real payments with full webhook processing.
API Configuration
Essential API settings for secure payment processing
API Base URL
https://mekapesa.com
Merchant ID & API Key
Your unique merchant credentials from Mekapesa dashboard
RequiredWebhook Secret
HMAC-SHA256 signature verification for secure callbacks
RecommendedAuthentication Headers
Required headers for all Mekapesa API requests:
X-Environment: sandbox|production
X-Merchant-Key: your_merchant_id
X-API-Key: your_api_key
Content-Type: application/json
Webhook Configuration
Real-time payment status updates and order processing
Webhook Endpoint
https://yoursite.com/wc-api/mekapesa_webhook
Configure this URL in your Mekapesa merchant dashboard for automatic order updates.
Supported Events
- Payment Success
- Payment Failed
- Payment Cancelled
- Payment Pending
- Refund Processed
- Payment Timeout
WooCommerce Blocks Integration
Modern Gutenberg checkout with React-based payment UI
Responsive Design
Compact, mobile-optimized payment interface that adapts to any screen size.
Dynamic Branding
Automatically fetches and displays your Mekapesa branding and logos.
Security Indicators
Clear SSL and security badges to build customer trust during checkout.
Test Mode Support
Clear sandbox indicators for testing without affecting live transactions.
Production Deployment Checklist
Ensure everything is configured correctly before going live
Technical Requirements
API Configuration
Final Verification
Troubleshooting
Common issues and solutions for Mekapesa WooCommerce integration
Payment method not showing in checkout
Solutions:
- Verify plugin is activated and enabled in WooCommerce → Settings → Payments
- Clear browser cache and WooCommerce cache
- Check if API credentials are correctly configured
- Ensure SSL certificate is properly installed
401 Unauthorized API errors
Solutions:
- Verify Merchant ID and API Key are correct
- Ensure environment (sandbox/production) matches your credentials
- Check that all required headers are being sent
- Contact Mekapesa support to verify account status
Orders not updating after payment
Solutions:
- Verify webhook URL is configured in Mekapesa dashboard
- Check webhook secret key matches plugin configuration
- Review WordPress error logs for webhook processing errors
- Test webhook delivery using Mekapesa dashboard tools
Interactive API Testing
Test Mekapesa API endpoints directly from this documentation. Use the demo credentials below for sandbox testing.
Demo Payment Information SANDBOX MODE
Use these demo credentials to test all payment methods in sandbox environment:
Demo Wallet
123456789
Password:
demo123
Demo Voucher
TESTVOUCHER
Gateway Payment
Auto Success
Testing Guidelines
- Environment Header: Always include
X-ENVIRONMENT: sandbox
in your API requests - Demo Credentials: Use the provided demo wallet/voucher codes for testing payment flows
- Sandbox Behavior: All payments auto-complete successfully without real money processing
- Transaction Status: Sandbox transactions are marked with "SANDBOX_TRANSACTION" in remarks
- IPN Notifications: Webhook notifications work normally in sandbox mode
sandbox
for testing and
production
for live transactions. Only sandbox credentials use
test_
prefix, production credentials have no prefix.
API Testing Console
Authentication Headers
Request Parameters
Sandbox Environment
Base URL: https://mekapesa.com
Environment Header: X-Environment: sandbox
Credentials: Use test_
prefixed keys
Purpose: Safe testing without real money
Production Environment
Base URL: https://mekapesa.com
Environment Header: X-Environment: production
Credentials: No prefix for production keys
Purpose: Live transactions with real money
Error Codes
Mekapesa API uses conventional HTTP response codes to indicate the success or failure of API requests.
HTTP Status Codes
Code | Status | Description |
---|---|---|
200 | OK | Request succeeded |
400 | Bad Request | Invalid request parameters |
401 | Unauthorized | Invalid or missing API credentials |
403 | Forbidden | Insufficient permissions |
404 | Not Found | Resource not found |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Server error occurred |
API Error Codes
Error Code | Description | Solution |
---|---|---|
INVALID_CREDENTIALS |
Invalid API credentials provided | Check your Merchant ID and API Key |
INSUFFICIENT_FUNDS |
Customer has insufficient funds | Customer needs to add funds to their wallet |
PAYMENT_DECLINED |
Payment was declined by payment processor | Customer should try a different payment method |
INVALID_AMOUNT |
Payment amount is invalid | Check minimum and maximum amount limits |
INVALID_CURRENCY |
Unsupported currency code | Use a supported currency code (USD, EUR, etc.) |
DUPLICATE_REFERENCE |
Transaction reference already exists | Use a unique transaction reference |
EXPIRED_SESSION |
Payment session has expired | Create a new payment request |
MERCHANT_SUSPENDED |
Merchant account is suspended | Contact Mekapesa support |
Error Response Format
{
"success": false,
"message": "Validation failed",
"error_code": "INVALID_AMOUNT",
"errors": {
"payment_amount": [
"The payment amount must be at least 1.00"
]
},
"timestamp": "2024-01-20T10:30:00Z"
}
Support
Technical Support
Need assistance with Mekapesa API integration? Our technical team provides comprehensive support.