Integrate M-Pesa payments in minutes. STK Push, callbacks, and webhooks made simple. Secure, scalable, and developer-friendly.
// Initiate STK Push - Custom Integration
$payload = [
"client_id" => "codian_prd_xxxxxxxxxxxx", // Your client ID
"client_secret" => "codian_sk_prd_xxxxxxxxxxxxxxxxxxxxxxxxxxxx", // Your secret
"amount" => 100.00,
"phone" => "2547xxxxxx" // Customer phone number
];
$ch = curl_init('https://api.codian.co.ke/v1/payments/c2b/initiate/');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
if ($data['success']) {
// Store checkout ID to match with webhook callback
$checkout_id = $data['CheckoutRequestID'];
echo "STK Push sent. Checkout ID: " . $checkout_id;
// Wait for webhook confirmation
}
// Initiate STK Push with Fetch API
const paymentData = {
client_id: 'codian_prd_xxxxxxxxxxxx', // Your client ID
client_secret: 'codian_sk_prd_xxxxxxxxxxxxxxxxxxxxxxxxxxxx', // Your secret
amount: 100.00,
phone: '2547xxxxxx' // Customer phone number
};
fetch('https://api.codian.co.ke/v1/payments/c2b/initiate/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(paymentData)
})
.then(res => res.json())
.then(data => {
if (data.success) {
console.log('STK Push sent. Checkout ID:', data.CheckoutRequestID);
// Show prompt to user
alert('Please check your phone and enter PIN to complete payment');
// Store checkout ID to match with webhook
localStorage.setItem('checkout_id', data.CheckoutRequestID);
}
})
.catch(error => console.error('Payment failed:', error));
import requests
# Initiate STK Push
payload = {
"client_id": "codian_prd_xxxxxxxxxxxx", # Your client ID
"client_secret": "codian_sk_prd_xxxxxxxxxxxxxxxxxxxxxxxxxxxx", # Your secret
"amount": 100.00,
"phone": "2547xxxxxx" # Customer phone number
}
response = requests.post(
"https://api.codian.co.ke/v1/payments/c2b/initiate/",
json=payload,
headers={"Content-Type": "application/json"},
timeout=30
)
data = response.json()
if data.get("success"):
checkout_id = data['CheckoutRequestID']
print(f"STK Push initiated! Checkout ID: {checkout_id}")
print("Waiting for webhook callback...")
# Store checkout_id in database to match with callback
else:
print(f"Error: {data.get('error', 'Unknown error')}")
const axios = require('axios');
// Initiate STK Push
const paymentData = {
client_id: 'codian_prd_xxxxxxxxxxxx', // Your client ID
client_secret: 'codian_sk_prd_xxxxxxxxxxxxxxxxxxxxxxxxxxxx', // Your secret
amount: 100.00,
phone: '2547xxxxxx' // Customer phone number
};
async function initiatePayment() {
try {
const response = await axios.post(
'https://api.codian.co.ke/v1/payments/c2b/initiate/',
paymentData,
{ headers: { 'Content-Type': 'application/json' } }
);
if (response.data.success) {
console.log('STK Push sent. Checkout ID:', response.data.CheckoutRequestID);
// Store checkout ID for webhook matching
return response.data.CheckoutRequestID;
}
} catch (error) {
console.error('Payment initiation failed:', error.response?.data || error.message);
}
}
initiatePayment();
// callback.php - Webhook endpoint (must return HTTP 200)
$raw_data = file_get_contents('php://input');
$callback = json_decode($raw_data, true);
if (isset($callback['Body']['stkCallback'])) {
$stk = $callback['Body']['stkCallback'];
$checkout_id = $stk['CheckoutRequestID'];
$result_code = $stk['ResultCode'];
$result_desc = $stk['ResultDesc'];
// Log the callback for debugging
error_log("Callback received - CheckoutID: $checkout_id, ResultCode: $result_code");
if ($result_code == 0) {
// Success - extract metadata
$metadata = [];
foreach ($stk['CallbackMetadata']['Item'] as $item) {
$metadata[$item['Name']] = $item['Value'];
}
$receipt = $metadata['MpesaReceiptNumber']; // M-Pesa transaction ID
$amount = $metadata['Amount'];
$phone = $metadata['PhoneNumber'];
// Update database: set status to completed
// UPDATE payments SET status='completed', receipt='$receipt'
// WHERE checkout_id='$checkout_id'
http_response_code(200);
echo json_encode(['success' => true, 'message' => 'Callback processed']);
} else {
// Payment failed - handle accordingly
// UPDATE payments SET status='failed', result_desc='$result_desc'
// WHERE checkout_id='$checkout_id'
http_response_code(200); // Always return 200
echo json_encode(['success' => false, 'message' => $result_desc]);
}
} else {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid callback']);
}
{
"Body": {
"stkCallback": {
"MerchantRequestID": "xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"CheckoutRequestID": "ws_xx_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"CallbackMetadata": {
"Item": [
{
"Name": "Amount",
"Value": 100
},
{
"Name": "MpesaReceiptNumber",
"Value": "xxxxxxx" // M-Pesa transaction ID
},
{
"Name": "TransactionDate",
"Value": "2025-01-23 14:30:26"
},
{
"Name": "PhoneNumber",
"Value": 2547xxxxxx // Customer phone
}
]
}
}
}
}
// Result Codes:
// 0: Success
// 1032: Transaction failed - insufficient funds
// 1037: Transaction cancelled by user
// 1: Internal server error