M-Pesa STK/USSD Push initiate(Wallet Based Settlement)
This API allows merchants to initiate Mpesa STK/USSD pushes to Customers.
- Contrary to Account-based API, the funds are credited to Jenga Wallet where one can settle to any bank account on-boarded on Jenga.
- The funds are available for settlement on a real-time basis.
Test URL
Live URL
Interface Definition
| Interface ID | mpesaStkPush |
|---|---|
| Version | 1.0 |
| Interface Type | REST/JSON |
| Mode | Synchronous |
| Method | POST |
| Endpoint | /api-checkout/mpesa-stk-push/v3.0/init |
| Description | Initiate Mpesa STK Push |
Request Parameters
| Field | Description | Format | Required |
|---|---|---|---|
| order.orderReference | Order reference, should be unique per order | String | Y |
| order.orderAmount | Original order amount | Number | Y |
| order.orderCurrency | Currency of the order | String | Y |
| order.source | Source of the order, i.e, APICHECKOUT | String | Y |
| order.countryCode | Source country code i.e alpha-2 country code E.g KE | String | Y |
| order.description | Description of the order | String | Y |
| customer.name | Customer's name | String | Y |
| customer.email | Customer's email | String | Y |
| customer.phoneNumber | Customer's phone number | String | Y |
| customer.identityNumber | Customer's identity number | String | Y |
| customer.firstAddress | Customer's first address | String | N |
| customer.secondAddress | Customer's second address | String | N |
| payment.paymentReference | Payment reference, should be unique per request | String | Y |
| payment.paymentCurrency | Currency of the payment | String | Y |
| payment.channel | Channel of the payment, e.g., MOBILE | String | Y |
| payment.service | Payment service, i.e., MPESA | String | Y |
| payment.provider | Payment provider, i.e., JENGA | String | Y |
| payment.callbackUrl | Callback URL to notify merchant when the transaction is completed or failed | String | Y |
| payment.details.msisdn | Number to push the STK | String | Y |
| payment.details.paymentAmount | Amount to pay on the request | Number | Y |
order.orderReference+payment.paymentCurrency+payment.details.msisdn+payment.details.paymentAmountExample Request
POST /api-checkout/mpesa-stk-push/v3.0/init
Content-Type: application/json
Authorization: Bearer {{ access_token }}
Signature: {{ generated_signature }}
{
"order": {
"orderReference": "OR28922980077", //Order reference, should be unique per order
"orderAmount": 2, //Origininal order amount
"orderCurrency": "KES",
"source": "APICHECKOUT",
"countryCode": "KE",
"description": "Purchase"
},
"customer": {
"name": "John Doe",
"email": "xyx2@gmail.com",
"phoneNumber": "0722000111",
"identityNumber": "0000000",
"firstAddress": "",
"secondAddress": ""
},
"payment": {
"paymentReference": "MKQR28922980073", //Should be unique per request
"paymentCurrency": "KES",
"channel": "MOBILE",
"service": "MPESA",
"provider": "JENGA",
"callbackUrl": "https://webhook.site/5c74a733-1caa-4f10-b876-3df9c0d7453c",
"details": {
"msisdn": "0722000000", //Number to push the stk
"paymentAmount": 2 //Amount to pay on the request
}
}
}Response Parameters
| Field | Description | Format | Req |
|---|---|---|---|
| status | Response status | String | Y |
| code | Response code | Number | Y |
| message | Response message | String | Y |
| data | Response data | Object | Y |
Example Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": true,
"code": "0",
"message": "Success. Request accepted for processing",
"data": {
"amount": 2.0,
"charge": 1.0,
"paymentReference": "MKQR28922980073",
"invoiceNumber": "INVDBU",
"orderReference": "OR28922980077",
"amountDebited": 2.0
}
}| Http Response Code | Status Code | Description |
|---|---|---|
| 401 | 106401 | Not Authorized to access this Telco,Kindly contact support@finserve.africa |
| 200 | -1 | Transaction has been successfully acknowledged, await final transaction status on callback |
| 200 | 106201 | Failed to initiate push. Kindly try again after a few minutes or contact support@finserve.africa |
| 500 | 106500 | Error loading service |
| 500 | 106501 | Error mapping service |
Jenga Callbacks
- Once a payment is complete , you will receive a callback in this format if you provide a callback url on the request.
- We dont recommend making your final decision based on this callback, but this can help when troubleshooting, We recommend the Complete Callback Response listed below.
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": true,
"code": "0",
"message": "The service request is processed successfully.",
"transactionReference": "MKQR28922980073",
"data": {
"Body": {
"stkCallback": {
"MerchantRequestID": "0528-4964-bbde-4f38e64fe5f147817775",
"CheckoutRequestID": "ws_CO_0908202416223944722000000",
"ResultDesc": "The service request is processed successfully.",
"ResultCode": 0,
"CallbackMetadata": {
"Item": [
{
"Value": 2,
"Name": "Amount"
},
{
"Value": "SH90HU7FO2",
"Name": "MpesaReceiptNumber"
},
{
"Name": "Balance"
},
{
"Value": 20240809162247,
"Name": "TransactionDate"
},
{
"Value": 254722000000,
"Name": "PhoneNumber"
}
]
}
}
}
}
}
Complete Callback Response
- We recommend registering a callback URL on Jenga HQ under Settings > IPNs as this is a confirmation of payment been processed successfully both on Mpesa and Jenga.
- To find more about IPN callbacks navigate to IPN
HTTP/1.1 200 OK
Content-Type: application/json
{
"callbackType": "IPN",
"customer": {
"name": "John Doe",
"mobileNumber": "0722000000",
"reference": "OR28922980077"
},
"transaction": {
"date": "2024-08-09 16:22:50",
"reference": "SH90HU7FO2",
"paymentMode": "MPESA",
"amount": 2,
"currency": "KES",
"serviceCharge": 1,
"billNumber": "INVDBU",
"servedBy": "JENGA",
"additionalInfo": "MPESA",
"orderAmount": 2,
"orderCurrency": "KES",
"status": "SUCCESS",
"remarks": "ED990222"
},
"bank": {
"reference": "NONE",
"transactionType": "C",
"account": "NONE"
}
}Notifications
~ Mpesa
Mpesa payment confirmed. You have successfully completed your transaction of KES 5.00 to Spotify
Kenya on 13-06-2024 at 02:22:51 PM. Service charge, KES 0.00. Ref. SH90HU7FO2
Error Responses
400 Bad Request
Missing or invalid parameters in the request body.
{
"status": false,
"code": 400,
"message": "Invalid request parameters",
"error_code": "INVALID_REQUEST"
}401 Unauthorized
Invalid or expired access token.
{
"status": false,
"code": 401,
"message": "Invalid or expired access token",
"error_code": "UNAUTHORIZED"
}403 Forbidden
Valid credentials but invalid signature or insufficient permissions.
{
"status": false,
"code": 403,
"message": "Invalid signature or insufficient permissions",
"error_code": "FORBIDDEN"
}404 Not Found
Account not found or invalid account number.
{
"status": false,
"code": 404,
"message": "Source or destination account not found",
"error_code": "ACCOUNT_NOT_FOUND"
}Transaction Status Errors
| Response Status | Response Code | Response Message |
|---|---|---|
| false | 111102 | Transaction with the passed reference cannot be found |
📖 Step-by-Step Guide
Step 1: 🔑 Set Up Security Keys
Generate your private and public key pair and share your public key with Finserve. See the Security & Signatures Documentation (opens in a new tab) for detailed instructions.
Step 2: 🎫 Authenticate
Obtain an access token using the authentication endpoint. See the Authentication API documentation (opens in a new tab) for details.
Step 3: 📋 Prepare Transaction Details
Gather all required information.
Step 4: ✍️ Generate Signature
Create the signature string by concatenating in this exact order:
order.orderReference+payment.paymentCurrency+payment.details.msisdn+payment.details.paymentAmountSign this string using your private key, then Base64 encode the result.
Step 5: 📝 Set Up Headers
Include the following headers in your request:
Content-Type: application/jsonAuthorization: Bearer [your_access_token]Signature: [your_base64_encoded_signature]
Step 6: 🔧 Construct Request Body
Create a JSON object with all required fields following the structure shown in the example request.
Step 7: 🚀 Send POST Request
Make a POST request to the internal bank transfer endpoint with your headers and body.
🌍 Supported Countries & Currencies
| Country | Country Code | Common Currency Codes |
|---|---|---|
| Kenya | KE | KES |
| Uganda | UG | UGX |
| Tanzania | TZ | TZS |
| Rwanda | RW | RWF |
| South Sudan | SS | USD |
| DRC | DRC | USD |
Best Practices
-
** Security**
- Store your private key securely and never expose it in client-side code or version control
- Always use HTTPS for API requests
- Store access tokens securely
- Regenerate signatures for each request
-
** Signature Generation**
- Ensure exact string concatenation order:
order.orderReference+payment.paymentCurrency+payment.details.msisdn+payment.details.paymentAmount - Do not include spaces, separators, or special characters in the concatenated string
- Always Base64 encode the signature before including it in headers
- Verify the values in the signature match exactly with the request body values
- Ensure exact string concatenation order:
-
** Transaction Reference**
- Use unique reference numbers for each transaction
- Implement a reference generation system to avoid duplicates
- Store reference numbers for reconciliation and audit purposes
- Never reuse reference numbers, even for failed transactions
-
Amount Formatting
- Always use decimal format with two decimal places (e.g., "500.00")
- Pass amounts as strings, not numbers
- Ensure the amount is positive and within allowed limits
- Verify amount matches exactly in signature and request body
-
Error Handling
- Implement retry logic with exponential backoff for transient errors
- Log transaction attempts and responses for audit purposes
- Handle signature validation errors by regenerating the signature
-
Testing
- Always test with the UAT endpoint before using the live endpoint
- Use test account numbers provided in the documentation
- Verify signature generation with sample data first
- Test error scenarios to ensure proper handling
-
Data Validation
- Validate all account numbers match the expected format
- Verify country codes are valid and supported
- Ensure transfer dates are in the correct format (YYYY-MM-DD)
- Validate currency codes match the destination country
Troubleshooting
Invalid Signature Error (403)
If you receive a 403 error with "Invalid signature":
- Verify the concatenation order:
order.orderReference+payment.paymentCurrency+payment.details.msisdn+payment.details.paymentAmount - Ensure no spaces or separators are included in the concatenated string
- Check that the signature is Base64 encoded
- Verify your public key is correctly registered with us
- Ensure the values in the signature match exactly with the request body values
Common Signature Mistakes
- Using wrong concatenation order
- Adding spaces or separators between values
- Not Base64 encoding the final signature
- Values in signature don't match request body values
Support
For questions or issues with this API:
- Email: support@finserve.africa