Wallet API
Query balances and accept customer top-ups to both Service and Payments wallets via M-Pesa STK Push.
Service Wallet
Used for service fees and platform operations
- Service fee payments
- Platform operations
- API usage fees
Payments Wallet
Used for customer payments and transactions
- Customer payments
- Transaction processing
- B2C transfers
Base Endpoint
/api_keys.php.Get Wallet Balances
Retrieve both service and payments wallet balances
{
"success": true,
"data": {
"user_id": 123,
"balances": {
"service_wallet_balance": 1250.00,
"payments_wallet_balance": 750.50
}
},
"timestamp": "2024-01-15T10:30:00+00:00"
}
Retrieve service wallet balance only
Retrieve payments wallet balance only
service, payments, payment
Initiate Wallet Top-up (STK)
Initiate an M-Pesa STK Push to add funds to either your Service Wallet or Payments Wallet.
Payments Wallet Deposit
{
"action": "deposit",
"wallet_type": "payments",
"phone_number": "254798765432",
"amount": 500
}
Service Wallet Top-up
{
"action": "topup",
"wallet_type": "service",
"phone_number": "254798765432",
"amount": 1000
}
Smart Defaults & Override
- If
wallet_typeis not specified:action: "topup"→ defaults to service walletaction: "deposit"→ defaults to payments wallet
- You can override the default by explicitly setting
wallet_type - Example:
{"action": "deposit", "wallet_type": "service"}deposits to service wallet - Always specify
wallet_typefor clarity in production!
Parameters
- action:
depositortopup - wallet_type:
paymentsorservice(optional) - phone_number: 07XXXXXXXX or 254XXXXXXXXX
- amount: 1 - 70000 KES
- user_callback_url (optional): Callback URL
Wallet Type Examples
// Default behavior
{"action": "deposit", "phone_number": "254798765432", "amount": 500}
// → Deposits to payments wallet
{"action": "topup", "phone_number": "254798765432", "amount": 1000}
// → Top-ups service wallet
// Explicit override
{"action": "deposit", "wallet_type": "service", "phone_number": "254798765432", "amount": 500}
// → Deposits to service wallet (overrides default)
{"action": "topup", "wallet_type": "payments", "phone_number": "254798765432", "amount": 1000}
// → Top-ups payments wallet (overrides default)
Success Response
{
"success": true,
"data": {
"message": "STK Push sent successfully",
"reference_number": "TOPUP-123-1642234567",
"amount": 1000,
"phone_number": "254798765432",
"transaction_id": 12345,
"checkout_request_id": "ws_CO_15012024103000123456",
"wallet_type": "service"
},
"timestamp": "2024-01-15T10:30:00+00:00"
}
Check Deposit Status
Check the status of a wallet deposit transaction
{
"success": true,
"data": {
"reference_number": "...",
"status": "pending",
"amount": 500.00,
"phone_number": "254...",
"created_at": "...",
"updated_at": "...",
"stk_push_id": "...",
"merchant_request_id": "..."
},
"timestamp": "..."
}
{
"success": true,
"data": {
"reference_number": "...",
"status": "completed",
"amount": 500.00,
"phone_number": "254...",
"created_at": "...",
"updated_at": "...",
"completed_at": "...",
"mpesa_receipt_number": "RKL1234567",
"new_balance": 1250.50
},
"timestamp": "..."
}
{
"success": true,
"data": {
"reference_number": "...",
"status": "failed",
"amount": 500.00,
"phone_number": "254...",
"created_at": "...",
"updated_at": "...",
"error_message": "Transaction cancelled by user",
"failed_at": "..."
},
"timestamp": "..."
}
Code Examples
Ready-to-use code examples for integrating with the enhanced Wallet API
# Get both wallet balances
curl -X GET "http://localhost/pay-app/v3/wallet/" \
-H "Authorization: Bearer YOUR_API_KEY_HERE"
# Get service wallet balance only
curl -X GET "http://localhost/pay-app/v3/wallet/?balance=service" \
-H "Authorization: Bearer YOUR_API_KEY_HERE"
# Get payments wallet balance only
curl -X GET "http://localhost/pay-app/v3/wallet/?balance=payments" \
-H "Authorization: Bearer YOUR_API_KEY_HERE"
# Top up service wallet (default behavior)
curl -X POST "http://localhost/pay-app/v3/wallet/" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"action": "topup",
"phone_number": "254798765432",
"amount": 1000
}'
# Deposit to payments wallet (default behavior)
curl -X POST "http://localhost/pay-app/v3/wallet/" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"action": "deposit",
"phone_number": "254798765432",
"amount": 500,
"user_callback_url": "https://yoursite.com/webhook"
}'
# Override: Deposit to service wallet (instead of payments)
curl -X POST "http://localhost/pay-app/v3/wallet/" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"action": "deposit",
"wallet_type": "service",
"phone_number": "254798765432",
"amount": 500
}'
# Override: Top up payments wallet (instead of service)
curl -X POST "http://localhost/pay-app/v3/wallet/" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"action": "topup",
"wallet_type": "payments",
"phone_number": "254798765432",
"amount": 1000
}'
# Check status
curl -X GET "http://localhost/pay-app/v3/wallet/status.php?reference=TOPUP-123-1642234567" \
-H "Authorization: Bearer YOUR_API_KEY_HERE"
const API_KEY = 'YOUR_API_KEY_HERE';
const BASE_URL = 'http://localhost/pay-app/v3/wallet/';
// Get both wallet balances
fetch(BASE_URL, {
method: 'GET',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
}
}).then(r => r.json()).then(console.log);
// Get specific wallet balance
async function getWalletBalance(walletType) {
const response = await fetch(`${BASE_URL}?balance=${walletType}`, {
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
}
});
return response.json();
}
// Top up service wallet (default behavior)
fetch(BASE_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'topup',
phone_number: '254798765432',
amount: 1000
})
}).then(r => r.json()).then(console.log);
// Deposit to payments wallet (default behavior)
fetch(BASE_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'deposit',
phone_number: '254798765432',
amount: 500,
user_callback_url: 'https://yoursite.com/webhook'
})
}).then(r => r.json()).then(console.log);
// Override: Deposit to service wallet
fetch(BASE_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'deposit',
wallet_type: 'service', // Override default
phone_number: '254798765432',
amount: 500
})
}).then(r => r.json()).then(console.log);
// Override: Top up payments wallet
fetch(BASE_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'topup',
wallet_type: 'payments', // Override default
phone_number: '254798765432',
amount: 1000
})
}).then(r => r.json()).then(console.log);
// Check deposit status
async function checkStatus(reference) {
const response = await fetch(
`${BASE_URL}status.php?reference=${reference}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
return response.json();
}
'topup',
'phone_number' => '254798765432',
'amount' => 1000
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($topupData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
// Deposit to payments wallet (default behavior)
$depositData = [
'action' => 'deposit',
'phone_number' => '254798765432',
'amount' => 500,
'user_callback_url' => 'https://yoursite.com/webhook'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($depositData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
// Override: Deposit to service wallet
$overrideDepositData = [
'action' => 'deposit',
'wallet_type' => 'service', // Override default
'phone_number' => '254798765432',
'amount' => 500
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($overrideDepositData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
// Check status
function checkDepositStatus($apiKey, $baseUrl, $reference) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseUrl . 'status.php?reference=' . urlencode($reference));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
?>
Testing & Troubleshooting
Use the built-in testing tools to verify your wallet integrations work correctly.
Built-in Wallet Tester
Interactive web interface for testing all wallet operations
- Test balance queries (both wallets)
- Test deposit/topup operations
- Test wallet type overrides
- Real-time response logging
Access: /v3/wallet/wallet_tester.html
Callback Testing
Simulate M-Pesa callbacks for testing
- Test successful payments
- Test failed/cancelled payments
- Verify wallet crediting
- Test callback forwarding
php test_callback.php --reference=REF --status=success
Common Issues
- Balance not updating: Check callback URL configuration and logs
- Wrong wallet credited: Verify wallet_type parameter and transaction metadata
- STK push not sent: Check API key validity and phone number format
- Callback not received: Ensure HTTPS and firewall configuration
Callback Notifications
Receive real-time notifications when wallet top-ups are completed by providing a user_callback_url.
Callback Payload
{
"success": true,
"transaction_id": 12345,
"status": "completed",
"checkout_request_id": "ws_CO_15012024103000123456",
"merchant_request_id": "1234-5678-9012",
"wallet_target": "service",
"amount": 1000.00,
"result": {
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"MpesaReceiptNumber": "RKL1234567",
"Phone": "254798765432"
},
"timestamp": "2024-01-15T10:35:00+00:00"
}
- wallet_target: "service" or "payments" - indicates which wallet was credited
- Callback timing: Sent immediately after M-Pesa confirms payment
- Retry logic: Failed callbacks are not automatically retried
- Security: Verify transaction_id against your records
Error Model
{
"success": false,
"error": "Error message description",
"code": 400,
"timestamp": "2024-01-15T10:30:00+00:00"
}
Common Codes
- 400 - Bad Request
- 401 - Unauthorized
- 404 - Not Found
- 405 - Method Not Allowed
- 500 - Internal Server Error
Validation Messages
- API key is required
- Invalid or inactive API key
- Missing required field: phone_number | amount | action
- Invalid phone number format. Use format: 0798765432 or 254798765432
- Amount must be between KES 1 and KES 70,000
- Invalid balance type. Use: service_wallet_balance, service, payments_wallet_balance, payments, or payment
- Invalid wallet_type. Use payments or service
- Invalid action. Supported actions: deposit, topup
- Invalid user_callback_url
- Deposit transaction not found