SHADOW PAY API

Comprehensive documentation for integrating with our payment system

Introduction

The SHADOW PAY API allows you to integrate M-Pesa STK Push payments into your applications. This documentation provides details on how to configure your system, initiate payments, and track transactions in real-time.

Base URL

All API endpoints are relative to the base URL:

https://stk-push.top/api/v2/

Authentication

All API requests require authentication using API keys. Include these in the request headers:

Response Format

All API responses are returned in JSON format with a consistent structure:

{
    "success": true|false,
    "message": "Descriptive message",
    // Additional data fields depending on the endpoint
}

API Test Interface

Use this interface to test the SHADOW PAY API endpoints with your credentials.

Test results will appear here...

Getting Started

1. Obtain API Credentials

To use the SHADOW PAY API, you need to:

  1. Create an account on the SHADOW PAY platform
  2. Generate your API Key and Secret from the dashboard
  3. Set up at least one payment account

2. PHP Integration Example

Here's a basic PHP implementation to get you started:

<?php
// Initialize payment
function initiatePayment($apiKey, $apiSecret, $paymentAccountId, $phone, $amount, $reference, $description) {
    $url = "https://stk-push.top/api/v2/stkpush.php";
    
    $data = [
        'payment_account_id' => $paymentAccountId,
        'phone' => $phone,
        'amount' => $amount,
        'reference' => $reference,
        'description' => $description
    ];
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'X-API-Key: ' . $apiKey,
        'X-API-Secret: ' . $apiSecret,
        'Content-Type: application/json'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    return json_decode($response, true);
}

// Your API credentials
$apiKey = "your_api_key_here";
$apiSecret = "your_api_secret_here";
$paymentAccountId = 14; // Your payment account ID

// Example usage
$result = initiatePayment($apiKey, $apiSecret, $paymentAccountId, "254712345678", 100, "ORDER123", "Test payment");

if ($result['success']) {
    echo "STK push sent successfully. Checkout Request ID: " . $result['checkout_request_id'];
} else {
    echo "Error: " . $result['message'];
}
?>

API Endpoints

Initiate STK Push

This endpoint initiates an M-Pesa STK Push payment request.

POST /stkpush.php

Request Parameters

Parameter Type Required Description
payment_account_id Integer Yes Your payment account ID
phone String Yes Customer phone number (format: 254712345678)
amount Float Yes Payment amount (minimum 1 KES)
reference String No Your internal reference for the transaction
description String No Description of the payment

Example Request

{
    "payment_account_id": 17,
    "phone": "254712345678",
    "amount": 100,
    "reference": "ORDER_12345",
    "description": "Payment for order #12345"
}

Response

Success Response (200 OK)
{
    "success": true,
    "message": "STK push sent successfully",
    "checkout_request_id": "ws_CO_20230101120000_abc123def456",
    "merchant_request_id": "ws_MR_20230101120000_xyz789uvw012"
}
Error Response (4xx/5xx)
{
    "success": false,
    "message": "Error description"
}

Check Payment Status

This endpoint checks the status of a payment transaction.

POST /status.php

Request Parameters

Parameter Type Required Description
checkout_request_id String Yes The checkout request ID from the STK Push response

Example Request

{
    "checkout_request_id": "ws_CO_20230101120000_abc123def456"
}

Response

Success Response (200 OK)
{
    "success": true,
    "status": "completed",
    "amount": 100,
    "phone": "254712345678",
    "transaction_code": "ABC123DEF456",
    "created_at": "2023-01-01 12:00:00"
}

Status Values

  • pending - Payment initiated but not completed
  • completed - Payment successfully completed
  • failed - Payment failed or was cancelled

List Transactions

This endpoint retrieves a list of your transactions with pagination support.

GET /transactions.php?page=1&limit=10

Query Parameters

Parameter Type Required Description
page Integer No Page number (default: 1)
limit Integer No Number of items per page (max: 100, default: 10)

Example Request

GET /transactions.php?page=2&limit=20

Response

Success Response (200 OK)
{
    "success": true,
    "data": [
        {
            "id": 123,
            "amount": 100,
            "status": "completed",
            "phone": "254712345678",
            "transaction_code": "ABC123DEF456",
            "fee_amount": 1.5,
            "fee_deducted": true,
            "type": "API",
            "created_at": "2023-01-01 12:00:00",
            "completed_at": "2023-01-01 12:02:30"
        }
    ],
    "pagination": {
        "current_page": 2,
        "per_page": 20,
        "total_items": 150,
        "total_pages": 8
    }
}

B2C Wallet API: Automatic Withdrawal / Send Money

This endpoint is for developers who want to integrate automatic payouts into their own system. It deducts money from the user's B2C Wallet, not the normal Service Wallet. Admin must enable Withdrawal for that user first. For automatic Safaricom payment, admin must also enable global Auto B2C and user Auto B2C.

POST /api/v2/withdraw.php

Authentication Headers

X-API-Key: YOUR_API_KEY
X-API-Secret: YOUR_API_SECRET
Content-Type: application/json

Request Parameters

ParameterTypeRequiredDescription
amountFloatYesAmount to pay out from B2C Wallet.
phoneStringYesRecipient M-Pesa number. Accepted examples: 0712345678 or 254712345678.
referenceStringNoYour order, payout, employee, commission, refund, or settlement reference.
send_moneyBooleanNoUse true when paying another person/customer. Admin must enable Send Money for that user.
Important Wallet Rule
Service Wallet = used for platform/API transaction fees.
B2C Wallet = used only for withdrawals and Send Money.
The API below deducts from B2C Wallet only.

Example Request

{
    "amount": 250,
    "phone": "254712345678",
    "reference": "PAYOUT_ORDER_1001",
    "send_money": true
}

PHP Integration Example

<?php
function sendB2CPayout($apiKey, $apiSecret, $phone, $amount, $reference) {
    $url = "https://stk-push.top/api/v2/withdraw.php";
    $payload = [
        "phone" => $phone,
        "amount" => $amount,
        "reference" => $reference,
        "send_money" => true
    ];

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload),
        CURLOPT_HTTPHEADER => [
            "X-API-Key: " . $apiKey,
            "X-API-Secret: " . $apiSecret,
            "Content-Type: application/json"
        ]
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true);
}

$result = sendB2CPayout("YOUR_API_KEY", "YOUR_API_SECRET", "254712345678", 250, "ORDER_1001");
if (!empty($result["success"])) {
    echo "Payout submitted. Reference: " . $result["reference"];
} else {
    echo "B2C error: " . $result["message"];
}
?>

Success Response

200 OK
{
    "success": true,
    "message": "B2C withdrawal request created.",
    "withdrawal_id": 12,
    "reference": "WD202605290001ABCD1234",
    "status": "processing",
    "method": "b2c",
    "balance_after": 1250.00
}

Error Response Examples

400 / 403
{
    "success": false,
    "message": "Withdrawal is not enabled for this account. Contact admin."
}
Insufficient B2C Wallet
{
    "success": false,
    "message": "Insufficient B2C Wallet balance. Required: KES 255.00"
}

Status Meaning

  • pending - request saved and waiting for admin/manual approval.
  • processing - Safaricom B2C request has been sent and is waiting for callback.
  • completed - B2C payout completed successfully.
  • failed - B2C failed and the B2C Wallet is refunded automatically where possible.
  • declined - admin declined and refunded the B2C Wallet.

Real-time Transaction Tracking

To track transactions in real-time, implement a polling mechanism that checks the status of a payment at regular intervals until it's completed or failed.

PHP Implementation Example

<?php
// Function to check payment status
function checkPaymentStatus($apiKey, $apiSecret, $checkoutRequestId) {
    $url = "https://stk-push.top/api/v2/status.php";
    
    $data = ['checkout_request_id' => $checkoutRequestId];
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'X-API-Key: ' . $apiKey,
        'X-API-Secret: . $apiSecret,
        'Content-Type: application/json'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    return json_decode($response, true);
}

// Example usage after initiating payment
$checkoutRequestId = $result['checkout_request_id']; // From STK Push response

// Check status with polling (every 5 seconds for up to 2 minutes)
$maxAttempts = 24;
$attempt = 0;

while ($attempt < $maxAttempts) {
    $status = checkPaymentStatus($apiKey, $apiSecret, $checkoutRequestId);
    
    if ($status['success']) {
        if ($status['status'] === 'completed') {
            echo "Payment completed. Transaction Code: " . $status['transaction_code'];
            break;
        } elseif ($status['status'] === 'failed') {
            echo "Payment failed.";
            break;
        }
        // If still pending, continue polling
    }
    
    $attempt++;
    sleep(5); // Wait 5 seconds before next check
}

if ($attempt >= $maxAttempts) {
    echo "Payment status check timeout.";
}
?>

Error Handling

The API uses standard HTTP status codes to indicate success or failure:

Code Description
200 Success
400 Bad Request - Invalid parameters
401 Unauthorized - Invalid API credentials
404 Not Found - Resource not found
405 Method Not Allowed
500 Internal Server Error

Common Error Messages

Complete Test Code

Here's a complete PHP implementation that includes a user interface for testing the API:

<?php
// =========================
// SHADOW PAY STK Push Demo
// =========================
// - Users enter phone + amount in a form
// - System sends STK Push using SHADOW PAY API
// - Tracks payment automatically in real-time
// =========================

// ==== API CREDENTIALS ====
// (replace these with your real API keys)
$apiKey    = "7390a54c44bcb9cb692f6c861562ff6f3b424094bef993d3434e692b17e02c46";
$apiSecret = "882e5a38596b0fd6cfd8f9631592e4290ca4f441a2be5990ec34d778974985c4";
$accountId = 17;

// ==== FUNCTIONS ====

// Function to send STK Push
function initiatePayment($apiKey, $apiSecret, $accountId, $phone, $amount, $reference, $description) {
    $url = "https://stk-push.top/api/v2/stkpush.php";

    $data = [
        'payment_account_id' => $accountId,
        'phone'              => $phone,
        'amount'             => $amount,
        'reference'          => $reference,
        'description'        => $description
    ];

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'X-API-Key: '    . $apiKey,
        'X-API-Secret: ' . $apiSecret,
        'Content-Type: application/json'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true);
}

// Function to check payment status
function checkPaymentStatus($apiKey, $apiSecret, $checkoutRequestId) {
    $url = "https://stk-push.top/api/v2/status.php";

    $data = ['checkout_request_id' => $checkoutRequestId];

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'X-API-Key: '    . $apiKey,
        'X-API-Secret: ' . $apiSecret,
        'Content-Type: application/json'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true);
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>SHADOW PAY STK Push Demo</title>
    <style>
        body { font-family: Arial, sans-serif; background:#f4f4f4; }
        .container { max-width:500px; margin:50px auto; background:#fff; padding:20px; border-radius:10px; box-shadow:0 0 10px rgba(0,0,0,0.1); }
        h2 { text-align:center; color:#333; }
        label { font-weight:bold; display:block; margin-top:10px; }
        input[type="text"], input[type="number"] { width:100%; padding:10px; margin-top:5px; border:1px solid #ccc; border-radius:5px; }
        button { margin-top:15px; padding:12px; background:#28a745; color:white; border:none; border-radius:5px; width:100%; font-size:16px; cursor:pointer; }
        button:hover { background:#218838; }
        .status-box { margin-top:20px; padding:15px; border-radius:8px; background:#f9f9f9; font-family:monospace; }
        .success { color:green; }
        .error { color:red; }
        .pending { color:orange; }
    </style>
</head>
<body>
<div class="container">
    <h2>💳 SHADOW PAY STK Push</h2>
    <form method="POST">
        <label>Phone Number (format: 2547XXXXXXXX)</label>
        <input type="text" name="phone" required placeholder="e.g. 254712345678">

        <label>Amount (KES)</label>
        <input type="number" name="amount" required placeholder="e.g. 100">

        <button type="submit" name="pay">Send STK Push</button>
    </form>

<?php
// ==== WHEN USER SUBMITS FORM ====
if (isset($_POST['pay'])) {
    $phone  = $_POST['phone'];
    $amount = $_POST['amount'];
    $reference   = "ORDER" . rand(1000,9999); // unique ref
    $description = "Payment via SHADOW PAY API";

    echo "<div class='status-box'>";
    echo "<p>📡 Sending STK Push to <b>$phone</b> for <b>KES $amount</b>...</p>";

    $result = initiatePayment($apiKey, $apiSecret, $accountId, $phone, $amount, $reference, $description);

    if (!$result['success']) {
        echo "<p class='error'>❌ Error: " . $result['message'] . "</p>";
        echo "</div>";
    } else {
        $checkoutRequestId = $result['checkout_request_id'];
        echo "<p class='success'>✅ STK Push sent successfully!</p>";
        echo "<p>Checkout Request ID: <b>$checkoutRequestId</b></p>";
        echo "<hr><p>🔍 Tracking payment status...</p>";

        // Auto-poll status
        $maxAttempts = 24; // 2 minutes
        $attempt = 0;

        while ($attempt < $maxAttempts) {
            $status = checkPaymentStatus($apiKey, $apiSecret, $checkoutRequestId);

            if ($status['success']) {
                $paymentStatus = $status['status'];

                echo "<p class='pending'>⏳ Attempt " . ($attempt + 1) . ": Status = <b>$paymentStatus</b></p>";
                flush();

                if ($paymentStatus === "completed") {
                    echo "<p class='success'>🎉 Payment Completed!</p>";
                    echo "<p>Transaction Code: <b>" . $status['transaction_code'] . "</b></p>";
                    break;
                } elseif ($paymentStatus === "failed") {
                    echo "<p class='error'>❌ Payment Failed.</p>";
                    break;
                }
            } else {
                echo "<p class='error'>⚠️ API Error: " . $status['message'] . "</p>";
                break;
            }

            $attempt++;
            sleep(5);
        }

        if ($attempt >= $maxAttempts) {
            echo "<p class='error'>⌛ Payment status check timed out.</p>";
        }

        echo "</div>";
    }
}
?>
</div>
</body>
</html>

Support

If you need help integrating with our API, please contact our support team.

Safaricom B2C Automatic Withdrawal Documentation

The separate B2C guide explains how developers can integrate automatic withdrawal/send-money using the B2C Wallet, API key, webhook callback and Safaricom B2C processing.

Open B2C API Documentation