Mobile Money Charge
This guide explains the end-to-end flow to charge a customer via mobile money, verify the transaction status, and handle webhook notifications.
1 - Initiate Charge
Field | Type | Required | Description |
---|---|---|---|
amount | Number | ✅ | Amount to be charged (e.g. 1500 ) |
currency | String | ✅ | Currency code (e.g. KES ) |
externalReference | String | ✅ | Your own unique ID for reconciliation (e.g. invoice number) |
phoneNumber | String | ✅ | Recipient phone number in E.164 without the plus (e.g. 254719624551 ) |
momoOperatorId | String | ❌ | Mobile-money operator ID (required for all currencies except KES ) |
walletCurrency | String | ❌ | Currency to settle funds into (defaults to the value of currency ) |
Note: If
currency
is notKES
, you must includemomoOperatorId
.
To charge a customer, collect the required payment information and send it to the initiate mobile money charge endpoint.
To retrieve the mobile momoOperatorId, call the get mobile money providers endpoint.
Example Request:
{
"amount": 100,
"currency": "KES",
"externalReference": "order_12345",
"phoneNumber": "254719624551"
}
{
"amount": 100,
"currency": "KES",
"externalReference": "order_12345",
"phoneNumber": "254719624551",
"momoOperatorId": "airtel"
}
Below is an example of the response:
{
"success": true,
"message": "Initiated request.",
"transactionId": "123456789"
}
Note:Use the returned transactionId (or your externalReference) to track status.
2 - Get Transaction Status
Always verify the payment status before providing value to your customer. Use the get transaction endpoint with either:
- The transactionId from the charge response or
- Your externalReference
Here's an example of charge verification and response:
{
"success": true,
"data": {
"transactionId": 'lBK9bMny2gs4hLsG3XGq',
"amount": 25,
"type": 'deposit',
"currency": 'KES',
"senderCurrency": 'KES',
"senderAmount": 25,
"receiverCurrency": 'KES',
"receiverAmount": 25,
"chargeStatus": 'successful',
"status": 'SUCCESSFUL',
"method": 'momo',
"note": 'TFH174NFSJ Confirmed. Ksh25.00 sent to Honeycoin 722223344 on 17/06/25 at 1:17 AM. Transaction cost, Ksh0.00.', // optional
"fullTimestamp": '2025-06-17T01:16:44+03:00',
"externalReference": "test",
"thirdPartyReference": 'TFH174NFSJ', // optional
"phoneNumber": '254722416788',
"stepRequired": "otp", // Only when a step is required it can be: otp/redirect.
"redirectUrl": "https://test.com"
}
}
{
"success": true,
"data": {
"transactionId": '173675873400000038',
"amount": 102,
"type": 'deposit',
"currency": 'KES',
"senderCurrency": 'KES',
"senderAmount": 102,
"receiverCurrency": 'KES',
"receiverAmount": 102,
"chargeStatus": 'failed',
"status": 'FAILED',
"method": 'mpesa',
"note": 'STK Prompt Time Out',
"fullTimestamp": '2025-01-14T12:13:59+00:00',
"externalReference": '173675873400000038',
"phoneNumber": '254700000001'
}
}
3 - Handle Webhooks
Configure webhooks to receive real-time transaction updates instead of polling the status endpoint.
Setup
- Configure your webhook URL in your dashboard account.
- Implement webhook endpoint security and validation.
- Handle the webhook notifications in your application
Here's a sample of the webhook response:
{
"event": "transaction_created",
"data": {
"transactionId": "BeOfXV1NVIcZlsSVeQAF",
"status": "pending",
"type": "deposit",
"externalReference": "test",
},
"timestamp": "2024-10-03T16:33:14.600Z"
}
{
"event": "transaction_updated",
"data": {
"transactionId": "BeOfXV1NVIcZlsSVeQAF",
"status": "successful",
"type": "deposit",
"externalReference": "test",
"method": "momo",
"thirdPartReference": "TEQTEYRU" // optional. e.g Mpesa reference
},
"timestamp": "ISO-8601 timestamp"
}
{
"event": "transaction_updated",
"data": {
"transactionId": "BeOfXV1NVIcZlsSVeQAF",
"status": "failed",
"type": "deposit",
"externalReference": "test",
"method": "momo",
"note": "User could not be reached by stk" // optional. e.g error message
},
"timestamp": "ISO-8601 timestamp"
}
{
"event": "transaction_updated",
"data": {
"transactionId": "BeOfXV1NVIcZlsSVeQAF",
"status": "pending",
"type": "deposit",
"externalReference": "test",
"method": "momo",
"stepRequired": "otp"
},
"timestamp": "ISO-8601 timestamp"
}
{
"event": "transaction_updated",
"data": {
"transactionId": "BeOfXV1NVIcZlsSVeQAF",
"status": "pending",
"type": "deposit",
"externalReference": "test",
"method": "momo",
"stepRequired": "redirect",
"redirectUrl": "https://test.com/"
},
"timestamp": "ISO-8601 timestamp"
}
- If
stepRequired
isotp
: Prompt the customer to enter the OTP they received on their phone. - If
stepRequired
is redirect: Redirect the customer to the provided redirectUrl to complete the payment.
4 - Validate OTP
If a webhook with stepRequired: 'otp'
is received, you need to collect the OTP from the user and send it to the validate otp endpoint to proceed with the transaction.
URL: /api/b2b/fiat/deposit/:transactionId/validate-otp
Field | Type | Required | Description |
---|---|---|---|
otp | String | ✅ | The one-time password provided by the user |
Example Response:
{
"success": true,
"message": "OTP validated successfully.",
"transactionId": "123456789"
}
Note: After a successful OTP validation, the transaction will continue processing in the background. You will receive a final SUCCESSFUL or FAILED webhook when the transaction is complete.
Updated 8 days ago