Shadow Payment Gateway API

Comprehensive documentation for integrating with our payment system

Introduction

The Shadow Payment Gateway 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://shadow-pay.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 API, you need to:

  1. Create an account on the Shadow 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://shadow-pay.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
    }
}

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://shadow-pay.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 STK Push Demo
// =========================
// - Users enter phone + amount in a form
// - System sends STK Push using Shadow API
// - Tracks payment automatically in real-time
// =========================

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

// ==== FUNCTIONS ====

// Function to send STK Push
function initiatePayment($apiKey, $apiSecret, $accountId, $phone, $amount, $reference, $description) {
    $url = "https://shadow-pay.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://shadow-pay.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 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 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 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: