Code Examples

Ready-to-use code examples in PHP, JavaScript, Python, and cURL for rapid integration

Base URL
https://paynexus.mcbankske.space/api
Authentication
X-API-Key: your_key_here

🐘 PHP Examples

Complete Payment Integration (Updated with Proper Account Reference)

āš ļø Critical: Account reference usage depends on account type. Paybill accounts require merchant's account number for proper routing. Till accounts can use order references.

Vanilla PHP - Complete Workflow

<?php
class PaynexusClient {
    private $baseUrl = 'https://paynexus.mcbankske.space/api';
    private $secretKey = 'your_secret_api_key_here';
    
    public function getPaymentAccounts() {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->baseUrl . '/merchant/payment-accounts');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'X-API-Key: ' . $this->secretKey
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        return [
            'success' => $httpCode === 200,
            'data' => json_decode($response, true)['data'] ?? []
        ];
    }
    
    public function validatePhone($phone) {
        $data = ['phone' => $phone];
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->baseUrl . '/mpesa/validate-phone');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'X-API-Key: ' . $this->secretKey
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        return [
            'success' => $httpCode === 200,
            'data' => json_decode($response, true)['data'] ?? []
        ];
    }
    
    public function initiatePayment($paymentAccountId, $amount, $phone, $accountReference, $description = null) {
        $data = [
            'payment_account_id' => $paymentAccountId,
            'amount' => $amount,
            'phone' => $phone,
            'account_reference' => $accountReference, // CRITICAL: Must be set correctly
            'description' => $description ?: 'Payment via PayNexus',
            'remark' => 'Website Payment'
        ];
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->baseUrl . '/mpesa/payment/initiate');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'X-API-Key: ' . $this->secretKey
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        return [
            'success' => $httpCode === 200,
            'data' => json_decode($response, true)['data'] ?? []
        ];
    }
}

// COMPLETE USAGE EXAMPLE WITH PROPER ACCOUNT REFERENCE HANDLING
$paynexus = new PaynexusClient();

// Step 1: Get payment accounts to determine account type
$accountsResponse = $paynexus->getPaymentAccounts();
if (!$accountsResponse['success']) {
    die('Failed to get payment accounts: ' . json_encode($accountsResponse));
}

$mpesaAccount = null;
foreach ($accountsResponse['data'] as $account) {
    if ($account['provider'] === 'mpesa') {
        $mpesaAccount = $account;
        break;
    }
}

if (!$mpesaAccount) {
    die('No M-Pesa payment account found');
}

// Step 2: Validate phone number
$phoneValidation = $paynexus->validatePhone('0798808796');
if (!$phoneValidation['success'] || !$phoneValidation['data']['valid']) {
    die('Invalid phone number: ' . $phoneValidation['data']['error'] ?? 'Unknown error');
}
$normalizedPhone = $phoneValidation['data']['normalized'];

// Step 3: CRITICAL - Determine account reference based on account type
if ($mpesaAccount['type'] === 'paybill') {
    // FOR PAYBILL: Use merchant's account number for proper fund routing
    $accountReference = $mpesaAccount['account_number'];
    if (!$accountReference) {
        die('Paybill account number is required for proper routing');
    }
    $accountReference = substr($accountReference, 0, 12); // M-Pesa limit
    echo "Using Paybill account - Account Reference: $accountReference\n";
} elseif ($mpesaAccount['type'] === 'till') {
    // FOR TILL: Can use order reference (not used for routing)
    $accountReference = 'ORDER_' . time();
    $accountReference = substr($accountReference, 0, 12);
    echo "Using Till account - Account Reference: $accountReference\n";
} else {
    die('Unsupported payment account type: ' . $mpesaAccount['type']);
}

// Step 4: Initiate payment with correct account reference
$payment = $paynexus->initiatePayment(
    $mpesaAccount['id'],  // Payment account ID
    100,                 // Amount in KES
    $normalizedPhone,
    $accountReference,   // CRITICAL: Correct account reference
    'Payment for website order'
);

if ($payment['success']) {
    echo "āœ… Payment initiated successfully!\n";
    echo "šŸ“ Checkout Request ID: " . $payment['data']['checkout_request_id'] . "\n";
    echo "šŸ’¬ Customer message: " . $payment['data']['customer_message'] . "\n";
    echo "šŸ“Š Account Type: " . $mpesaAccount['type'] . "\n";
    echo "šŸ”‘ Account Reference Used: " . $accountReference . "\n";
    
    // Store checkout_request_id for status tracking
    // Redirect customer to payment confirmation page
} else {
    echo "āŒ Payment failed: " . ($payment['data']['error'] ?? 'Unknown error') . "\n";
}
?>

Laravel Integration

Service Class

// app/Services/PaynexusService.php
namespace App\Services;

use Illuminate\Support\Facades\Http;

class PaynexusService {
    private $baseUrl;
    private $secretKey;
    
    public function __construct() {
        $this->baseUrl = 'https://paynexus.mcbankske.space/api';
        $this->secretKey = config('services.paynexus.secret_key');
    }
    
    public function initiatePayment(array $data) {
        $response = Http::withHeaders([
            'Content-Type' => 'application/json',
            'X-API-Key' => $this->secretKey
        ])->post($this->baseUrl . '/mpesa/payment/initiate', $data);
        
        return [
            'success' => $response->successful(),
            'data' => $response->json()
        ];
    }
}
?>

Controller Usage - Updated with Account Reference Logic

// app/Http/Controllers/PaymentController.php
<?php
namespace App\Http\Controllers;

use App\Services\PaynexusService;
use Illuminate\Http\Request;

class PaymentController extends Controller
{
    protected $paynexus;
    
    public function __construct(PaynexusService $paynexus)
    {
        $this->paynexus = $paynexus;
    }
    
    public function initiate(Request $request)
    {
        try {
            // Step 1: Get payment accounts to determine account type
            $accountsResponse = $this->paynexus->getPaymentAccounts();
            if (!$accountsResponse['success']) {
                return response()->json([
                    'success' => false,
                    'error' => 'Failed to get payment accounts'
                ], 400);
            }
            
            $mpesaAccount = null;
            foreach ($accountsResponse['data'] as $account) {
                if ($account['provider'] === 'mpesa') {
                    $mpesaAccount = $account;
                    break;
                }
            }
            
            if (!$mpesaAccount) {
                return response()->json([
                    'success' => false,
                    'error' => 'No M-Pesa payment account found'
                ], 400);
            }
            
            // Step 2: Validate phone number
            $phoneValidation = $this->paynexus->validatePhone($request->phone);
            if (!$phoneValidation['success'] || !$phoneValidation['data']['valid']) {
                return response()->json([
                    'success' => false,
                    'error' => 'Invalid phone number: ' . ($phoneValidation['data']['error'] ?? 'Unknown')
                ], 400);
            }
            
            // Step 3: CRITICAL - Determine account reference based on account type
            if ($mpesaAccount['type'] === 'paybill') {
                // FOR PAYBILL: Use merchant's account number for proper fund routing
                $accountReference = $mpesaAccount['account_number'];
                if (!$accountReference) {
                    return response()->json([
                        'success' => false,
                        'error' => 'Paybill account number is required for proper routing'
                    ], 400);
                }
                $accountReference = substr($accountReference, 0, 12); // M-Pesa limit
            } elseif ($mpesaAccount['type'] === 'till') {
                // FOR TILL: Can use order reference (not used for routing)
                $accountReference = $request->accountReference ?: 'ORDER_' . time();
                $accountReference = substr($accountReference, 0, 12);
            } else {
                return response()->json([
                    'success' => false,
                    'error' => 'Unsupported payment account type: ' . $mpesaAccount['type']
                ], 400);
            }
            
            // Step 4: Initiate payment with correct account reference
            $data = [
                'payment_account_id' => $mpesaAccount['id'],
                'amount' => $request->amount,
                'phone' => $phoneValidation['data']['normalized'],
                'account_reference' => $accountReference, // CRITICAL: Correct account reference
                'description' => $request->description ?: 'Payment via PayNexus',
                'remark' => 'Website Payment'
            ];
            
            $result = $this->paynexus->initiatePayment($data);
            
            return response()->json([
                'success' => $result['success'],
                'data' => $result['data'],
                'account_type' => $mpesaAccount['type'],
                'account_reference_used' => $accountReference,
                'message' => $result['success'] ? 'Payment initiated successfully' : 'Payment failed'
            ]);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'error' => 'Payment processing failed: ' . $e->getMessage()
            ], 500);
        }
    }
}
?>

🟢 JavaScript/Node.js Examples

Complete Node.js Integration (Updated with Proper Account Reference)

āš ļø Critical: Account reference usage depends on account type. Paybill accounts require merchant's account number for proper routing. Till accounts can use order references.

Payment Class - Complete Workflow

const https = require('https');

class PaynexusClient {
    constructor(secretKey) {
        this.baseUrl = 'https://paynexus.mcbankske.space/api';
        this.secretKey = secretKey;
    }
    
    async getPaymentAccounts() {
        return this.makeRequest('/merchant/payment-accounts', null, 'GET');
    }
    
    async validatePhone(phone) {
        const data = { phone: phone };
        return this.makeRequest('/mpesa/validate-phone', data);
    }
    
    async initiatePayment(paymentAccountId, amount, phone, accountReference, description = null) {
        const data = {
            payment_account_id: paymentAccountId,
            amount: amount,
            phone: phone,
            account_reference: accountReference, // CRITICAL: Must be set correctly
            description: description || 'Payment via PayNexus',
            remark: 'Website Payment'
        };
        
        return this.makeRequest('/mpesa/payment/initiate', data);
    }
    
    makeRequest(endpoint, data = null, method = 'POST') {
        return new Promise((resolve, reject) => {
            let postData = '';
            let path = '/api' + endpoint;
            
            if (method === 'POST' && data) {
                postData = JSON.stringify(data);
            }
            
            const options = {
                hostname: 'paynexus.mcbankske.space',
                path: path,
                method: method,
                headers: {
                    'Content-Type': 'application/json',
                    'X-API-Key': this.secretKey,
                    ...(method === 'POST' && postData ? { 'Content-Length': Buffer.byteLength(postData) } : {})
                }
            };
            
            const req = https.request(options, (res) => {
                let responseData = '';
                res.on('data', (chunk) => responseData += chunk);
                res.on('end', () => {
                    try {
                        const jsonResponse = JSON.parse(responseData);
                        resolve({
                            success: res.statusCode === 200,
                            data: jsonResponse.data || jsonResponse
                        });
                    } catch (error) {
                        resolve({
                            success: false,
                            error: 'Invalid JSON response'
                        });
                    }
                });
            });
            
            req.on('error', reject);
            
            if (method === 'POST' && postData) {
                req.write(postData);
            }
            req.end();
        });
    }
}

// COMPLETE USAGE EXAMPLE WITH PROPER ACCOUNT REFERENCE HANDLING
async function processPayment() {
    const paynexus = new PaynexusClient('your_secret_api_key_here');
    
    try {
        // Step 1: Get payment accounts to determine account type
        const accountsResponse = await paynexus.getPaymentAccounts();
        if (!accountsResponse.success) {
            console.error('āŒ Failed to get payment accounts:', accountsResponse.error);
            return;
        }
        
        const mpesaAccount = accountsResponse.data.find(acc => acc.provider === 'mpesa');
        if (!mpesaAccount) {
            console.error('āŒ No M-Pesa payment account found');
            return;
        }
        
        // Step 2: Validate phone number
        const phoneValidation = await paynexus.validatePhone('0798808796');
        if (!phoneValidation.success || !phoneValidation.data.valid) {
            console.error('āŒ Invalid phone number:', phoneValidation.data.error);
            return;
        }
        const normalizedPhone = phoneValidation.data.normalized;
        
        // Step 3: CRITICAL - Determine account reference based on account type
        let accountReference;
        if (mpesaAccount.type === 'paybill') {
            // FOR PAYBILL: Use merchant's account number for proper fund routing
            accountReference = mpesaAccount.account_number;
            if (!accountReference) {
                console.error('āŒ Paybill account number is required for proper routing');
                return;
            }
            accountReference = accountReference.substring(0, 12); // M-Pesa limit
            console.log('šŸ“Š Using Paybill account - Account Reference:', accountReference);
        } else if (mpesaAccount.type === 'till') {
            // FOR TILL: Can use order reference (not used for routing)
            accountReference = 'ORDER_' + Date.now();
            accountReference = accountReference.substring(0, 12);
            console.log('šŸ“Š Using Till account - Account Reference:', accountReference);
        } else {
            console.error('āŒ Unsupported payment account type:', mpesaAccount.type);
            return;
        }
        
        // Step 4: Initiate payment with correct account reference
        const payment = await paynexus.initiatePayment(
            mpesaAccount.id,     // Payment account ID
            100,                 // Amount in KES
            normalizedPhone,
            accountReference,   // CRITICAL: Correct account reference
            'Payment for website order'
        );
        
        if (payment.success) {
            console.log('āœ… Payment initiated successfully!');
            console.log('šŸ“ Checkout Request ID:', payment.data.checkout_request_id);
            console.log('šŸ’¬ Customer message:', payment.data.customer_message);
            console.log('šŸ“Š Account Type:', mpesaAccount.type);
            console.log('šŸ”‘ Account Reference Used:', accountReference);
            
            // Store checkout_request_id for status tracking
            // Show payment confirmation to user
        } else {
            console.error('āŒ Payment failed:', payment.data.error || 'Unknown error');
        }
        
    } catch (error) {
        console.error('āŒ Payment processing error:', error.message);
    }
}

// Execute the payment processing
processPayment();

Express.js Integration

Express Server

const express = require('express');
const axios = require('axios');

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

const PAYNEXUS_URL = 'https://paynexus.mcbankske.space/api';
const SECRET_KEY = 'your_secret_api_key_here';

app.post('/api/payment/initiate', async (req, res) => {
    try {
        const response = await axios.post(
            `${PAYNEXUS_URL}/mpesa/payment/initiate`,
            {
                payment_account_id: 1,
                amount: req.body.amount,
                phone: req.body.phone,
                account_reference: req.body.accountReference,
                description: 'Payment',
                remark: 'Payment'
            },
            {
                headers: {
                    'Content-Type': 'application/json',
                    'X-API-Key': SECRET_KEY
                }
            }
        );
        
        res.json({
            success: true,
            transactionId: response.data.transaction_id,
            message: 'Payment initiated'
        });
    } catch (error) {
        res.status(400).json({
            success: false,
            error: error.response?.data || error.message
        });
    }
});

// Webhook endpoint
app.post('/webhook/mpesa', (req, res) => {
    const payload = req.body;
    console.log('Webhook received:', payload);
    
    if (payload.ResultCode === 0) {
        // Payment successful - update database
        console.log(`Payment successful: ${payload.TransactionID}`);
    }
    
    res.json({ ResultCode: 0, ResultDesc: 'Success' });
});

app.listen(3000);

šŸ Python Examples

Basic Python

Payment Class

import requests
import json

class PaynexusMpesa:
    def __init__(self, secret_key):
        self.base_url = 'https://paynexus.mcbankske.space/api'
        self.secret_key = secret_key
        self.headers = {
            'Content-Type': 'application/json',
            'X-API-Key': self.secret_key
        }
    
    def initiate_payment(self, phone, amount, account_reference):
        data = {
            'payment_account_id': 1,
            'amount': amount,
            'phone': phone,
            'account_reference': account_reference,
            'description': 'Payment',
            'remark': 'Payment'
        }
        
        response = requests.post(
            f"{self.base_url}/mpesa/payment/initiate",
            headers=self.headers,
            json=data
        )
        
        return {
            'success': response.status_code == 200,
            'data': response.json()
        }

# Usage
mpesa = PaynexusMpesa('your_secret_api_key_here')
result = mpesa.initiate_payment('254712345678', 100, 'ORDER123')

if result['success']:
    print(f"Payment initiated: {result['data']['transaction_id']}")
else:
    print(f"Payment failed: {result['data']}")

Flask Integration

Flask App

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

PAYNEXUS_URL = 'https://paynexus.mcbankske.space/api'
SECRET_KEY = 'your_secret_api_key_here'

@app.route('/api/payment/initiate', methods=['POST'])
def initiate_payment():
    data = request.get_json()
    
    response = requests.post(
        f"{PAYNEXUS_URL}/mpesa/payment/initiate",
        headers={
            'Content-Type': 'application/json',
            'X-API-Key': SECRET_KEY
        },
        json={
            'payment_account_id': 1,
            'amount': data['amount'],
            'phone': data['phone'],
            'account_reference': data['accountReference'],
            'description': 'Payment',
            'remark': 'Payment'
        }
    )
    
    if response.status_code == 200:
        return jsonify({
            'success': True,
            'transactionId': response.json()['transaction_id']
        })
    
    return jsonify({
        'success': False,
        'error': response.json()
    }), 400

@app.route('/webhook/mpesa', methods=['POST'])
def mpesa_webhook():
    payload = request.get_json()
    print(f"Webhook received: {payload}")
    
    if payload.get('ResultCode') == 0:
        # Payment successful
        print(f"Payment successful: {payload.get('TransactionID')}")
    
    return jsonify({'ResultCode': 0, 'ResultDesc': 'Success'})

if __name__ == '__main__':
    app.run(debug=True)

šŸ“Ÿ cURL Examples

Complete API Workflow (Updated with Proper Account Reference)

āš ļø Critical: Account reference usage depends on account type. Paybill accounts require merchant's account number for proper routing. Till accounts can use order references.

API Test Script - Complete Workflow

#!/bin/bash

BASE_URL="https://paynexus.mcbankske.space/api"
SECRET_KEY="your_secret_api_key_here"
PUBLIC_KEY="your_public_api_key_here"

echo "šŸ” Step 1: Health check"
curl -X GET "${BASE_URL}/health" \
  -H "Content-Type: application/json"

echo -e "\nšŸ“Š Step 2: Get merchant info"
curl -X GET "${BASE_URL}/merchant" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ${PUBLIC_KEY}"

echo -e "\nšŸ’³ Step 3: Get payment accounts (CRITICAL for account reference)"
ACCOUNTS_RESPONSE=$(curl -s -X GET "${BASE_URL}/merchant/payment-accounts" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ${PUBLIC_KEY}")

echo "Payment accounts response:"
echo "$ACCOUNTS_RESPONSE" | jq '.'

# Extract account type and account number (requires jq)
ACCOUNT_TYPE=$(echo "$ACCOUNTS_RESPONSE" | jq -r '.data[] | select(.provider=="mpesa") | .type')
ACCOUNT_NUMBER=$(echo "$ACCOUNTS_RESPONSE" | jq -r '.data[] | select(.provider=="mpesa") | .account_number')

echo -e "\nšŸ“Š Account Type: $ACCOUNT_TYPE"
echo "šŸ“Š Account Number: $ACCOUNT_NUMBER"

echo -e "\nšŸ“ž Step 4: Validate phone number"
curl -X POST "${BASE_URL}/mpesa/validate-phone" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ${PUBLIC_KEY}" \
  -d '{"phone": "254712345678"}'

echo -e "\nšŸ’° Step 5: Initiate payment with CORRECT account reference"

# CRITICAL: Set account reference based on account type
if [ "$ACCOUNT_TYPE" = "paybill" ]; then
    # FOR PAYBILL: Use merchant's account number for proper fund routing
    ACCOUNT_REFERENCE="$ACCOUNT_NUMBER"
    echo "šŸ”‘ Using Paybill account - Account Reference: $ACCOUNT_REFERENCE"
elif [ "$ACCOUNT_TYPE" = "till" ]; then
    # FOR TILL: Can use order reference (not used for routing)
    ACCOUNT_REFERENCE="ORDER_$(date +%s)"
    echo "šŸ”‘ Using Till account - Account Reference: $ACCOUNT_REFERENCE"
else
    echo "āŒ Unknown account type: $ACCOUNT_TYPE"
    exit 1
fi

# Ensure account reference is 12 characters or less
ACCOUNT_REFERENCE=$(echo "$ACCOUNT_REFERENCE" | cut -c1-12)
echo "šŸ“ Final Account Reference (12 chars max): $ACCOUNT_REFERENCE"

curl -X POST "${BASE_URL}/mpesa/payment/initiate" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ${SECRET_KEY}" \
  -d "{
    \"payment_account_id\": 1,
    \"amount\": 100,
    \"phone\": \"254712345678\",
    \"account_reference\": \"$ACCOUNT_REFERENCE\",
    \"description\": \"Payment for website order\",
    \"remark\": \"Website Payment\"
  }"

echo -e "\nšŸ“Š Step 6: Check payment status (replace with actual checkout_request_id)"
curl -X POST "${BASE_URL}/mpesa/payment/status" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ${SECRET_KEY}" \
  -d '{"transaction_id": "YOUR_CHECKOUT_REQUEST_ID"}'

echo -e "\nāœ… Workflow complete!"
echo "šŸ“ Remember to store the checkout_request_id for status tracking"
echo "šŸ”‘ Account reference used: $ACCOUNT_REFERENCE (Account Type: $ACCOUNT_TYPE)"

šŸš€ API v2 Enhanced Examples

Batch Payment Processing

PHP - Batch Payments

<?php
class PaynexusBatch {
    private $baseUrl = 'https://paynexus.mcbankske.space/api/v2';
    private $secretKey = 'your_secret_api_key_here';
    
    public function initiateBatchPayments(array $payments) {
        $data = [
            'payments' => array_map(function($payment) {
                return [
                    'payment_account_id' => $payment['account_id'],
                    'amount' => $payment['amount'],
                    'phone' => $payment['phone'],
                    'account_reference' => $payment['reference'],
                    'description' => $payment['description'] ?? 'Batch payment',
                ];
            }, $payments)
        ];
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->baseUrl . '/payments/batch');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'X-API-Key: ' . $this->secretKey,
            'API-Version: v2'
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        return [
            'success' => $httpCode === 200,
            'data' => json_decode($response, true),
            'http_code' => $httpCode
        ];
    }
}

// Usage - Process 10 payments at once
$batch = new PaynexusBatch();
$payments = [
    ['account_id' => 1, 'amount' => 100, 'phone' => '254712345678', 'reference' => 'ORD001'],
    ['account_id' => 1, 'amount' => 200, 'phone' => '254712345679', 'reference' => 'ORD002'],
    // ... more payments
];

$result = $batch->initiateBatchPayments($payments);

if ($result['success']) {
    echo "Batch initiated! Batch ID: " . $result['data']['batch_id'];
    echo "Payments processed: " . count($result['data']['payments']);
} else {
    echo "Batch failed: " . $result['data']['message'];
}
?>

Analytics Dashboard Integration

JavaScript - Real-time Analytics

class PaynexusAnalytics {
    constructor(secretKey) {
        this.baseUrl = 'https://paynexus.mcbankske.space/api/v2';
        this.secretKey = secretKey;
    }
    
    async getPaymentAnalytics(days = 30) {
        const response = await fetch(`${this.baseUrl}/analytics/payments?days=${days}`, {
            headers: {
                'Content-Type': 'application/json',
                'X-API-Key': this.secretKey,
                'API-Version': 'v2'
            }
        });
        
        return response.json();
    }
    
    async getRevenueAnalytics(days = 30) {
        const response = await fetch(`${this.baseUrl}/analytics/revenue?days=${days}`, {
            headers: {
                'Content-Type': 'application/json',
                'X-API-Key': this.secretKey,
                'API-Version': 'v2'
            }
        });
        
        return response.json();
    }
    
    async getPerformanceMetrics() {
        const response = await fetch(`${this.baseUrl}/analytics/performance`, {
            headers: {
                'Content-Type': 'application/json',
                'X-API-Key': this.secretKey,
                'API-Version': 'v2'
            }
        });
        
        return response.json();
    }
    
    // Real-time dashboard update
    async updateDashboard() {
        try {
            const [payments, revenue, performance] = await Promise.all([
                this.getPaymentAnalytics(7),
                this.getRevenueAnalytics(7),
                this.getPerformanceMetrics()
            ]);
            
            // Update dashboard UI
            this.updateCharts(payments);
            this.updateRevenueCards(revenue);
            this.updatePerformanceMetrics(performance);
            
        } catch (error) {
            console.error('Analytics update failed:', error);
        }
    }
    
    updateCharts(data) {
        // Update payment charts with data
        const chartData = data.daily_stats.map(day => ({
            date: day.date,
            revenue: day.revenue,
            payments: day.total_payments,
            success_rate: day.success_rate
        }));
        
        // Update your chart library (Chart.js, D3.js, etc.)
        this.renderPaymentChart(chartData);
    }
    
    updateRevenueCards(data) {
        document.getElementById('total-revenue').textContent = 
            `KES ${data.total_revenue.toLocaleString()}`;
        document.getElementById('avg-revenue').textContent = 
            `KES ${data.daily_average_revenue.toLocaleString()}`;
        document.getElementById('growth-rate').textContent = 
            `${data.revenue_growth}%`;
    }
    
    updatePerformanceMetrics(data) {
        document.getElementById('avg-response-time').textContent = 
            `${data.api_performance.avg_response_time}ms`;
        document.getElementById('success-rate').textContent = 
            `${data.api_performance.error_rate}%`;
        document.getElementById('cache-hit-rate').textContent = 
            `${data.cache_performance.hit_rate}%`;
    }
}

// Initialize analytics dashboard
const analytics = new PaynexusAnalytics('your_secret_api_key_here');

// Update dashboard every 30 seconds
setInterval(() => analytics.updateDashboard(), 30000);

// Initial load
analytics.updateDashboard();

Webhook Testing & Retry Management

Python - Webhook Testing

import requests
import json
from flask import Flask, request, jsonify

class PaynexusWebhookManager:
    def __init__(self, secret_key):
        self.base_url = 'https://paynexus.mcbankske.space/api/v2'
        self.secret_key = secret_key
        self.headers = {
            'Content-Type': 'application/json',
            'X-API-Key': self.secret_key,
            'API-Version': 'v2'
        }
    
    def test_webhook(self, webhook_url, test_payload=None):
        """Test webhook endpoint with sample data"""
        if test_payload is None:
            test_payload = {
                "ResultCode": 0,
                "ResultDesc": "Success",
                "TransactionID": "TEST123456",
                "TransactionAmount": 1000,
                "PhoneNumber": "+254712345678",
                "MerchantRequestID": "TEST789"
            }
        
        response = requests.post(
            f"{self.base_url}/webhooks/test",
            headers=self.headers,
            json={
                "webhook_url": webhook_url,
                "test_payload": test_payload
            }
        )
        
        return {
            'success': response.status_code == 200,
            'data': response.json()
        }
    
    def retry_failed_webhook(self, webhook_id):
        """Retry a failed webhook delivery"""
        response = requests.post(
            f"{self.base_url}/webhooks/{webhook_id}/retry",
            headers=self.headers
        )
        
        return {
            'success': response.status_code == 200,
            'data': response.json()
        }
    
    def get_webhook_status(self, webhook_id):
        """Get webhook delivery status"""
        response = requests.get(
            f"{self.base_url}/webhooks/{webhook_id}/status",
            headers=self.headers
        )
        
        return {
            'success': response.status_code == 200,
            'data': response.json()
        }

# Flask webhook handler with signature verification
app = Flask(__name__)
webhook_manager = PaynexusWebhookManager('your_secret_api_key_here')

@app.route('/webhook/mpesa', methods=['POST'])
def mpesa_webhook():
    # Verify webhook signature
    signature = request.headers.get('X-PayNexus-Signature', '')
    payload = request.get_json()
    
    if not verify_webhook_signature(payload, signature):
        return jsonify({'error': 'Invalid signature'}), 401
    
    # Process webhook payload
    if payload.get('ResultCode') == 0:
        # Payment successful
        transaction_id = payload.get('TransactionID')
        amount = payload.get('TransactionAmount')
        phone = payload.get('PhoneNumber')
        
        # Update your database
        update_payment_status(transaction_id, 'completed', amount, phone)
        
        # Send real-time notification to frontend
        send_websocket_notification({
            'type': 'payment_completed',
            'transaction_id': transaction_id,
            'amount': amount,
            'phone': phone
        })
        
        print(f"Payment successful: {transaction_id} - KES {amount}")
    else:
        # Payment failed
        transaction_id = payload.get('TransactionID')
        error_desc = payload.get('ResultDesc', 'Unknown error')
        
        update_payment_status(transaction_id, 'failed', None, None, error_desc)
        print(f"Payment failed: {transaction_id} - {error_desc}")
    
    return jsonify({'ResultCode': 0, 'ResultDesc': 'Success'})

def verify_webhook_signature(payload, signature):
    """Verify webhook signature for security"""
    secret = 'your_webhook_secret'
    expected_signature = hash_hmac('sha256', json.dumps(payload), secret)
    return hash_equals(signature, expected_signature)

def update_payment_status(transaction_id, status, amount=None, phone=None, error=None):
    """Update payment status in database"""
    # Your database update logic here
    pass

def send_websocket_notification(data):
    """Send real-time notification to frontend"""
    # Your WebSocket notification logic here
    pass

if __name__ == '__main__':
    app.run(debug=True)

šŸ”§ Common Integration Patterns

Error Handling

try {
    const result = await mpesa.initiatePayment(phone, amount, ref);
    if (result.success) {
        // Handle success
        console.log('Payment successful:', result.data.transaction_id);
    } else {
        // Handle API error
        console.error('API Error:', result.data.message);
    }
} catch (error) {
    // Handle network error
    console.error('Network Error:', error.message);
}
try:
    result = mpesa.initiate_payment(phone, amount, ref)
    if result['success']:
        # Handle success
        print(f"Payment successful: {result['data']['transaction_id']}")
    else:
        # Handle API error
        print(f"API Error: {result['data']['message']}")
except requests.exceptions.RequestException as e:
    # Handle network error
    print(f"Network Error: {e}")

Webhook Security

Signature Verification (PHP)

<?php
function verifyWebhookSignature($payload, $signature) {
    $secret = 'your_webhook_secret';
    $expectedSignature = hash_hmac('sha256', json_encode($payload), $secret);
    return hash_equals($signature, $expectedSignature);
}

// In your webhook handler
$signature = $_SERVER['HTTP_X_PAYNEXUS_SIGNATURE'] ?? '';
$payload = json_decode(file_get_contents('php://input'), true);

if (!verifyWebhookSignature($payload, $signature)) {
    http_response_code(401);
    echo json_encode(['error' => 'Unauthorized']);
    exit;
}
?>

Payment Status Polling

Reliable Status Checking

// Poll payment status
async function pollPaymentStatus(transactionId, maxAttempts = 10) {
    for (let i = 0; i < maxAttempts; i++) {
        const status = await mpesa.checkPaymentStatus(transactionId);
        
        if (status.completed) {
            return status;
        }
        
        // Wait 5 seconds before next check
        await new Promise(resolve => setTimeout(resolve, 5000));
    }
    
    throw new Error('Payment status check timeout');
}