Payment API Gateway (template version) (5.7.3)

Download OpenAPI specification:

This API enables integration with the Payment Gateway to initiate transactions, monitor their statuses, and interact with various payment providers.

Intro

This API is part of the our ecosystem. It allows you to make payments, find out the status of transactions and much more. Here you will find the latest documentation on setting up your solution.

Important: all Mobile Money C2B/B2C payments must be processed only with verified phone numbers.

Before proceeding with the request, please confirm that the phone number:

  • has been previously verified by the customer;
  • matches the number stored in your database.

This requirement is essential for minimizing fraud risk.

Available Payment Providers (Test Environment)

Use the test provider below during sandbox integration. Note that responses from the simulator do not include a callback — transactions remain in the "in progress" state.

ID Name Country Description
14 Simulator ANY For testing purposes

Example response from test provider (ID: 14)

When using the simulator provider, a successful request returns the following structure. Note that status: 1 means "in progress" — use the /status method to poll for updates.

{
  "order_id": "54321",
  "transaction_id": "12345",
  "transaction_ref": "",
  "status": 1,
  "result": {
    "code": 0,
    "message": "OK"
  },
  "provider_result": {
    "code": -8888,
    "message": "Good"
  },
  "service_id": 1,
  "service_version": "1.03/1.14|1.0/1.26|1.0/1.0|1.01/1.01|1.01/1.01||1.01/1.27",
  "service_date_time": "2023-05-15 10:00:00.000000",
  "confirm_type": 0
}

Generating Signature

To generate a correct signature, use your secretKey and apply recursive HMAC SHA-512 hashing on all request fields, excluding the signature field itself.

Example in PHP:

function calculateSignature(array $data, string $secretKey, string $prefix = '', int $depth = 16, int $level = 0): string {
    if ($level >= $depth) {
        throw new Exception('Too deep');
    }

    $result = '';
    foreach ($data as $key => $value) {
        if ($key === 'signature') continue;
        $fullKey = $prefix . $key;
        if (is_array($value)) {
            $result .= calculateSignature($value, $secretKey, $fullKey . '.', $depth, $level + 1);
        } else {
            $result .= $fullKey . $value;
        }
    }

    return $level === 0
        ? strtolower(hash_hmac('sha512', $result, $secretKey))
        : $result;
}

$postData = [
    'merchant_id' => 'fffed61b...',
    'provider_id' => 10,
    'client_id' => '254000000000',
    'country' => 'KE',
    'order_id' => 'order_3444298767545',
    'amount' => 1000,
    'currency' => 'KES',
    'callback_url' => 'https://my.callback.url'
];

$secretKey = 'cf11635572...';
$postData['signature'] = calculateSignature($postData, $secretKey);

Status Codes

Code Name Description
-1 undefined Unknown or error state
0 initiated Operation is initiated
1 in progress Operation is in progress
2 success Operation completed successfully
3 failed Operation failed
4 cancelled Operation was cancelled

Operation Types

Operation type codes visible in callbacks:

Code Type
16 payment_b2c
17 payment_c2b
32 paybill

Available Currencies

Code Note
KES Example for test requests
MWK Malawian kwacha
UGX Ugandan shilling
TZS Tanzanian shilling
ZMW Zambian kwacha

Callbacks

Transaction status is sent via callback because it requires asynchronous confirmation from the client system. Usually the callback should arrive within 2–3 minutes.

If no callback is received, use the status API method to check transaction result. Successful callback = HTTP 200 from merchant'

Payment Methods

Malawi

Provider ID Provider Name Notes
2087 Airtel
2088 TNM

Tanzania

Provider ID Provider Name Notes
2109, 2111 Tigo Check with your manager
2110, 2112 Vodacom Check with your manager
2108, 2113 HaloPesa Check with your manager
2107, 2114 Airtel Check with your manager

Zambia

Provider ID Provider Name Notes
2118 Airtel
2119 MTN

Online Payments

Cashless payment from the customer to the merchant

path Parameters
public_id
required
string
Example: f54ec96649be11ebb3780242ac130002

Merchant public ID

Request Body schema: application/json
required

Parameters to initiate a customer to the merchant payment

merchant_id
required
string (merchantIdDef)

Unique Merchant ID received during the merchant registration

customer_id
required
string (customerIdDef)

Customer ID (usually mobile phone number of the customer)

customer_user_id
required
string (customerUserIdDef) <= 200 characters

Unique customer identifier on the merchant’s side (e.g., internal user ID, CRM ID)

order_id
required
string (orderIdDef)

The unique value is generated by the transaction initiator for each Operation. Max length is 128 symbols. Allowed symbols: [a-z], [A-Z], [0-9], “_” (underscore character), “-” (hyphen), “:” (colon), “.” (dot). For example, GUID or TIMESTAMP can be used as an order_id. This parameter provides API idempotency. It means that requests with identical nonce from the same transaction initiator will have identical responses and The corresponding operation won’t be repeated.

amount
required
string

Amount to pay, should be in format with two digits after point

currency
required
string (currencyDef)

Currency code in ISO 4217 format from the list of availabe currencies

country
string (countryDef)

Country code in ISO 3166-1 alpha-2 format as defined in the payment providers

callback_url
string

URL to notify the merchant via callback. Recommended

provider_id
required
integer (providerDef)
Enum: 14 40

Provider ID. Can be one of the option from this list.

signature
required
string (signatureDef)

Merchant’s request and callback have to be signed to verify sent data. To generate the signature all sent parameters are included in the order they were sent. The parameter signature should be excluded, of course. Example can be found here

Responses

Callbacks

Request samples

Content type
application/json
{
  • "merchant_id": "e0fecd91fcb24f348048193b3fb34875ba3722b4",
  • "customer_id": 900000001,
  • "customer_user_id": "user-123456",
  • "order_id": "16280954971628095497",
  • "amount": "100.00",
  • "currency": "KES",
  • "country": "KE",
  • "callback_url": "https://example.com/callback",
  • "provider_id": 14,
  • "signature": "d7d6d76b0e22c6f9d369fa6c24f107053d12bfd24d3b154f2deb6676bf179c123134e1f20879c803be455d81cfe792f00cd8892c26ce7cf5a05beebb9c80843e"
}

Response samples

Content type
application/json
{
  • "order_id": "16280954971628095497",
  • "transaction_id": "732007046722",
  • "transaction_ref": "MP.33234.342.CP33",
  • "status": 2,
  • "result": {
    },
  • "provider_result": {
    },
  • "service_id": 1,
  • "service_version": 11.1,
  • "service_date_time": "2020-11-25 10:08:32.832969"
}

Callback payload samples

Callback
POST: Asynchronous notification of the merchant about the last performed transaction
Content type
application/json
{
  • "transaction_id": "1234567",
  • "order_id": "16280954971628095497",
  • "service_id": 12345,
  • "service_version": "5.2.0",
  • "service_date_time": "2020-11-25 10:08:32.832969",
  • "result": {
    },
  • "signature": "d7d6d76b0e22c6f9d369fa6c24f107053d12bfd24d3b154f2deb6676bf179c123134e1f20879c803be455d81cfe792f00cd8892c26ce7cf5a05beebb9c80843e"
}

Cashless payment from the merchant to the customer.

Cashless payment from the merchant to the customer. If the confirm_type response parameter is a non-zero merchant, send the second payment_b2c request with confirmation data according to the section Confirmation Types.

path Parameters
public_id
required
string
Example: f54ec96649be11ebb3780242ac130002

Merchant public ID

Request Body schema: application/json
required

Parameters to initiate the merchant to the customer payment

merchant_id
required
string (merchantIdDef)

Unique Merchant ID received during the merchant registration

customer_id
required
string (customerIdDef)

Customer ID (usually mobile phone number of the customer)

customer_user_id
required
string (customerUserIdDef) <= 200 characters

Unique customer identifier on the merchant’s side (e.g., internal user ID, CRM ID)

order_id
required
string (orderIdDef)

The unique value is generated by the transaction initiator for each Operation. Max length is 128 symbols. Allowed symbols: [a-z], [A-Z], [0-9], “_” (underscore character), “-” (hyphen), “:” (colon), “.” (dot). For example, GUID or TIMESTAMP can be used as an order_id. This parameter provides API idempotency. It means that requests with identical nonce from the same transaction initiator will have identical responses and The corresponding operation won’t be repeated.

amount
required
string

Amount to pay, with two digits after point

currency
required
string (currencyDef)

Currency code in ISO 4217 format from the list of availabe currencies

country
string (countryDef)

Country code in ISO 3166-1 alpha-2 format as defined in the payment providers

callback_url
string

URL to notify the merchant via callback

provider_id
required
integer (providerDef)
Enum: 14 40

Provider ID. Can be one of the option from this list.

signature
required
string (signatureDef)

Merchant’s request and callback have to be signed to verify sent data. To generate the signature all sent parameters are included in the order they were sent. The parameter signature should be excluded, of course. Example can be found here

Responses

Request samples

Content type
application/json
{
  • "merchant_id": "e0fecd91fcb24f348048193b3fb34875ba3722b4",
  • "customer_id": 900000001,
  • "customer_user_id": "user-123456",
  • "order_id": "16280954971628095497",
  • "amount": "100.00",
  • "currency": "KES",
  • "country": "KE",
  • "callback_url": "https://example.com/callback",
  • "provider_id": 14,
  • "signature": "d7d6d76b0e22c6f9d369fa6c24f107053d12bfd24d3b154f2deb6676bf179c123134e1f20879c803be455d81cfe792f00cd8892c26ce7cf5a05beebb9c80843e"
}

Response samples

Content type
application/json
{
  • "order_id": "16280954971628095497",
  • "transaction_id": "532007056722",
  • "transaction_ref": "",
  • "status": 2,
  • "result": {
    },
  • "provider_result": {
    },
  • "service_id": 11,
  • "service_version": 11.1,
  • "service_date_time": "2020-11-25 10:08:32.832969",
  • "confirm_type": 0
}

Request a status of the transaction performed earlier

path Parameters
public_id
required
string
Example: f54ec96649be11ebb3780242ac130002

Merchant public ID

Request Body schema: application/json
required

Get the status of the performed transaction

merchant_id
required
string (merchantIdDef)

Unique Merchant ID received during the merchant registration

order_id
required
string (orderIdDef)

The unique value is generated by the transaction initiator for each Operation. Max length is 128 symbols. Allowed symbols: [a-z], [A-Z], [0-9], “_” (underscore character), “-” (hyphen), “:” (colon), “.” (dot). For example, GUID or TIMESTAMP can be used as an order_id. This parameter provides API idempotency. It means that requests with identical nonce from the same transaction initiator will have identical responses and The corresponding operation won’t be repeated.

signature
required
string (signatureDef)

Merchant’s request and callback have to be signed to verify sent data. To generate the signature all sent parameters are included in the order they were sent. The parameter signature should be excluded, of course. Example can be found here

Responses

Request samples

Content type
application/json
{
  • "merchant_id": "e0fecd91fcb24f348048193b3fb34875ba3722b4",
  • "order_id": "16280954971628095497",
  • "signature": "d7d6d76b0e22c6f9d369fa6c24f107053d12bfd24d3b154f2deb6676bf179c123134e1f20879c803be455d81cfe792f00cd8892c26ce7cf5a05beebb9c80843e"
}

Response samples

Content type
application/json
{
  • "order_id": "16280954971628095497",
  • "transaction_id": "732007046722",
  • "transaction_ref": "MP.33234.342.CP33",
  • "status": 2,
  • "result": {
    },
  • "provider_result": {
    },
  • "service_id": 1,
  • "service_version": 11.1,
  • "service_date_time": "2020-11-25 10:08:32.832969"
}

Universal Flow Guide

Quick start for working with multi-step payments


What is it?

A simple way to start payments without worrying about required fields — the system will guide you if anything is missing.


How it works

Depending on the data you provide, the payment will either start immediately, request missing information, or require confirmation (e.g. OTP or email). You can use it in 2 ways:

  1. API only (host-to-host) - mutli-step flow, however it maybe more complex to implement and requires more API calls.
  2. Web payment page (redirect method) - the page will handle the flow for you. This is the recommended way to implement multi-step payments, as it requires minimal API calls and the page will handle all the steps for you.

Option 1: All data provided

INIT with data → status: 1 or 2 → Done

Option 2: Missing data (Universal Flow)

INIT without data → status: 8
    ↓
STATUS request → receive action.fields (list of required fields)
    ↓
STATUS with data → status: 1 → Payment initiated

Option 3: Confirmation required (Multi-Step)

INIT with data → status: 1 (OTP sent)
    ↓
STATUS with OTP → status: 2 (success)

Status codes

Each status tells you exactly what is happening with the payment and what to do next.

Status Meaning Action
8 Data required Send STATUS to get the list of required fields
1 In progress Wait for callback or poll STATUS
2 Success Done
3 Error Check result.message

API Endpoints

Use INIT to start a payment and STATUS to check or continue it.

INIT:

POST /{merchant_hash}/payment_c2b
POST /{merchant_hash}/payment_b2c

STATUS:

POST /{merchant_hash}/status

Required parameters

These are the minimum fields needed to start and manage a payment.

INIT request:

{
  "merchant_id": "string",
  "order_id": "string",
  "amount": "string",
  "currency": "string",
  "customer_id": "string",
  "provider_id": "integer",
  "signature": "string"
}

STATUS request:

{
  "merchant_id": "string",
  "order_id": "string",
  "signature": "string"
}

Optional parameters

Additional fields you can use to enhance or complete the payment flow.

Optional (INIT):

  • extra — additional parameters
  • callback_url — URL for notifications

Optional (STATUS):

  • extra — missing data or OTP codes

Example: Universal Flow

Example of how the system requests and processes missing data.

1. INIT without email:

{"order_id": "ORDER-1", "amount": "100", "currency": "NGN", "provider_id": 170}

Response: status: 8

2. STATUS (get required fields):

{"order_id": "ORDER-1"}

Response: action.fields: {customer_email: {...}}, action.errors: {customer_email: ["..."]}

3. STATUS with data:

{"order_id": "ORDER-1", "extra": {"customer_email": "test@example.com"}}

Response: status: 1 (initiated)


Example: Multi-Step (OTP)

Example of a payment that requires user confirmation.

1. INIT with data:

{"order_id": "ORDER-2", "amount": "500", "currency": "KES", "extra": {"phone": "254712345678"}}

Response: status: 1 + provider_result.message: "OTP sent"

2. STATUS with OTP:

{"order_id": "ORDER-2", "extra": {"otp": "123456"}}

Response: status: 2 (success)


Important notes

Key behaviors to understand, to avoid confusion during integration:

Data from INIT is stored — no need to resend it

STATUS takes priority — can temporarily override saved data, but changes won't be persisted

Validation errors don't fail the operation — it stays in status: 8; fix the data and resubmit

action.errors are arrays — use errors[field][0] for the first message

Mechanisms work together — Universal Flow + Multi-Step can be combined


Common mistakes

Mistakes that can lead to incorrect handling of the flow:

❌ Treating result.code === 0 for status: 8 as a success indicator

❌ Expecting the action block in the INIT response

❌ Parsing action.errors[field] as a string (it's an array!)

❌ Trying to modify saved data via STATUS


Payment page

Gate 8** (via payment page)

C2B

  1. Customer initiates the payment on Merchant side
  2. Merchant sends request to the platform (POST payment_c2b)
  3. Merchant gets link for redirect in pp_url in the payment response
  4. Merchant redirects Customer to payment page
  5. Customer makes a payment on the page
  6. Merchant gets a callback with the final state of the operation

Gate 5** (via payment page)

C2B

  1. Customer initiates the payment on Merchant side
  2. Merchant sends request to the platform (POST payment_c2b)
  3. Merchant gets link for redirect in pp_url in the payment response
  4. Merchant redirects Customer to the payment page
  5. Customer makes a payment on the page
  6. Merchant gets a callback with the final state of the operation

B2C

  1. Customer initiates the payment on Merchant side
  2. Merchant sends request to the platform (POST payment_b2c)
  3. Merchant gets link for redirect in pp_url in the payment response
  4. Merchant redirects Customer to the payment page
  5. Customer chooses bank on the page
  6. Merchant gets a callback with the final state of the operation

Response example

{
  "order_id": "example_order_id_***",
  "transaction_id": "",
  "transaction_ref": "",
  "status": 8,
  "result": {
    "code": 0,
    "message": "request error"
  },
  "provider_result": {
    "code": -8888,
    "message": ""
  },
  "service_id": 1,
  "service_version": "1.03/1.14|1.0/2.0|1.0/1.0|1.01/1.0|1.01/2.0||1.02/1.27",
  "service_date_time": "2026-04-09 14:08:48.535688",
  "pp_url": "https://pp.domain.example/cdeffe6210c7fdc7402c13b210dbd8ddec02da0c/****",
  "confirm_type": 0
}