Quick Start Guide

Get your M-Pesa payment integration up and running in minutes!

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

๐Ÿ“‹ Prerequisites

PayNexus merchant account
API keys (Public and Secret)
Web server (for webhooks)
Basic programming knowledge

๐Ÿ”‘ Step 1: Get Your API Keys

  1. Log into your PayNexus dashboard
  2. Navigate to API Keys section
  3. Create a new API key for your payment account:
    • Public Key (pk_...) - For read operations like phone validation
    • Secret Key (sk_...) - For write operations like payment initiation
  4. Each API key is linked to a specific payment account, ensuring payments go to the correct merchant
Security Note: Never expose your secret keys in client-side code! Use public keys for client-side operations.
Important: API keys are automatically associated with your merchant account and payment account. The system validates that you can only access your own payment accounts.

๐Ÿ’ป Step 2: Choose Your Language

๐Ÿ˜

PHP

Perfect for Laravel, WordPress, or custom PHP applications

๐ŸŸข

JavaScript/Node.js

Great for Express.js, React, or modern web applications

๐Ÿ

Python

Ideal for Django, Flask, or Python-based services

๐Ÿ“Ÿ

cURL

For testing and command-line integration

โšก Basic Integration

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

Here's how to make your first payment:

PHP Example - Complete Workflow

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class PaynexusClient {
    private $client;
    private $baseUrl;
    private $secretKey;
    
    public function __construct($secretKey) {
        $this->baseUrl = 'https://paynexus.mcbankske.space/api';
        $this->secretKey = $secretKey;
        $this->client = new Client([
            'base_uri' => $this->baseUrl,
            'headers' => [
                'X-API-Key' => $secretKey,
                'Content-Type' => 'application/json'
            ]
        ]);
    }
    
    public function getPaymentAccounts() {
        $response = $this->client->get('/merchant/payment-accounts');
        return json_decode($response->getBody(), true);
    }
    
    public function validatePhone($phone) {
        $response = $this->client->post('/mpesa/validate-phone', [
            'json' => ['phone' => $phone]
        ]);
        return json_decode($response->getBody(), true);
    }
    
    public function initiatePayment($paymentAccountId, $amount, $phone, $accountReference, $description = null) {
        $response = $this->client->post('/mpesa/payment/initiate', [
            'json' => [
                '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 json_decode($response->getBody(), true);
    }
}

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

try {
    // Step 1: Get payment accounts to determine account type
    $accountsResponse = $paynexus->getPaymentAccounts();
    if (!$accountsResponse['success']) {
        die('Failed to get payment accounts');
    }
    
    $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');
    }
    $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
    $result = $paynexus->initiatePayment(
        $mpesaAccount['id'],     // Payment account ID
        100,                 // Amount in KES
        $normalizedPhone,
        $accountReference,   // CRITICAL: Correct account reference
        'Payment for website order'
    );
    
    if ($result['success']) {
        echo "โœ… Payment initiated successfully!\n";
        echo "๐Ÿ“ Checkout Request ID: " . $result['data']['checkout_request_id'] . "\n";
        echo "๐Ÿ“Š Account Type: " . $mpesaAccount['type'] . "\n";
        echo "๐Ÿ”‘ Account Reference Used: $accountReference\n";
    } else {
        echo "โŒ Payment failed: " . ($result['error'] ?? 'Unknown error') . "\n";
    }
    
} catch (Exception $e) {
    echo "โŒ Error: " . $e->getMessage() . "\n";
}
?>

JavaScript Example - Complete Workflow

const axios = require('axios');

class PaynexusClient {
    constructor(secretKey) {
        this.client = axios.create({
            baseURL: 'https://paynexus.mcbankske.space/api',
            headers: { 'X-API-Key': secretKey }
        });
    }
    
    async getPaymentAccounts() {
        const response = await this.client.get('/merchant/payment-accounts');
        return response.data;
    }
    
    async validatePhone(phone) {
        const response = await this.client.post('/mpesa/validate-phone', { phone });
        return response.data;
    }
    
    async initiatePayment(paymentAccountId, amount, phone, accountReference, description = null) {
        const response = await this.client.post('/mpesa/payment/initiate', {
            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 response.data;
    }
}

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

const processPayment = async () => {
    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');
            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');
            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 result = await paynexus.initiatePayment(
            mpesaAccount.id,     // Payment account ID
            100,                 // Amount in KES
            normalizedPhone,
            accountReference,   // CRITICAL: Correct account reference
            'Payment for website order'
        );
        
        if (result.success) {
            console.log('โœ… Payment initiated successfully!');
            console.log('๐Ÿ“ Checkout Request ID:', result.data.checkout_request_id);
            console.log('๐Ÿ“Š Account Type:', mpesaAccount.type);
            console.log('๐Ÿ”‘ Account Reference Used:', accountReference);
        } else {
            console.error('โŒ Payment failed:', result.error || 'Unknown error');
        }
        
    } catch (error) {
        console.error('โŒ Error:', error.message);
    }
};

processPayment();

Python Example - Check Status

import requests

# Initialize session with your secret API key
session = requests.Session()
session.headers.update({'X-API-Key': 'sk_your_secret_key_here'})
base_url = 'https://paynexus.mcbankske.space/api'

def check_payment_status(payment_reference):
    """Check payment status using payment reference"""
    try:
        response = session.get(f'{base_url}/payments/{payment_reference}')
        result = response.json()
        
        if result['success']:
            payment = result['data']
            print(f"Payment Status: {payment['status']}")
            print(f"Amount: {payment['amount']} {payment['currency']}")
            print(f"Phone: {payment['phone']}")
            print(f"Created: {payment['created_at']}")
            if payment.get('provider_transaction_id'):
                print(f"MPESA Transaction ID: {payment['provider_transaction_id']}")
        else:
            print(f"Error: {result['message']}")
            
    except requests.exceptions.RequestException as error:
        print(f"Request failed: {error}")

def check_status_by_checkout_id(checkout_request_id):
    """Check payment status using checkout request ID"""
    try:
        response = session.post(f'{base_url}/payments/status-by-checkout-id', json={
            'checkout_request_id': checkout_request_id
        })
        result = response.json()
        
        if result['success']:
            payment = result['data']
            print(f"Payment Status: {payment['status']}")
            print(f"Reference: {payment['reference']}")
            print(f"Amount: {payment['amount']} {payment['currency']}")
        else:
            print(f"Error: {result['message']}")
            
    except requests.exceptions.RequestException as error:
        print(f"Request failed: {error}")

# Example usage
check_payment_status('ORDER_123')  # Use your actual payment reference
check_status_by_checkout_id('ws_CO_123456789')  # Use your actual checkout request ID

๐Ÿงช Step 4: Test Your Integration

Health Check

curl https://paynexus.mcbankske.space/api/health

Phone Validation

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"}'

Get Payment Accounts

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

Returns your available payment accounts with their IDs

Check Transaction Status (by Reference)

curl -X GET "http://127.0.0.1:8000/api/payments/YOUR_PAYMENT_REFERENCE" \
-H "X-API-Key: sk_your_secret_key_here"

Check payment status using the payment reference returned during initiation

Check Transaction Status (by Checkout ID)

curl -X POST "http://127.0.0.1:8000/api/payments/status-by-checkout-id" \
-H "X-API-Key: sk_your_secret_key_here" \
-H "Content-Type: application/json" \
-d '{"checkout_request_id": "YOUR_CHECKOUT_REQUEST_ID"}'

Check payment status using the checkout_request_id from MPESA response

MPESA Real-time Status Check

curl -X POST "http://127.0.0.1:8000/api/mpesa/payment/status" \
-H "X-API-Key: sk_your_secret_key_here" \
-H "Content-Type: application/json" \
-d '{"checkout_request_id": "YOUR_CHECKOUT_REQUEST_ID"}'

Real-time status check directly from MPESA (requires additional API calls)

๐Ÿ”— Step 5: Set Up Webhooks

Create a webhook endpoint to receive payment notifications:

Basic Webhook Handler (PHP)

<?php
// webhook.php
header('Content-Type: application/json');

$payload = json_decode(file_get_contents('php://input'), true);

if ($payload['ResultCode'] == 0) {
    // Payment successful
    $transactionId = $payload['TransactionID'];
    $amount = $payload['Amount'];
    
    // Update your database
    // Mark order as paid
    // Send confirmation email
    
    echo json_encode(['ResultCode' => 0, 'ResultDesc' => 'Success']);
} else {
    // Payment failed
    echo json_encode(['ResultCode' => 0, 'ResultDesc' => 'Received']);
}
?>

๐Ÿš€ Step 6: Go Live!

Use production API keys
Set up HTTPS for webhooks
Monitor your payments
Test with real M-Pesa transactions
Congratulations! ๐ŸŽ‰ You're ready to accept M-Pesa payments through PayNexus.

โ“ Common Issues & Solutions

Issue Solution
"Unauthorized" Check API key validity and permissions. Ensure you're using the correct key type (pk_ for read, sk_ for write operations)
"Payment not found or not accessible" Verify the payment reference/checkout ID belongs to your merchant account. Use GET /merchant/payment-accounts to get your valid IDs
"Transaction not found" Check if the checkout_request_id is correct and the payment was initiated successfully
"Status check failed" The MPESA service might be temporarily unavailable. Try again later or use the database status check instead
"Invalid phone number" Use phone validation first. Format should be 254XXXXXXXXX for Kenya numbers
"Merchant authentication required" Ensure your API key has merchant.auth middleware applied and your merchant account is approved
Webhook not working Ensure HTTPS and correct URL