Version: 6.0 | Last Updated: July 2024
MOKO Afrika is a payment aggregator that connects merchants and telecommunications networks to enable secure, real-time transactions. It provides a flexible API for seamless payment integration, supporting various payment methods while ensuring security and scalability for businesses in sectors like e-commerce and financial services.
Welcome to the MOKO Afrika Payment Processing API documentation. This guide provides detailed information on how to interact with the MOKO Afrika payment switch for send money and payment transactions.
Communication with the MOKO Afrika processor API follows an asynchronous communication model. In this model, the initiator sends a request to the API, which returns a response that is not the final status of the transaction. The initial response is merely an acknowledgment indicating that the money transfer request has been received.
To obtain the final status of the transaction, the initiator must perform a check using an appropriate search action via the same API, but with a slightly different parameter format. This allows the initiator to retrieve the updated status of the transaction.
Additionally, final status notifications can also be sent to the initiator via a callback mechanism. When making a transaction request, you can specify a callback URL in the request parameters. Once the final status of the transaction is available, MOKO Afrika will send a notification to this callback URL with the transaction details, including the final status.
Client Application
MOKO Afrika API
Payment Processor
Mobile Money Platform / Bank
To interact with the MOKO Afrika API, you need to authenticate your requests using a token. This token is generated using your merchant ID and merchant secret.
To generate an authentication token for interacting with the processor API, you can make a request to the following endpoint:
{
"merchant_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"merchant_secrete": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
}
{
"merchant_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"merchant_secrete": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDk4OTMwODcsIm5iZiI6MTcwOTg5MzA4NywinanRpIjoiNmFlNTVjM2EtOGU4Zi00MTJiLTgxMDYtY2ZhMzE4MTliODUzIiwiZXhwIjoxNzA5ODkzMjA3LCJpZGVudGl0eSI6Imp3SGZqZG9wZW5jM3l0JFRiIiwiZnJlc2giOmZhbHNlLCJ0eXBlIjoiYWNjZXNzIn0.hjgCcpZ9gLZWJY9oZfV-fX2c-QYvPIuHU0zLeLk6-cg"
}
The authentication token has a limited validity period. Make sure to generate a new token if your previous one has expired. You should include this token in the Authorization header of your subsequent API requests.
By default, upon the creation of a merchant account, the merchant_secrete is sent to the merchant via email. However, this behavior can be changed as needed.
This request allows you to change the merchant_secrete associated with a specific merchant_id. Make sure to specify the correct merchant_id in the request to perform the modification correctly.
{
"merchant_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
}
{
"resultCode": 0,
"resultCodeDescription": "Processed",
"resultData": {
"merchant_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"merchant_secrete": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"updated_at": "2024-03-08 09:45:01.326874"
}
}
Keep your merchant secret secure and do not share it with unauthorized parties. If you suspect your secret has been compromised, generate a new one immediately using this endpoint.
MOKO Afrika supports various payment methods for transactions. Each payment method has a specific identifier that should be used in the method field of your API requests.
Operator | Payment Service | Method Identifier | Status |
---|---|---|---|
Airtel | Airtel Money | airtel | Available |
Orange | Orange Money | orange | Available |
Vodacom | M-Pesa | mpesa | Available |
Africell | Afrimoney | africell | Available |
For each payment method, make sure to use the correct method identifier in the method
field of your API requests. Using an incorrect or unsupported method will result in an error.
MOKO Afrika API allows you to perform two types of transactions: Deposit (C2B) and Payout (B2C). This section explains how to initiate these transactions and handle responses.
Developers can interact with the processor to either place a request for debit (C2B) or credit (B2C), or to request the status of a particular transaction.
Parameter | Type | Required | Description |
---|---|---|---|
merchant_id | string | Required | Your MOKO Afrika merchant ID |
merchant_secrete | string | Required | Your MOKO Afrika merchant secret key |
amount | string | Required | The transaction amount |
currency | string | Required | The currency of the transaction (CDF or USD) |
action | string | Required | The action to perform (debit or credit) |
customer_number | string | Required | The customer's phone number |
firstname | string | Required | The customer's first name |
lastname | string | Required | The customer's last name |
string | Required | The customer's email address | |
reference | string | Required | A unique reference for the transaction |
method | string | Required | The payment method to use (e.g., airtel, orange, mpesa) |
callback_url | string | Optional | The URL where MOKO Afrika will send transaction notifications |
To initiate a debit transaction (C2B), developers can send a request to the processor API with the necessary parameters.
{
"merchant_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"merchant_secrete": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"amount": "100",
"currency": "CDF",
"action": "debit",
"customer_number": "0972148867",
"firstname": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"lastname": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"e-mail": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"reference": "testfp09",
"method": "airtel",
"callback_url": ""
}
Similarly, developers can place a request for a credit transaction (B2C) by sending the required parameters to the processor API.
{
"merchant_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"merchant_secrete": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"amount": "100",
"currency": "CDF",
"action": "credit",
"customer_number": "0972148867",
"firstname": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"lastname": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"e-mail": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"reference": "testfp09",
"method": "airtel",
"callback_url": ""
}
Upon success, you will receive a response with the HTTP status 200 OK and details of the transaction performed.
{
"Amount": 100,
"Comment": "Transaction Received Successfully",
"Created_At": "2024-03-08 08:08:51.533410",
"Currency": "CDF",
"Customer_Number": "972148867",
"Reference": "testfp09",
"Status": "Success",
"Transaction_id": "PDABXkT03IfD08M9PfR24Ci4",
"Updated_At": "2024-03-08 08:08:51.533410"
}
In case of an error, you will receive a response with the corresponding HTTP status and a detailed message explaining the issue.
{
"Comment": "Customer number is incorrect, be sure to start with 243",
"Status": "Error",
"resultCode": 1,
"resultCodeDescription": "request not executed...",
"resultCodeError": 405,
"resultCodeErrorDescription": "Customer number is incorrect, be sure to start with 243..."
}
MOKO Afrika can send transaction notifications to the specified URL via a callback method. Here's an example of a callback payload:
{
"Action": "debit",
"Amount": 100.0,
"Comment": "Transaction Found",
"Currency": "CDF",
"Customer_Details": "972148867",
"Financial_Institution_id": "",
"Method": "airtel",
"PayDRC_Reference": "PD9pLLM03QZJ08X7fXM242x6",
"Reference": "testfp09",
"Status": "Success",
"Status_Description": "La reference de la transaction est invalide, veuillez reesayez ou contactez le service client au 1213",
"Trans_Status": "Failed",
"Trans_Status_Description": "La reference de la transaction est invalide, veuillez reesayez ou contactez le service client au 1213"
}
{
"Action": "credit",
"Amount": 100.0,
"Comment": "Transaction Found",
"Currency": "CDF",
"Customer_Details": "972148867",
"Financial_Institution_id": "CI240308.0908.C64554",
"Method": "airtel",
"PayDRC_Reference": "PDABXkT03IfD08M9PfR24Ci4",
"Reference": "testfp09",
"Status": "Success",
"Status_Description": "Trans.ID: CI240308.0908.C64554 vous avez envoye de 100.0000 CDF a 972148867.Votre solde est -606965937.9689CDF.Cout:1.1600CDF",
"Trans_Status": "Success",
"Trans_Status_Description": "Transaction successful"
}
MOKO Afrika ensures security and data integrity through multiple security measures when sending callbacks to your systems:
To guarantee the integrity of incoming data, MOKO Afrika uses an HMAC-SHA256 signature. This ensures that the message has not been altered and originates from a trusted source.
How it works:
To ensure that only the intended recipient can read the data, AES-256-CBC encryption is used.
How it works:
Even if an attacker intercepts the message, they cannot read it without the secret key.
MOKO Afrika requires that clients whitelist our IP addresses. This ensures that only authorized systems can send or receive callbacks.
Security Benefits:
Each payment notification sent by MOKO Afrika includes:
{
"data": "<ENCRYPTED_PAYLOAD>"
}
Your server must respond with either a success or an error message based on the validation of the request.
{
"status": "Callback received successfully",
"data": { ... }
}
Below are secure implementations in Python, PHP, and Node.js for processing the callback safely.
import json
import base64
import hashlib
import hmac
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from flask import Flask, request, jsonify
SECRET_KEY = b'xxxxxxxxxxxxxxxx' # 16-byte AES key
HMAC_KEY = b'xxxxxxxxxxxxxxxx'
app = Flask(__name__)
def decrypt_data(encrypted_data):
cipher = AES.new(SECRET_KEY, AES.MODE_CBC, iv=SECRET_KEY)
decrypted_bytes = unpad(cipher.decrypt(base64.b64decode(encrypted_data)), AES.block_size)
return json.loads(decrypted_bytes.decode())
def verify_signature(encrypted_message, received_signature):
calculated_signature = hmac.new(HMAC_KEY, encrypted_message.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(calculated_signature, received_signature)
@app.route('/callback', methods=['POST'])
def callback_handler():
data = request.json.get("data")
received_signature = request.headers.get("X-Signature")
if not received_signature:
return jsonify({"error": "Signature missing"}), 400
if verify_signature(data, received_signature):
try:
decrypted_data = decrypt_data(data)
return jsonify({"status": "Callback received", "data": decrypted_data}), 200
except Exception:
return jsonify({"error": "Invalid encryption"}), 400
else:
return jsonify({"error": "Invalid signature"}), 401
<?php
define('SECRET_KEY', 'xxxxxxxxxxxxxxxx');
define('HMAC_KEY', 'xxxxxxxxxxxxxxxx');
function decrypt_data($encrypted_data) {
$decoded_data = base64_decode($encrypted_data);
$iv = SECRET_KEY;
$decrypted = openssl_decrypt($decoded_data, 'AES-128-CBC', SECRET_KEY, OPENSSL_RAW_DATA, $iv);
return json_decode($decrypted, true);
}
function verify_signature($encrypted_message, $received_signature) {
$calculated_signature = hash_hmac('sha256', $encrypted_message, HMAC_KEY);
return hash_equals($calculated_signature, $received_signature);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true)['data'];
$received_signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
if (!$received_signature) {
echo json_encode(["error" => "Signature missing"]);
http_response_code(400);
exit;
}
if (verify_signature($data, $received_signature)) {
try {
$decrypted_data = decrypt_data($data);
echo json_encode(["status" => "Callback received", "data" => $decrypted_data]);
http_response_code(200);
} catch (Exception $e) {
echo json_encode(["error" => "Invalid encryption"]);
http_response_code(400);
}
} else {
echo json_encode(["error" => "Invalid signature"]);
http_response_code(401);
}
}
?>
const express = require('express');
const crypto = require('crypto');
const base64 = require('base-64');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const SECRET_KEY = Buffer.from('xxxxxxxxxxxxxxxx'); // 16-byte AES key
const HMAC_KEY = 'xxxxxxxxxxxxxxxx';
// Function to decrypt AES-256-CBC data
function decryptData(encryptedData) {
const decipher = crypto.createDecipheriv('aes-128-cbc', SECRET_KEY, SECRET_KEY);
let decrypted = decipher.update(base64.decode(encryptedData), 'base64', 'utf8');
decrypted += decipher.final('utf8');
return JSON.parse(decrypted);
}
// Function to verify HMAC-SHA256 signature
function verifySignature(encryptedMessage, receivedSignature) {
const hmac = crypto.createHmac('sha256', HMAC_KEY);
hmac.update(encryptedMessage);
const calculatedSignature = hmac.digest('hex');
return calculatedSignature === receivedSignature;
}
// Route to handle callback
app.post('/callback', (req, res) => {
const data = req.body.data;
const receivedSignature = req.headers['x-signature'];
if (!receivedSignature) {
return res.status(400).json({ error: "Signature missing" });
}
if (verifySignature(data, receivedSignature)) {
try {
const decryptedData = decryptData(data);
return res.status(200).json({ status: "Callback received", data: decryptedData });
} catch (error) {
return res.status(400).json({ error: "Invalid encryption" });
}
} else {
return res.status(401).json({ error: "Invalid signature" });
}
});
app.listen(3000, () => console.log('Callback server running on port 3000'));
This API allows for remittance transactions, which involve transferring funds from a sender to a recipient.
Parameter | Type | Required | Description |
---|---|---|---|
merchant_id | string | Required | Unique identifier for the merchant |
merchant_secrete | string | Required | Merchant's secret key for authentication |
amount | string | Required | Amount to be transferred |
currency | string | Required | Currency in which the amount is expressed (e.g., USD, CDF) |
action | string | Required | Type of transaction (e.g., inbound, outbound) |
customer_number | string | Required | Customer's phone number |
firstname | string | Required | Customer's first name |
lastname | string | Required | Customer's last name |
string | Required | Customer's email address | |
reference | string | Required | Unique reference for the transaction |
method | string | Required | Payment method used (e.g., airtel, orange, mpesa) |
prenom_expediteur | string | Required | Sender's first name |
prenom_destinataire | string | Required | Recipient's first name |
nom_expediteur | string | Required | Sender's last name |
nom_destinataire | string | Required | Recipien's last name |
nationalite_expediteur | string | Required | Sende's nationality |
nationalite_destinataire | string | Required | Recipient's nationality |
numero_identite | string | Required | Sender's identity document number |
nom_fournisseur | string | Required | Name of the service provider |
date_naissance_expediteur | string | Required | Sender's birthdate (format: YYYY-MM-DD) |
pays_envoi | string | Required | Country of origin of the transfer |
pays_reception | string | Required | Country where the funds will be received |
ville_expediteur | string | Required | Sender's city of residence |
date_naissance_beneficiaire_lieu | string | Required | Recipient's birthdate and place (format: YYYY-MM-DD, City) |
date_emission_document_identite | string | Required | Issue date of the identity document (format: YYYY-MM-DD) |
date_expiration_document_identite | string | Required | Expiry date of the identity document (format: YYYY-MM-DD) |
adresse_actuelle | string | Required | Sender's current address |
adresse_permanente | string | Required | Sender's permanent address |
profession | string | Required | Sender's occupation |
{
"merchant_id": "jwHfjdopenc3yt$Tb",
"merchant_secrete": "jz5ulzR!a54kGg!iF",
"amount": "100.00",
"currency": "CDF",
"action": "inbound",
"customer_number": "0842634652",
"firstname": "SupportTest",
"lastname": "SupportTest",
"e-mail": "support@gofreshbakery.com",
"reference": "REF123456",
"method": "orange",
"prenom_expediteur": "Jean",
"prenom_destinataire": "Marie",
"nom_expediteur": "Dupont",
"nom_destinataire": "Martin",
"nationalite_expediteur": "French",
"nationalite_destinataire": "Canadian",
"numero_identite": "AB1234567",
"nom_fournisseur": "ProviderName",
"date_naissance_expediteur": "1980-01-01",
"pays_envoi": "France",
"pays_reception": "Canada",
"ville_expediteur": "Paris",
"date_naissance_beneficiaire_lieu": "1985-05-15, Toronto",
"date_emission_document_identite": "2022-01-01",
"date_expiration_document_identite": "2032-01-01",
"adresse_actuelle": "123 Current St, Paris, France",
"adresse_permanente": "456 Permanent Ave, Paris, France",
"profession": "Engineer"
}
{
"Amount": 100,
"Comment": "Transaction Received Successfully",
"Created_At": "2024-07-19 13:32:11.750967",
"Currency": "CDF",
"Customer_Number": "0842634652",
"Reference": "REF123456",
"Status": "Success",
"Transaction_id": "PDsfCS007JfM19jIhFz247bw",
"Updated_At": "2024-07-19 13:32:11.750967"
}
The verify endpoint allows you to check the status of a transaction. This is particularly useful for asynchronous transactions where the final status might not be immediately available.
Parameter | Type | Required | Description |
---|---|---|---|
merchant_id | string | Required | Your MOKO Afrika merchant ID |
merchant_secrete | string | Required | Your MOKO Afrika merchant secret key |
action | string | Required | The action to perform (verify) |
reference | string | Required | The transaction reference or ID to check |
{
"merchant_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"merchant_secrete": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
"action": "verify",
"reference": "PDYjZJL10X8J26VcQmM23bHV"
}
{
"Action": "debit",
"Amount": 100.0,
"Comment": "Transaction Found",
"Created_at": "2024-03-08 08:05:37",
"Currency": "CDF",
"Customer_Details": "972148867",
"Financial_Institution_id": "null",
"Method": "airtel",
"Reference": "testfp09",
"Status": "Success",
"Trans_Status": "Failed",
"Trans_Status_Description": "La reference de la transaction est invalide, veuillez reesayez ou contactez le service client au 1213",
"Transaction_id": "PD9pLLM03QZJ08X7fXM242x6",
"Updated_at": "2024-03-08 08:51:30"
}
The Trans_Status
field in the response indicates the final status of the transaction. Possible values include "Successful", "Failed", "Pending", etc. Always check this field to determine the actual outcome of the transaction.
When interacting with the MOKO Afrika API, you may encounter various error codes. This section provides a comprehensive list of error codes, their descriptions, and recommended actions.
Error | Status | Code | Description | Comment |
---|---|---|---|---|
An Error Occurred During Execution | Error | 400 | An error occurred during the execution of the request. | There was an error during the execution of the request. |
You Cannot Make a Payment of This Amount | Error | 401 | You cannot make a payment of this amount. | The specified payment amount is not allowed or exceeds the limit. |
Your Balance is Insufficient | Error | 402 | Your balance is insufficient to make this payment. | Your account balance is not enough to cover the payment. |
Transaction Identifier Not Recognized | Error | 404 | The transaction identifier is not recognized in the system. | The provided transaction identifier does not exist in the system. |
Wallet Identifier Not Recognized | Error | 404 | The wallet identifier is not recognized in the system. | The provided wallet identifier does not exist in the system. |
User Information Not Found | Error | 404 | User information was not found in the system. | User information could not be found in the system or the user does not have sufficient authorization. |
Merchant Identifier Not Recognized | Error | 404 | The provided merchant identifier is not recognized in the system. | Please ensure that you provide valid merchant information in the request parameters (merchant_id, merchant_secrete, firstname, lastname) |
Country Flag Not Allowed | Error | 405 | The country flag in the provided phone number is not allowed. | Please ensure that you are using a phone number with the correct country code (243 for DRC). |
Incorrect Customer Number Prefix | Error | 405 | The prefix of the customer phone number is incorrect. | Please ensure that the customer phone number starts with "+243" for DRC country code. |
Invalid Currency Value | Error | 407 | The provided currency value is incorrect or unrecognized. | Currency can only be specified as USD or CDF. |
Action Not Recognized | Error | 408 | The specified action is not recognized by the system. | Please ensure that you are using a valid action according to the documentation. |
Connectivity Problem with the Database | Error | 409 | Connectivity problem with the database. | There was a connectivity issue with the database. |
Internal Server Error | Error | 500 | Internal server error. | There was an internal server error. |