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.
When To Use This
Use Mobile Money Charge when you already know the customer's mobile money phone number, currency, and operator. This is a direct API flow for mobile money collections where your application owns the payment form.
Use the Universal Payment SDK instead when you want Honeycoin to host the checkout page or let the customer choose from multiple payment methods.
Happy Path
- Initiate the charge with the amount, currency, phone number, and your
externalReference. - Customer approves the payment prompt or completes any required OTP or redirect step.
- Listen for the final webhook or query the transaction status.
- Fulfill the order only after
chargeStatusissuccessful.
1 - Initiate Charge
| Field | Type | Required | Description |
|---|---|---|---|
amount | Number | ✅ | Amount to be charged (e.g. 1500) |
currency | String | ✅ | ISO 4217 currency code (e.g. KES, UGX, BWP) |
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 code from the Mobile Money Operators guide. Required for all currencies except KES |
otpCode | String | ❌ | One-time payment code required by some operators before charge initiation |
voucherPin | String | ❌ | 16-character OTT voucher PIN (required only if currency is BWP and using OTT vouchers) |
walletCurrency | String | ❌ | Currency to settle funds into (defaults to the value of currency) |
successRedirectUrl | String | ❌ | URL to return the customer to after a successful provider-hosted redirect, where supported |
failureRedirectUrl | String | ❌ | URL to return the customer to after a failed provider-hosted redirect, where supported |
Note: If
currencyis notKES, you must includemomoOperatorId. Additionally, ifcurrencyisBWPand you are processing an OTT payment, you must include thevoucherPin.
Pre-charge OTPs: Some operators require the customer to generate a one-time payment code before you initiate the charge. Follow the operator-specific customer instructions for the market you are collecting in, then send the generated code as
otpCodein the charge request.
Redirect support:
successRedirectUrlandfailureRedirectUrlare relevant for redirect-capable mobile money rails, such as Wave in Senegal (XOF). Some mobile money operators complete through STK, OTP, USSD, or provider prompts and may ignore these fields. Always use webhooks or the get transaction endpoint to confirm the final status before fulfilling an order.
To charge a customer, collect the required payment information and send it to the initiate mobile money charge endpoint.
To choose the correct momoOperatorId, view the Mobile Money Operators guide and use the listed code for the customer's country and operator.
Example Request:
{
"amount": 100,
"currency": "KES",
"externalReference": "order_12345",
"phoneNumber": "254719624551"
}{
"amount": 100,
"currency": "KES",
"externalReference": "order_12345",
"phoneNumber": "254719624551",
"momoOperatorId": "airtel"
}{
"amount": 1000,
"currency": "XOF",
"externalReference": "order_wave_sn_12345",
"phoneNumber": "221771234567",
"momoOperatorId": "wave",
"successRedirectUrl": "https://merchant.example.com/payments/success",
"failureRedirectUrl": "https://merchant.example.com/payments/failed"
}{
"amount": 1000,
"currency": "XOF",
"externalReference": "order_otp_12345",
"phoneNumber": "00000000000",
"momoOperatorId": "operator-id",
"otpCode": "123456"
}{
"amount": 150,
"currency": "BWP",
"externalReference": "order_12345_bwp",
"phoneNumber": "26771234567",
"momoOperatorId": "ott-voucher",
"voucherPin": "1234567890123456"
}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 successful and failed transaction:
{
"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.",
"fullTimestamp": "2025-06-17T01:16:44+03:00",
"externalReference": "test",
"thirdPartyReference": "TFH174NFSJ",
"phoneNumber": "254722416788",
"stepRequired": "otp",
"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",
"thirdPartyReference": "TEQTEYRU"
},
"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"
},
"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
stepRequiredisotp: Prompt the customer to enter the OTP they received on their phone. - If
stepRequiredisredirect: 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.
This is different from a pre-charge otpCode. If an operator requires otpCode before charge initiation, follow the operator-specific customer instructions and include the generated code when creating the mobile money charge instead of using this endpoint.
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.
5 - Handle Redirects
When the stepRequired field in a transaction status response or webhook is redirect, you must forward the customer to the provided redirectUrl to authorise the payment.
Extract the URL: Get the redirectUrl from the webhook or the response from the Get Transaction Status endpoint.
Redirect the User: Redirect your customer to this URL. They will be taken to a secure, external page (like their bank's or mobile money provider's website) to approve the transaction.
If you supplied successRedirectUrl and failureRedirectUrl, the provider may return the customer to one of those URLs after the redirect flow. Treat that return as a customer navigation event, not final payment confirmation.
Await Final Status: After the user completes the authorization you should wait for the final webhook notification (transaction_updated with a status of successful or failed) to confirm the outcome before providing value to the customer.
