NAV Navbar
shell php elixir java csharp html

Introduction

Welcome to the KhusaPay API guide. You can use our API to access KhusaPay API endpoints, which can allow you to perform payment transactions within our eco-system.

We have language bindings in Shell, JAVA , PHP and Elixir for necessary endpoints. You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

Invoicing

Creating invoices is easy. KhusaPay allows you to do this using the available checkout iframe endpoint.

To do this you need an api_client_id, secret and key for generating a secure hash.

These (api_client_id, secret and key) are provided by KhusaPay Team. If you have issues with your credentials contact Our Support Team for assistance.

Secure Hash

<?php
$api_client_id= "my-client-id provided by KhusaPay";
$service_id = "my-client-id provided by KhusaPay";
$id_number = "Customer ID number";
$currency = "MWK";
$client_invoice_ref = "Ab-CD-EF-001";
$desc = "Description of invoice";
$name = "John Doe";
$secret = "my-secret";
$key = "my-key";

data_string = "$api_client_id"."$amount_expected"."$service_id"."$id_number"."$currency"."$client_invoice_ref"."$desc"."$name"."$secret";
secure_hash = base64_encode(hash_hmac('sha256', $data_string, $key));
echo "SecureHash : $secure_hash\n";
?>

generate_secure_hash("my-key",["1","100","124","12345678","MWK","REF-01-2024-XVBC","Some invoice","John Doe","my-secret"])

def generate_secure_hash(secret, hash_list) when is_list(hash_list) do
    :crypto.hmac(:sha256, secret, hash_list |> Enum.map(&to_string(&1)))
    |> Base.encode16()
    |> String.downcase()
    |> Base.encode64()
end

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.lang.*;
import java.io.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
public class Main {
private final String USER_AGENT = "Mozilla/5.0";
  public static void main(String[] args) throws Exception {
      Main m = new Main();
      String hash = m.genSig();

      System.out.println("\nHash - "+ hash);
  }
  // HTTP POST request
  private String genSig() {

      /**
       * Validate Payment
       * This endpoint validates a invoices or topups exist before payment is accepted.
       * To generate your secure hash, use format below,
       * 
       */


      String provided_hmac_secret = "your-key"; 
      String api_client_id= "your-api-client-id";
      String data_string = "[YOUR-DATA-STRING]";

      return  generate_signature(data_string, provided_hmac_secret);
  }

  static String generate_signature(String data_string, String secret){
     try{

           SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(),"HmacSHA256");

           Mac mac = Mac.getInstance("HmacSHA256");
           mac.init(keySpec);
           byte[] result = mac.doFinal(data_string.getBytes());

          String encode_result = Base16Encoder.encode(result);


          return Base64.getEncoder().encodeToString(encode_result.toLowerCase().getBytes());

       } catch (Exception e) {

            return "Cannot Process Signature";
      }
  }
}
class Base16Encoder {
    private final static char[] HEX = new char[]{
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    /**
     * Convert bytes to a base16 string.
     */
    public static String encode(byte[] byteArray) {
        StringBuffer hexBuffer = new StringBuffer(byteArray.length * 2);
        for (int i = 0; i < byteArray.length; i++)
            for (int j = 1; j >= 0; j--)
                hexBuffer.append(HEX[(byteArray[i] >> (j * 4)) & 0xF]);
        return hexBuffer.toString();
    }

    /**
     * Convert a base16 string into a byte array.
     */
    public static byte[] decode(String s) {
        int len = s.length();
        byte[] r = new byte[len / 2];
        for (int i = 0; i < r.length; i++) {
            int digit1 = s.charAt(i * 2), digit2 = s.charAt(i * 2 + 1);
            if (digit1 >= '0' && digit1 <= '9')
                digit1 -= '0';
            else if (digit1 >= 'A' && digit1 <= 'F')
                digit1 -= 'A' - 10;
            if (digit2 >= '0' && digit2 <= '9')
                digit2 -= '0';
            else if (digit2 >= 'A' && digit2 <= 'F')
                digit2 -= 'A' - 10;

            r[i] = (byte) ((digit1 << 4) + digit2);
        }
        return r;
    }
}
using System;
using System.Security.Cryptography;
using System.Text;
public class HelloWorld {
    public static void Main(string[] args) {
        var key =  "[YOUR-KEY]";
        var data_string = "[YOUR-DATA-STRING]";
        byte[] res = hmacSHA256(data_string, key);
        var enc = BitConverter.ToString(res).Replace("-", "").ToLower();
        Console.WriteLine(Base64Encode(enc));
    }
    static byte[] hmacSHA256(String data, String key) {
        using (HMACSHA256 hmac = new HMACSHA256(Encoding.ASCII.GetBytes(key)))
        {
            return hmac.ComputeHash(Encoding.ASCII.GetBytes(data));
        }
    }
    public static string Base64Encode(string plainText) {
    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
    return System.Convert.ToBase64String(plainTextBytes);
    }
}

This segment explains or demonstrates how to generate a secure hash. To generate your secure hash for checkout purpose, the below fields are required:-

Expected Parameters

Parameter Mandatory Description
apiClientID Yes Provided by KhusaPay
amountExpected Yes Amount to be paid
serviceID Yes Service ID Provided by KhusaPay
clientIDNumber Yes Customers ID Number
currency Yes Bill currency
billRefNumber Yes Unique client reference
billDesc Yes Description of bill
clientName Yes Customers Name
secret Yes Unique client secret provided by KhusaPay

The above command returns a base64 encoded hash

Checkout

curl -H "Authorization: Bearer <TOKEN>" \
  -d '<see sample payload in `php` or `elixir` tab >'
 -XPOST "https://{UAT_SERVER_URL}/api/PaymentAPI/checkout"

<form method="post" action="https://{UAT_SERVER_URL}/api/PaymentAPI/checkout" target="some_iframe" id="some_form">
    <input type="hidden" name="apiClientID" id="apiClientID" value="[apiClientID provided by KhusaPay]">
    <input type="hidden" name="serviceID" id="serviceID" value="[serviceID provided by KhusaPay]">
    <input type="hidden" name="billDesc" id="billDesc" value="Invoice for service X">
    <input type="hidden" name="currency" id="currency" value="MWK">
    <input type="hidden" name="billRefNumber" id="billRefNumber" value="Bill-bb9a0495-f586-40b6-b4a7-37f78f7e51a0">
    <input type="hidden" name="clientMSISDN" id="clientMSISDN" value="2657XXXYYYZZ">
    <input type="hidden" name="clientName" id="clientName" value="John Doe"/>
    <input type="hidden" name="clientIDNumber" id="clientIDNumber" value="00"/>
    <input type="hidden" name="clientEmail" id="clientEmail" value="some@gmail.com"/>
    <input type="hidden" name="callBackURLOnSuccess" id="callBackURLOnSuccess" value="[Your webpage callback URL]"/>
    <input type="hidden" name="amountExpected" id="amountExpected" value="1"/>
    <input type="hidden" name="notificationURL" id="notificationURL" value="[Your notification URL]"/>
    <input type="hidden" name="pictureURL" id="pictureURL" value=""/>
    <input type="hidden" name="secureHash" id="secureHash" value="MmEwMWNlNDNkZWFmYTg4MmFkMTE0MWFkMmRjNjliZDU2Mzk4NTViNmRiMjFiMWY2MjRjNzFkMjVjYzdmYmI0MA=="/>
    <input type="hidden" name="format" value="html"/>
    <input type="hidden" name="sendSTK" value="false"/>
    <input type="hidden"  value="Submit"/>
</form>
<form method="post" action="https://{UAT_SERVER_URL}/api/PaymentAPI/checkout" target="some_iframe" id="some_form">
    <input type="hidden" name="apiClientID" id="apiClientID" value="[apiClientID provided by KhusaPay]">
    <input type="hidden" name="serviceID" id="serviceID" value="[serviceID provided by KhusaPay]">
    <input type="hidden" name="billDesc" id="billDesc" value="Invoice for service X">
    <input type="hidden" name="currency" id="currency" value="MWK">
    <input type="hidden" name="billRefNumber" id="billRefNumber" value="Bill-bb9a0495-f586-40b6-b4a7-37f78f7e51a0">
    <input type="hidden" name="clientMSISDN" id="clientMSISDN" value="2657XXXYYYZZ">
    <input type="hidden" name="clientName" id="clientName" value="John Doe"/>
    <input type="hidden" name="clientIDNumber" id="clientIDNumber" value="00"/>
    <input type="hidden" name="clientEmail" id="clientEmail" value="some@gmail.com"/>
    <input type="hidden" name="callBackURLOnSuccess" id="callBackURLOnSuccess" value="[Your webpage callback URL]"/>
    <input type="hidden" name="amountExpected" id="amountExpected" value="1"/>
    <input type="hidden" name="notificationURL" id="notificationURL" value="[Your notification URL]"/>
    <input type="hidden" name="pictureURL" id="pictureURL" value=""/>
    <input type="hidden" name="secureHash" id="secureHash" value="MmEwMWNlNDNkZWFmYTg4MmFkMTE0MWFkMmRjNjliZDU2Mzk4NTViNmRiMjFiMWY2MjRjNzFkMjVjYzdmYmI0MA=="/>
    <input type="hidden" name="format" value="html"/>
    <input type="hidden" name="sendSTK" value="false"/>
    <input type="hidden"  value="Submit"/>
</form>

The above will redirect or return a html page that allows your customer to make payment.

This endpoint creates a new invoice for a customer and redirects to the KhusaPay checkout page.

HTTP Request

Post data as form data

POST https://{UAT_SERVER_URL}/api/PaymentAPI/checkout

Expected Parameters

Parameter Mandatory Description
apiClientID Yes Provided by KhusaPay
serviceID Yes Provided by KhusaPay
billRefNumber Yes Unique client transaction number
billDesc Yes Description of the invoice
clientMSISDN Yes Mobile number of whoever is receiving the invoice or bill
clientIDNumber Yes Customers ID Number
clientName Yes Customers Name
clientEmail Yes Customers Email
notificationURL Yes Clients endpoint URL to receive invoice updates on payment
pictureURL No URL of customers image to be displayed on checkout
callBackURLOnSuccess Yes Callback url to be invoked for redirection to merchant site on successful payment
currency Yes Currency for the invoice. This is based on the serviceID.
amountExpected Yes Invoice amount
format No Default format is 'html'. If you want to store the invoice number set this to 'json'
sendSTK No If you want the customer to receive Mobile Money STK set this to 'true'
secureHash Yes Hash generated to ensure validity of invoice

Payments

This segment applies to Payment Aggregators and Banks and describes the API available on KhusaPay, to receive and process payment instructions for invoices paid at the aggregator or bank.

Validate Payment

curl -H "Content-Type: application/json" \
 -XPOST -d '<see sample payload in `php` or `elixir` tab >' \
 "https://{UAT_SERVER_URL}/api/payment/validate"

<?php

  //Sample hash-generation
  $secret="some-secret";
  $key="some-key";
  $api_client_id="your-id";
  $invoice_number="invoice-number";
  $amount="some-amount";

  $data_string = $api_client_id.$invoice_number.$amount.$secret;

  echo "\nDataString: $data_string\n";

  $hash = base64_encode(hash_hmac('sha256', $data_string, $key));

  echo "\n $hash \n"
?>

 <?php
  $url = 'https://{UAT_SERVER_URL}/api/payment/validate';
  $api_client_id = "";
  $invoice_no = "";
  $amount = "";
  $currency="";
  $secret="";
  $key="";

  $data = array(
    "api_client_id"=> $api_client_id,
    "ref_no"=> $invoice_no,
    "currency"=> $currency,
    "amount"=> $amount,    
    "secure_hash"=> base64_encode(hash_hmac('sha256', $api_client_id.$invoice_number.$amount.$secret, $key));,
  );
  $payload = json_encode($data);
  $headers = ["Content-Type","application/json"];

  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $url);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_POST, true);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
  curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  $curl_response = curl_exec($curl);
  echo $curl_response;
?>
url = 'https://{UAT_SERVER_URL}/api/payment/validate'
payload = %{
  "api_client_id": "",
  "ref_no": "",
  "currency": "",
  "amount": "",  
  "secure_hash": "",
}
{:ok, %HTTPoison.Response{body: body}} = HTTPoison.post(url, payload, %{
                    "Content-type" => "application/json;charset=UTF-8"})
{:ok, body}
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.lang.*;
import java.io.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
public class Main {
private final String USER_AGENT = "Mozilla/5.0";
  public static void main(String[] args) throws Exception {
      Main m = new Main();
      String hash = m.genSig();

      System.out.println("\nHash - "+ hash);
  }
  // HTTP POST request
  private String genSig() {

      /**
       * Validate Payment
       * This endpoint validates a invoices or topups exist before payment is accepted.
       * To generate your secure hash, use format below,
       * data_string = "$api_client_id"."$ref_no"."$amount"."$secret"
       * 
       */
      String provided_hmac_secret = "your-key"; //
      String api_client_id= "your-api-client-id";
      String invoice_no = "";
      String amount = "";
      String secret = "your-secret";
      String data_string = api_client_id + invoice_no + amount + secret;

      return  generate_signature(data_string, provided_hmac_secret);
  }

  static String generate_signature(String data_string, String secret){
     try{

           SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(),"HmacSHA256");

           Mac mac = Mac.getInstance("HmacSHA256");
           mac.init(keySpec);
           byte[] result = mac.doFinal(data_string.getBytes());

          String encode_result = Base16Encoder.encode(result);


          return Base64.getEncoder().encodeToString(encode_result.toLowerCase().getBytes());

       } catch (Exception e) {

            return "Cannot Process Signature";
      }
  }
}
class Base16Encoder {
    private final static char[] HEX = new char[]{
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    /**
     * Convert bytes to a base16 string.
     */
    public static String encode(byte[] byteArray) {
        StringBuffer hexBuffer = new StringBuffer(byteArray.length * 2);
        for (int i = 0; i < byteArray.length; i++)
            for (int j = 1; j >= 0; j--)
                hexBuffer.append(HEX[(byteArray[i] >> (j * 4)) & 0xF]);
        return hexBuffer.toString();
    }

    /**
     * Convert a base16 string into a byte array.
     */
    public static byte[] decode(String s) {
        int len = s.length();
        byte[] r = new byte[len / 2];
        for (int i = 0; i < r.length; i++) {
            int digit1 = s.charAt(i * 2), digit2 = s.charAt(i * 2 + 1);
            if (digit1 >= '0' && digit1 <= '9')
                digit1 -= '0';
            else if (digit1 >= 'A' && digit1 <= 'F')
                digit1 -= 'A' - 10;
            if (digit2 >= '0' && digit2 <= '9')
                digit2 -= '0';
            else if (digit2 >= 'A' && digit2 <= 'F')
                digit2 -= 'A' - 10;

            r[i] = (byte) ((digit1 << 4) + digit2);
        }
        return r;
    }
}

The above command returns JSON structured like this:

  {
    "status": 200,
    "description": "Bill Found",
    "data": {
      "amount": "xxx",
      "name": "xxx",
      "currency": "xxx"
    }
  }

  {
    "status": 404,
    "description": "Bill not found",
  }

  {
    "status": 500,
    "description": "Internal Error or any other failure error",
  }

This endpoint checks the validity of an invoice before payment is accepted.

To generate your secure hash, use fields below:-

Expected Parameters

Parameter Mandatory Description
api_client_id Yes Provided by KhusaPay
ref_no Yes Invoice Reference
amount Yes Amount paid
secret Yes Unique client secret provided by KhusaPay

HTTP Request

POST https://{UAT_SERVER_URL}/api/payment/validate

Expected Parameters

Parameter Mandatory Description
api_client_id Yes The provided client ID.
ref_no Yes Unique invoice/topup reference code from KhusaPay.
currency Yes Currency associated
amount Yes Amount of the bill.
secure_hash Yes A sha256 encoded hash.

Confirm Payment

curl -H "Content-Type: application/json"\
-XPOST -d '<see sample payload in `php` or `elixir` tab >'\
"https://{UAT_SERVER_URL}/api/payment/confirm"
 <?php
  $url = 'https://{UAT_SERVER_URL}/api/payment/confirm';
  $key = "";
  $secret = "";
  $api_client_id = "";
  $invoice_no = "";
  $amount = "";
  $currency="";
  $gateway_transaction_id = "";
  $gateway_transaction_date = date("Y-m-d H:i:s");
  $customer_name = "John Doe";
  $customer_account_number = "";

  $data = array(
    "api_client_id"=> $api_client_id,
    "ref_no"=> $invoice_no,
    "amount"=> $amount, 
    "currency"=> $currency,
    "gateway_transaction_id"=> $gateway_transaction_id,
    "gateway_transaction_date"=> $gateway_transaction_date,
    "customer_name"=> $customer_name,
    "customer_account_number"=> $customer_account_number,
    "secure_hash"=> base64_encode(hash_hmac('sha256',$api_client_id . $invoice_no . $amount . $currency . $gateway_transaction_id . $gateway_transaction_date . $customer_name . $customer_account_number . $secret, $key))
  );

  $headers = ["Content-Type","application/json"];

  $payload = json_encode($data);
  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $url);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_POST, true);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
  curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  $curl_response = curl_exec($curl);
  echo $curl_response;
?>
url = 'https://{UAT_SERVER_URL}/api/payment/confirm'
payload = %{
  "api_client_id": "",
  "ref_no": "",
  "amount": "",  
  "currency": "",
  "gateway_transaction_id": "",
  "gateway_transaction_date": "",
  "customer_name": "",
  "customer_account_number": "",
  "secure_hash": "",
}
{:ok, %HTTPoison.Response{body: body}} = HTTPoison.post(url, payload, %{
                    "Content-type" => "application/json;charset=UTF-8"})
{:ok, body}
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.lang.*;
import java.io.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
public class Main {
private final String USER_AGENT = "Mozilla/5.0";
  public static void main(String[] args) throws Exception {
      Main m = new Main();
      String hash = m.genSig();

      System.out.println("\nHash - "+ hash);
  }
  // HTTP POST request
  private String genSig() {

      String provided_hmac_secret = "key"; //
      String api_client_id= "1";
      String invoice_no = "NWQTYU";
      String currency = "SSP";
      String amount = "10";
      String gateway_transaction_id="1234434";
      String gateway_transaction_date="2021-01-26 15:30:01";
      String customer_name = "JOHN DOE";
      String customer_account_number = "438485959";
      String secret = "secret";
      String data_string = api_client_id + invoice_no + amount + currency + gateway_transaction_id + gateway_transaction_date + customer_name + customer_account_number + secret;

      return  generate_signature(data_string, provided_hmac_secret);
  }

  static String generate_signature(String data_string, String secret){
     try{

           SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(),"HmacSHA256");

           Mac mac = Mac.getInstance("HmacSHA256");
           mac.init(keySpec);
           byte[] result = mac.doFinal(data_string.getBytes());

          String encode_result = Base16Encoder.encode(result);


          return Base64.getEncoder().encodeToString(encode_result.toLowerCase().getBytes());

       } catch (Exception e) {

            return "Cannot Process Signature";
      }
  }
}
class Base16Encoder {
    private final static char[] HEX = new char[]{
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    /**
     * Convert bytes to a base16 string.
     */
    public static String encode(byte[] byteArray) {
        StringBuffer hexBuffer = new StringBuffer(byteArray.length * 2);
        for (int i = 0; i < byteArray.length; i++)
            for (int j = 1; j >= 0; j--)
                hexBuffer.append(HEX[(byteArray[i] >> (j * 4)) & 0xF]);
        return hexBuffer.toString();
    }

    /**
     * Convert a base16 string into a byte array.
     */
    public static byte[] decode(String s) {
        int len = s.length();
        byte[] r = new byte[len / 2];
        for (int i = 0; i < r.length; i++) {
            int digit1 = s.charAt(i * 2), digit2 = s.charAt(i * 2 + 1);
            if (digit1 >= '0' && digit1 <= '9')
                digit1 -= '0';
            else if (digit1 >= 'A' && digit1 <= 'F')
                digit1 -= 'A' - 10;
            if (digit2 >= '0' && digit2 <= '9')
                digit2 -= '0';
            else if (digit2 >= 'A' && digit2 <= 'F')
                digit2 -= 'A' - 10;

            r[i] = (byte) ((digit1 << 4) + digit2);
        }
        return r;
    }
}

The above command returns JSON structured like this:

  {
    "status": 200,
    "description": "Success",
  }

  {
    "status": 500,
    "description": "Failed",
  }

This endpoint received and stores the payment tied to a specific invoice.

To generate your secure hash, use fields below:-

Expected Parameters

Parameter Mandatory Description
api_client_id Yes Provided by KhusaPay
ref_no Yes Invoice Reference
amount Yes Amount paid
currency Yes Currency
gateway_transaction_id Yes Unique gateway transaction ID
gateway_transaction_date Yes Transaction date, format Y-m-d H:i:s
customer_name Yes Customers name
customer_account_number Yes Customers account number e.g phone number
secret Yes Unique client secret provided by KhusaPay

HTTP Request

POST https://{UAT_SERVER_URL}/api/payment/confirm

URL Parameters

Parameter Mandatory Description
api_client_id Yes The provided client ID.
ref_no Yes Unique bill/invoice reference code.
amount Yes Amount of the bill.
currency Yes Currency used for payment
gateway_transaction_id Yes Callers unique transaction reference
gateway_transaction_date Yes Date and time transaction takes place. Format Y-m-d h:i:s e.g 2019-08-01 08:30:08
customer_name Yes Name of customer.
customer_account_number Yes Associated account identifier for customer e,g id_number or msisdn can be used here
secure_hash Yes A sha256 encoded hash.

Instant Payment Notification

POST https://{thirdparty endpoint}

<?php
  $json = file_get_contents('php://input');
  $data = json_decode($json);
  //Sample hash-generation
  $secret="some-secret";
  $key="some-key";
  $client_invoice_ref= $data["client_invoice_ref"];
  $invoice_number=$data["invoice_number"];
  $amount=$data["amount_paid"];
  $payment_date = $data["payment_date"];

  $data_string = $client_invoice_ref.$invoice_number.$amount.$payment_date.$secret;

  echo "\nDataString: $data_string\n";

  $hash = base64_encode(hash_hmac('sha256', $data_string, $key));

  echo "\n $hash \n"
?>
key = "my-key"
secret = "my-secret"
hash_list = ["#{client_invoice_ref}", "#{invoice_number}", "#{amount_paid}", "#{payment_date}", "#{secret}"]

def generate_secure_hash(key, hash_list) when is_list(hash_list) do
  :crypto.hmac(:sha256, key, hash_list)
  |> Base.encode16()
  |> String.downcase()
  |> Base.encode64()
end

KhusaPay will send a payment notification after SUCCESSFUL processing. You will expect a payload that is structured as below and within this payload, a secure_hash is provided so that third-party systems can be able to verify the legitimacy of the notification. A Sample is below,

{
  "payment_channel" : "Some Channel",
  "client_invoice_ref" : "SS6489db2e1fdfd",
  "payment_reference" : [ {
    "payment_reference" : "ABCDEF-925",
    "payment_date" : "2023-06-14T15:33:16",
    "inserted_at" : "2023-06-14T15:33:16",
    "currency" : "MWK",
    "amount" : "102.00"
  } ],
  "currency" : "MWK",
  "amount_paid" : "102.00",
  "invoice_amount" : "102.00",
  "status" : "settled",
  "invoice_number" : "ABCDEF",
  "payment_date" : "2023-06-14 15:33:16Z",
  "last_payment_amount" : "102.00",
  "secure_hash": "NTU4NzEzNzhiOTI1N2NlODY3YWYzYjVhYjQ4MzNiNDYzY2M3MzQwYmNlZDc4ZDJlZjg3ZDZkOTQ5ZjUyM2EzNQ==",
}

Please note: Use secure_hash, to check validity of the received notification.

In addition, to generate a matching hash, use/concatenate the following fields in the same order:-

Expected Parameters

Parameter Mandatory Description
client_invoice_ref Yes Clients reference
invoice_number Yes Invoice Reference
amount_paid Yes Amount paid
payment_date Yes Payment Date
secret Yes Unique client secret provided by KhusaPay

The above parameters are from the sample IPN payload on to your right side.

Query Payment Status

curl -H "Content-Type: application/json" \
-X GET -d '<see sample payload in `php` or `elixir` tab >' \
"https://{UAT_SERVER_URL}/api/invoice/payment/status?api_client_id=YOUR_CLIENT_ID&ref_no=YOUR_REF_NO&secure_hash=YOUR_HASH"
 <?php

  $key = "my_key";
  $data_string = "my_client_id"."my_ref_no";
  $secure_hash = base64_encode(hash_hmac('sha256', $data_string, $key));

  $url = 'https://{UAT_SERVER_URL}/invoice/payment/status';
  $data = array(
    "api_client_id"=>"",
    "ref_no"=>"",
    "secure_hash"=>"$secure_hash",
  );
  $payload = json_encode($data);
  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $url);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_POST, true);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
  $curl_response = curl_exec($curl);
  echo $curl_response;
?>
key = "my-key"
api_client_id="my_api_client_id"
ref_no="ref-no"
hash_list = ["#{api_client_id}", "#{ref_no}"]

secure_hah = generate_secure_hash(key, hash_list) # Referenced from the Elixir hash sample

url = 'https://{UAT_SERVER_URL}/invoice/payment/status'
payload = %{
  "api_client_id": "",
  "ref_no": "",
  "secure_hash": ""
}
{:ok, %HTTPoison.Response{body: body}} = HTTPoison.get(url, payload, %{
                    "Content-type" => "application/json;charset=UTF-8"})
{:ok, body}

The above command returns JSON structured like this(example):

{
    "status": "pending",
    "ref_no": "WVZGEP",
    "name": "Test User",
    "currency": "MWK",
    "client_invoice_ref": "EPIC-ORDER103",
    "payment_date": "2024-01-01 12:30:01",
    "amount_paid": "0.00",
    "amount_expected": "100.00"
}

Status can be - pending, partial, settled

In the event that push notification is not available, or Network Timeouts, clients can use this endpoint to query the status of bill payments.

This endpoint checks the status of an invoice. To generate your secure hash, use fields below:-

Expected Parameters

Parameter Mandatory Description
api_client_id Yes Provided by KhusaPay
ref_no Yes Invoice Reference
key Yes Unique client key provided by KhusaPay

HTTP Request

GET https://{UAT_SERVER_URL}/api/invoice/payment/status

URL Parameters

Parameter Mandatory Description
api_client_id Yes Provided by KhusaPay
ref_no Yes Unique invoice number from KhusaPay.
secure_hash Yes A sha256 encoded hash.

FAQs

Still unable to integrate, here are the frequently asked questions about the process.

Issue Meaning
No IPN Response Check if your IPN endpoint has an error and you are able to connect to it via the internet.
Invalid Hash Error Please review the secure hash section and verify that your data string conforms to the documentation. Confirm that the credentials you are using are valid.
502 Error Possible maintainance on-going. Wait a few minutes to retry

Errors

The KhusaPay API uses the following HTTP error codes:

Error Code Meaning
200 Valid Request -- Your request is valid.
400 Invalid Request -- Your request is invalid.
401 Unauthorized/Invalid secure hash -- Your API key is wrong.
403 Forbidden Access
404 Item not found or does not exist -- The item (e.g) does not exist.
500 Internal Server Error -- We had a problem with our server. Try again later.
502 Bad Gateway - Something went wrong.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.