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

GET
/v3/wallet/
All endpoints require an API key. Manage keys at /api_keys.php.

Get Wallet Balances

GET
/v3/wallet/

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" }
GET
/v3/wallet/?balance=service_wallet_balance

Retrieve service wallet balance only

GET
/v3/wallet/?balance=payments_wallet_balance

Retrieve payments wallet balance only

Aliases supported: 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.

POST
/v3/wallet/
Payments Wallet Deposit
{ "action": "deposit", "wallet_type": "payments", "phone_number": "254798765432", "amount": 500 }
POST
/v3/wallet/
Service Wallet Top-up
{ "action": "topup", "wallet_type": "service", "phone_number": "254798765432", "amount": 1000 }
Smart Defaults & Override
  • If wallet_type is not specified:
    • action: "topup" → defaults to service wallet
    • action: "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_type for clarity in production!
Parameters
  • action: deposit or topup
  • wallet_type: payments or service (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

GET
/v3/wallet/status.php?reference=WD20240115103000001

Check the status of a wallet deposit transaction

Pending
{ "success": true, "data": { "reference_number": "...", "status": "pending", "amount": 500.00, "phone_number": "254...", "created_at": "...", "updated_at": "...", "stk_push_id": "...", "merchant_request_id": "..." }, "timestamp": "..." }
Completed
{ "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": "..." }
Failed
{ "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