Integration Guide

Complete documentation for integrating PayNexus M-Pesa payments

Base URL
https://paynexus.mcbankske.space/api
Authentication
X-API-Key: your_key_here
API Versions
v1 (Stable) | v2 (Enhanced)
Rate Limits
v1: 60/min | v2: 100/min

PayNexus M-Pesa Payment Integration Guide



This comprehensive guide helps developers integrate PayNexus M-Pesa payment processing into their external websites and applications.

Table of Contents



1. [Overview](#overview)
2. [Authentication](#authentication)
3. [API Endpoints](#api-endpoints)
4. [Integration Examples](#integration-examples)
- [PHP](#php)
- [JavaScript/Node.js](#javascriptnodejs)
- [Python](#python)
- [cURL](#curl)
5. [Webhook Integration](#webhook-integration)
6. [Error Handling](#error-handling)
7. [Security Best Practices](#security-best-practices)
8. [Testing](#testing)
9. [Support](#support)

Overview



PayNexus provides a simple REST API for processing M-Pesa payments through your merchant account. The API supports:

- STK Push Payments: Initiate M-Pesa payments directly from customer phones
- Payment Status Tracking: Check transaction status in real-time
- Phone Validation: Validate and normalize phone numbers
- Webhook Callbacks: Receive payment notifications
- Multi-account Support: Manage multiple payment accounts

Base URL



Production: https://paynexus.mcbankske.space/api


Authentication



PayNexus uses API key authentication. Include your API key in the request header:

bash
X-API-Key: your_api_key_here


Types of API Keys


- Public Keys (pk_...): For read operations (merchant info, payment accounts, status checks)
- Secret Keys (sk_...): For write operations (initiate payments, manage webhooks)

Never expose your secret keys in client-side code!

API Endpoints



Core Endpoints



| Method | Endpoint | Description | Auth Required |
|--------|----------------------------|------------------------|---------------|
| GET | /health | API health check | No |
| GET | /mpesa/health | M-Pesa service status | No |
| GET | /merchant | Get merchant information | Public Key |
| GET | /merchant/payment-accounts | List payment accounts | Public Key |
| POST | /mpesa/validate-phone | Validate phone number | Public Key |
| POST | /mpesa/payment/initiate | Initiate STK push payment | Secret Key |
| POST | /mpesa/payment/status | Check payment status | Secret Key |
| GET | /api-keys | List API keys | Public Key |

Integration Examples



PHP



#### 1. Install Guzzle HTTP Client
bash
composer require guzzlehttp/guzzle


#### 2. Payment Integration Example

php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class PayNexusClient
{
private $client;
private $apiKey;
private $baseUrl;

public function __construct($apiKey, $baseUrl = 'https://paynexus.mcbankske.space/api')
{
$this->apiKey = $apiKey;
$this->baseUrl = $baseUrl;

$this->client = new Client([
'base_uri' => $baseUrl,
'headers' => [
'X-API-Key' => $apiKey,
'Content-Type' => 'application/json',
'Accept' => 'application/json'
],
'timeout' => 30
]);
}

/
* Initiate M-Pesa STK Push Payment
*/
public function initiatePayment($paymentAccountId, $amount, $phone, $accountReference = null, $description = null)
{
try {
$payload = [
'payment_account_id' => $paymentAccountId,
'amount' => $amount,
'phone' => $phone,
'account_reference' => $accountReference ?: 'ORDER_' . time(),
'description' => $description ?: 'Payment for order',
'remark' => 'Website Payment'
];

$response = $this->client->post('/mpesa/payment/initiate', [
'json' => $payload
]);

$data = json_decode($response->getBody()->getContents(), true);

if ($data['success']) {
return [
'success' => true,
'payment_id' => $data['data']['payment_id'],
'checkout_request_id' => $data['data']['checkout_request_id'],
'customer_message' => $data['data']['customer_message']
];
} else {
return [
'success' => false,
'error' => $data['error'] ?? 'Payment initiation failed'
];
}

} catch (RequestException $e) {
return [
'success' => false,
'error' => $e->getMessage(),
'response' => $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : null
];
}
}

/
* Check payment status
*/
public function checkPaymentStatus($transactionId)
{
try {
$response = $this->client->post('/mpesa/payment/status', [
'json' => [
'transaction_id' => $transactionId
]
]);

$data = json_decode($response->getBody()->getContents(), true);

return [
'success' => true,
'status' => $data['data']['status'] ?? 'unknown',
'transaction_id' => $data['data']['transaction_id'] ?? null,
'amount' => $data['data']['amount'] ?? null
];

} catch (RequestException $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}

/
* Validate phone number
*/
public function validatePhone($phone)
{
try {
$response = $this->client->post('/mpesa/validate-phone', [
'json' => ['phone' => $phone]
]);

$data = json_decode($response->getBody()->getContents(), true);

return [
'success' => true,
'valid' => $data['data']['valid'],
'normalized' => $data['data']['normalized'] ?? $phone
];

} catch (RequestException $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
}

// Usage Example
$paynexus = new PayNexusClient('sk_your_secret_key_here');

// Validate phone number
$phoneValidation = $paynexus->validatePhone('0798808796');
if ($phoneValidation['success'] && $phoneValidation['valid']) {
$normalizedPhone = $phoneValidation['normalized'];

// Initiate payment
$payment = $paynexus->initiatePayment(
1, // Payment account ID
100, // Amount in KES
$normalizedPhone,
'ORDER_12345',
'Payment for website order'
);

if ($payment['success']) {
echo "Payment initiated! Checkout Request ID: " . $payment['checkout_request_id'];
echo "Customer message: " . $payment['customer_message'];

// Store checkout_request_id for status checking
// Redirect to payment confirmation page
} else {
echo "Payment failed: " . $payment['error'];
}
}
?>


#### 3. Webhook Handler

php
// webhook_handler.php
header('Content-Type: application/json');

// Get the webhook payload
$payload = file_get_contents('php://input');
$data = json_decode($payload, true);

// Verify webhook (implement your verification logic)
if (!verifyWebhookSignature($payload)) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}

// Process the callback
if (isset($data['ResultCode']) && $data['ResultCode'] == 0) {
// Payment successful
$transactionId = $data['TransactionID'] ?? null;
$checkoutRequestId = $data['CheckoutRequestID'] ?? null;
$amount = $data['Amount'] ?? null;
$phoneNumber = $data['PhoneNumber'] ?? null;

// Update your database
// Mark order as paid
// Send confirmation email
// Update customer account

logTransaction($transactionId, $checkoutRequestId, 'completed', $amount, $phoneNumber);

} else {
// Payment failed
$checkoutRequestId = $data['CheckoutRequestID'] ?? null;
$errorCode = $data['ResultCode'] ?? null;
$errorMessage = $data['ResultDesc'] ?? 'Payment failed';

// Update your database
// Mark order as failed
// Notify customer

logTransaction(null, $checkoutRequestId, 'failed', null, null, $errorMessage);
}

// Always return success to avoid retries
echo json_encode(['ResultCode' => 0, 'ResultDesc' => 'Callback received']);

function verifyWebhookSignature($payload)
{
// Implement your webhook verification logic
// Check against stored secrets, verify timestamps, etc.
return true; // Simplified for example
}

function logTransaction($transactionId, $checkoutRequestId, $status, $amount, $phone, $error = null)
{
// Implement your database logging
$logEntry = [
'transaction_id' => $transactionId,
'checkout_request_id' => $checkoutRequestId,
'status' => $status,
'amount' => $amount,
'phone' => $phone,
'error' => $error,
'timestamp' => date('Y-m-d H:i:s')
];

// Save to database or log file
file_put_contents('payment_log.txt', json_encode($logEntry) . "\n", FILE_APPEND);
}
?>


JavaScript/Node.js



#### 1. Install Axios
bash
npm install axios


#### 2. Payment Integration Example

javascript
// paynexus-client.js
const axios = require('axios');

class PayNexusClient {
constructor(apiKey, baseUrl = 'https://paynexus.mcbankske.space/api') {
this.apiKey = apiKey;
this.baseUrl = baseUrl;

this.client = axios.create({
baseURL: baseUrl,
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
timeout: 30000
});
}

/
* Initiate M-Pesa STK Push Payment
*/
async initiatePayment(paymentAccountId, amount, phone, accountReference = null, description = null) {
try {
const payload = {
payment_account_id: paymentAccountId,
amount: amount,
phone: phone,
account_reference: accountReference ||
ORDER_${Date.now()},
description: description || 'Payment for order',
remark: 'Website Payment'
};

const response = await this.client.post('/mpesa/payment/initiate', payload);

if (response.data.success) {
return {
success: true,
payment_id: response.data.data.payment_id,
checkout_request_id: response.data.data.checkout_request_id,
customer_message: response.data.data.customer_message
};
} else {
return {
success: false,
error: response.data.error || 'Payment initiation failed'
};
}

} catch (error) {
return {
success: false,
error: error.response?.data?.error || error.message,
details: error.response?.data
};
}
}

/
* Check payment status
*/
async checkPaymentStatus(transactionId) {
try {
const response = await this.client.post('/mpesa/payment/status', {
transaction_id: transactionId
});

return {
success: true,
status: response.data.data.status,
transaction_id: response.data.data.transaction_id,
amount: response.data.data.amount
};

} catch (error) {
return {
success: false,
error: error.response?.data?.error || error.message
};
}
}

/
* Validate phone number
*/
async validatePhone(phone) {
try {
const response = await this.client.post('/mpesa/validate-phone', {
phone: phone
});

return {
success: true,
valid: response.data.data.valid,
normalized: response.data.data.normalized || phone
};

} catch (error) {
return {
success: false,
error: error.response?.data?.error || error.message
};
}
}

/
* Get merchant information
*/
async getMerchantInfo() {
try {
const response = await this.client.get('/merchant');
return {
success: true,
data: response.data.data
};
} catch (error) {
return {
success: false,
error: error.response?.data?.error || error.message
};
}
}

/
* Get payment accounts
*/
async getPaymentAccounts() {
try {
const response = await this.client.get('/merchant/payment-accounts');
return {
success: true,
data: response.data.data
};
} catch (error) {
return {
success: false,
error: error.response?.data?.error || error.message
};
}
}
}

// Usage Example
async function processPayment() {
const paynexus = new PayNexusClient('sk_your_secret_key_here');

try {
// Get merchant payment accounts
const accounts = await paynexus.getPaymentAccounts();
if (accounts.success && accounts.data.length > 0) {
const mpesaAccount = accounts.data.find(acc => acc.provider === 'mpesa');

if (mpesaAccount) {
// Validate phone number
const phoneValidation = await paynexus.validatePhone('0798808796');

if (phoneValidation.success && phoneValidation.valid) {
// Initiate payment
const payment = await paynexus.initiatePayment(
mpesaAccount.id,
100,
phoneValidation.normalized,
'ORDER_12345',
'Payment for website order'
);

if (payment.success) {
console.log('Payment initiated successfully!');
console.log('Checkout Request ID:', payment.checkout_request_id);
console.log('Customer message:', payment.customer_message);

// Store checkout_request_id for status checking
// Show payment confirmation to user
} else {
console.error('Payment failed:', payment.error);
}
} else {
console.error('Invalid phone number:', phoneValidation.error);
}
} else {
console.error('No M-Pesa account found');
}
} else {
console.error('Failed to get payment accounts:', accounts.error);
}
} catch (error) {
console.error('Payment processing error:', error);
}
}

processPayment();


#### 3. Express.js Webhook Handler

javascript
// webhook-server.js
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

// Webhook endpoint
app.post('/webhook/mpesa', (req, res) => {
const payload = req.body;

// Verify webhook signature (implement your verification)
if (!verifyWebhookSignature(req)) {
return res.status(401).json({ error: 'Unauthorized' });
}

// Process the callback
if (payload.ResultCode === 0) {
// Payment successful
const transactionId = payload.TransactionID;
const checkoutRequestId = payload.CheckoutRequestID;
const amount = payload.Amount;
const phoneNumber = payload.PhoneNumber;

console.log(
Payment successful: ${transactionId} for ${amount} KES);

// Update database
// Mark order as paid
// Send confirmation email
// Update customer account

updatePaymentStatus(checkoutRequestId, 'completed', {
transactionId,
amount,
phoneNumber
});

} else {
// Payment failed
const checkoutRequestId = payload.CheckoutRequestID;
const errorCode = payload.ResultCode;
const errorMessage = payload.ResultDesc;

console.log(
Payment failed: ${checkoutRequestId} - ${errorMessage});

// Update database
// Mark order as failed
// Notify customer

updatePaymentStatus(checkoutRequestId, 'failed', {
errorCode,
errorMessage
});
}

// Always return success to avoid retries
res.json({ ResultCode: 0, ResultDesc: 'Callback received' });
});

function verifyWebhookSignature(req) {
// Implement your webhook verification logic
// Check against stored secrets, verify timestamps, etc.
return true; // Simplified for example
}

function updatePaymentStatus(checkoutRequestId, status, details) {
// Implement your database update logic
console.log(
Updating payment ${checkoutRequestId} to ${status}, details);

// Example: Update in database
// db.collection('payments').updateOne(
// { checkout_request_id: checkoutRequestId },
// {
// $set: {
// status,
// updated_at: new Date(),
// ...details
// }
// }
// );
}

const PORT = process.env.WEBHOOK_PORT || 3000;
app.listen(PORT, () => {
console.log(
Webhook server listening on port ${PORT});
});


Python



#### 1. Install Requests
bash
pip install requests


#### 2. Payment Integration Example

python

paynexus_client.py


import requests
import json
from typing import Dict, Optional, Any

class PayNexusClient:
def __init__(self, api_key: str, base_url: str = 'https://paynexus.mcbankske.space/api'):
self.api_key = api_key
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({
'X-API-Key': api_key,
'Content-Type': 'application/json',
'Accept': 'application/json'
})
self.session.timeout = 30

def initiate_payment(self, payment_account_id: int, amount: float, phone: str,
account_reference: Optional[str] = None,
description: Optional[str] = None) -> Dict[str, Any]:
"""
Initiate M-Pesa STK Push Payment
"""
try:
payload = {
'payment_account_id': payment_account_id,
'amount': amount,
'phone': phone,
'account_reference': account_reference or f'ORDER_{int(time.time())}',
'description': description or 'Payment for order',
'remark': 'Website Payment'
}

response = self.session.post(f'{self.base_url}/mpesa/payment/initiate', json=payload)
response.raise_for_status()

data = response.json()

if data.get('success'):
return {
'success': True,
'payment_id': data['data']['payment_id'],
'checkout_request_id': data['data']['checkout_request_id'],
'customer_message': data['data']['customer_message']
}
else:
return {
'success': False,
'error': data.get('error', 'Payment initiation failed')
}

except requests.exceptions.RequestException as e:
return {
'success': False,
'error': str(e),
'response': e.response.text if e.response else None
}

def check_payment_status(self, transaction_id: str) -> Dict[str, Any]:
"""
Check payment status
"""
try:
payload = {
'transaction_id': transaction_id
}

response = self.session.post(f'{self.base_url}/mpesa/payment/status', json=payload)
response.raise_for_status()

data = response.json()

return {
'success': True,
'status': data['data'].get('status', 'unknown'),
'transaction_id': data['data'].get('transaction_id'),
'amount': data['data'].get('amount')
}

except requests.exceptions.RequestException as e:
return {
'success': False,
'error': str(e)
}

def validate_phone(self, phone: str) -> Dict[str, Any]:
"""
Validate phone number
"""
try:
payload = {'phone': phone}
response = self.session.post(f'{self.base_url}/mpesa/validate-phone', json=payload)
response.raise_for_status()

data = response.json()

return {
'success': True,
'valid': data['data']['valid'],
'normalized': data['data'].get('normalized', phone)
}

except requests.exceptions.RequestException as e:
return {
'success': False,
'error': str(e)
}

def get_merchant_info(self) -> Dict[str, Any]:
"""
Get merchant information
"""
try:
response = self.session.get(f'{self.base_url}/merchant')
response.raise_for_status()

return {
'success': True,
'data': response.json()['data']
}

except requests.exceptions.RequestException as e:
return {
'success': False,
'error': str(e)
}

def get_payment_accounts(self) -> Dict[str, Any]:
"""
Get payment accounts
"""
try:
response = self.session.get(f'{self.base_url}/merchant/payment-accounts')
response.raise_for_status()

return {
'success': True,
'data': response.json()['data']
}

except requests.exceptions.RequestException as e:
return {
'success': False,
'error': str(e)
}


Usage Example


import time

def process_payment():
paynexus = PayNexusClient('sk_your_secret_key_here')

try:
# Get merchant payment accounts
accounts = paynexus.get_payment_accounts()
if accounts['success'] and accounts['data']:
mpesa_account = next((acc for acc in accounts['data'] if acc['provider'] == 'mpesa'), None)

if mpesa_account:
# Validate phone number
phone_validation = paynexus.validate_phone('0798808796')

if phone_validation['success'] and phone_validation['valid']:
# Initiate payment
payment = paynexus.initiate_payment(
mpesa_account['id'],
100,
phone_validation['normalized'],
'ORDER_12345',
'Payment for website order'
)

if payment['success']:
print(f"Payment initiated successfully!")
print(f"Checkout Request ID: {payment['checkout_request_id']}")
print(f"Customer message: {payment['customer_message']}")

# Store checkout_request_id for status checking
# Show payment confirmation to user
return payment
else:
print(f"Payment failed: {payment['error']}")
else:
print(f"Invalid phone number: {phone_validation['error']}")
else:
print("No M-Pesa account found")
else:
print(f"Failed to get payment accounts: {accounts['error']}")

except Exception as e:
print(f"Payment processing error: {e}")

if __name__ == "__main__":
process_payment()


#### 3. Flask Webhook Handler

python

webhook_server.py


from flask import Flask, request, jsonify
import hashlib
import hmac
import json
from datetime import datetime

app = Flask(__name__)

@app.route('/webhook/mpesa', methods=['POST'])
def mpesa_webhook():
payload = request.get_json()

# Verify webhook signature (implement your verification)
if not verify_webhook_signature(request):
return jsonify({'error': 'Unauthorized'}), 401

# Process the callback
if payload.get('ResultCode') == 0:
# Payment successful
transaction_id = payload.get('TransactionID')
checkout_request_id = payload.get('CheckoutRequestID')
amount = payload.get('Amount')
phone_number = payload.get('PhoneNumber')

print(f"Payment successful: {transaction_id} for {amount} KES")

# Update database
# Mark order as paid
# Send confirmation email
# Update customer account

update_payment_status(checkout_request_id, 'completed', {
'transaction_id': transaction_id,
'amount': amount,
'phone_number': phone_number
})

else:
# Payment failed
checkout_request_id = payload.get('CheckoutRequestID')
error_code = payload.get('ResultCode')
error_message = payload.get('ResultDesc')

print(f"Payment failed: {checkout_request_id} - {error_message}")

# Update database
# Mark order as failed
# Notify customer

update_payment_status(checkout_request_id, 'failed', {
'error_code': error_code,
'error_message': error_message
})

# Always return success to avoid retries
return jsonify({'ResultCode': 0, 'ResultDesc': 'Callback received'})

def verify_webhook_signature(request):
# Implement your webhook verification logic
# Check against stored secrets, verify timestamps, etc.
return True # Simplified for example

def update_payment_status(checkout_request_id, status, details):
# Implement your database update logic
print(f"Updating payment {checkout_request_id} to {status}", details)

# Example: Update in database
# db.payments.update_one(
# {'checkout_request_id': checkout_request_id},
# {
# '$set': {
# 'status': status,
# 'updated_at': datetime.utcnow(),
# details
# }
# }
# )

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)


cURL Examples



#### Basic API Calls

bash

Health Check


curl -X GET "https://paynexus.mcbankske.space/api/health" \
-H "Accept: application/json"

Get Merchant Info


curl -X GET "https://paynexus.mcbankske.space/api/merchant" \
-H "X-API-Key: pk_your_public_key_here" \
-H "Accept: application/json"

Get Payment Accounts


curl -X GET "https://paynexus.mcbankske.space/api/merchant/payment-accounts" \
-H "X-API-Key: pk_your_public_key_here" \
-H "Accept: application/json"

Validate Phone Number


curl -X POST "https://paynexus.mcbankske.space/api/mpesa/validate-phone" \
-H "X-API-Key: pk_your_public_key_here" \
-H "Content-Type: application/json" \
-d '{"phone": "0798808796"}'

Initiate Payment


curl -X POST "https://paynexus.mcbankske.space/api/mpesa/payment/initiate" \
-H "X-API-Key: sk_your_secret_key_here" \
-H "Content-Type: application/json" \
-d '{
"payment_account_id": 1,
"amount": 100,
"phone": "254798808796",
"account_reference": "ORDER_12345",
"description": "Payment for website order",
"remark": "Website Payment"
}'

Check Payment Status


curl -X POST "https://paynexus.mcbankske.space/api/mpesa/payment/status" \
-H "X-API-Key: sk_your_secret_key_here" \
-H "Content-Type: application/json" \
-d '{
"transaction_id": "YOUR_TRANSACTION_ID_HERE"
}'


Webhook Integration



Setting Up Webhooks



1. Register Webhook URL (via API or dashboard)
2. Handle Incoming Callbacks from M-Pesa
3. Verify Webhook Signatures for security
4. Update Your Database based on payment status

Webhook Payload Structure



json
{
"ResultCode": 0,
"ResultDesc": "Success. Request accepted for processing",
"CheckoutRequestID": "ws_CO_27012026101718139798808796",
"MerchantRequestID": "e569-40f5-bc0a-03c08bc2061a3446498",
"Amount": "100",
"MpesaReceiptNumber": "OEI2AK4Q16",
"TransactionDate": "20260127101718",
"PhoneNumber": "254798808796"
}


Result Codes



| Code | Description |
|------|-------------|
| 0 | Success |
| 1 | Insufficient funds |
| 1032 | Account closed |
| 1037 | Account blocked |
| 2001 | Invalid amount |
| 2002 | Invalid phone number |
| 2003 | Invalid transaction |
| 2004 | Invalid security credential |
| 2005 | Invalid receiver party |
| 2006 | Invalid initiator |
| 2007 | Invalid short code |

Error Handling



Common Error Responses



json
{
"success": false,
"error": "Validation failed",
"code": "VALIDATION_FAILED",
"details": {
"payment_account_id": ["The payment account id field is required."],
"amount": ["The amount must be at least 1."]
}
}


HTTP Status Codes



| Status | Description |
|--------|-------------|
| 200 | Success |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 422 | Validation Error |
| 500 | Server Error |

Error Handling Best Practices



1. Always check the success field in API responses
2. Handle validation errors gracefully
3. Implement retry logic for network failures
4. Log errors for debugging
5. Provide user-friendly error messages

Security Best Practices



API Key Security



1. Never expose secret keys in client-side code
2. Use environment variables to store API keys
3. Rotate API keys regularly
4. Use different keys for different environments
5. Implement IP restrictions where possible

Webhook Security



1. Verify webhook signatures using HMAC
2. Check request timestamps to prevent replay attacks
3. Use HTTPS for all webhook endpoints
4. Validate payload structure before processing

Example Environment Setup



bash

.env file


PAYNEXUS_PUBLIC_KEY=pk_your_public_key_here
PAYNEXUS_SECRET_KEY=sk_your_secret_key_here
PAYNEXUS_WEBHOOK_SECRET=your_webhook_secret_here
PAYNEXUS_BASE_URL=https://api.paynexus.co.ke/api


javascript
// JavaScript example
const apiKey = process.env.PAYNEXUS_SECRET_KEY;
const baseUrl = process.env.PAYNEXUS_BASE_URL;


Testing



Using the Test Suite



Run the included test suite to verify your integration:

bash

Run the test script


bash test_mpesa_postman.sh


Test Cards/Numbers



For testing, use these phone numbers:
- 254798808796 - Valid test number
- 0798808796 - Valid test number (will be normalized)

Test Amounts



- Use small amounts (1-10 KES) for testing
- Maximum test amount: 150,000 KES

Support



Documentation



- API Reference: /api/docs endpoint
- Health Check: /api/health
- M-Pesa Health: /api/mpesa/health

Common Issues



1. "Insufficient permissions" - Use correct API key type
2. "Invalid phone number" - Validate phone numbers first
3. "Payment account not found" - Check payment account ID
4. "Webhook timeout" - Ensure webhook endpoint is accessible

Getting Help



- Check the API logs for detailed error messages
- Verify your API keys and permissions
- Test with the provided test suite
- Contact support with error details and request IDs

---

Quick Start Checklist



- [ ] Get your API keys from the PayNexus dashboard
- [ ] Choose your integration language (PHP, Node.js, Python)
- [ ] Set up your development environment
- [ ] Test the health endpoints
- [ ] Implement phone validation
- [ ] Implement payment initiation
- [ ] Set up webhook handler
- [ ] Test the complete flow
- [ ] Deploy to production
- [ ] Monitor payment status and errors

Happy coding! 🚀