Payouts Integration

Payouts in a jiffy with our step-by-step guide

Overview

Integrating Ozow as an automated payout provider via a RESTful API can be achieved with the following sequence of steps:

  • Step 1: Check the payout availability for the destination bank, including the realtime clearing (RTC) feature (optional, but recommended for a better client experience)
  • Step 2: Submit a payout request via Ozow API
  • Step 3: Ozow requires each merchant to build a web-hook API end-point which will be triggered in order to verify each payout request
  • Step 4: Check payout status using Ozow's API (optional but recommended)

Before you start

Please make sure to use your live API Key after testing, and once you are ready to go live.

N.B. Please ensure that the relevant SiteCode and APIKey are included in the headers for all requests to the Payouts endpoints

📘

Payouts Float

Ozow uses the funds you have loaded into your Ozow float to perform payouts.

Please click on the following link to top up your float balance: https://hub.ozow.com/docs/top-up-guide


Service details

Version

The current API version is: v1

Environment base urls

Production: https://payoutsapi.ozow.com/{version}

Development: https://stagingpayoutsapi.ozow.com/{version}

Mock API endpoint base urls

Ozow provides a mock implementation for each API endpoint, which can be utilised during the development phase of a payout integration. These mock endpoints enable a merchant that validation rules and business logic is implemented correctly and tested prior to an actual payout taking place. The endpoints are safe to consume and no monies will be transferred. In order to consume a mock endpoint, the "mock" route is added to the environment base urls:

Production: https://payoutsapi.ozow.com/mock/{version}

Development: https://stagingpayoutsapi.ozow.com/mock/{version}

N.B. Payout requests submitted to the Mock API endpoints are not visible on the dashboard

Authentication

Ozow’s payout API authentication requires two headers to be added for each endpoint request. The following headers are required:

  1. SiteCode - A unique code for the site currently in use. A site code is generated when adding a site in the Ozow merchant admin section.
  2. APIKey  – A new payout API specific key will be provided by Ozow.

Test account details

SiteCode: [Please contact support for SiteCode - [email protected]]

ApiKey: [Please contact support for Key - [email protected]]


Account number encryption

For security and verification purposes

Ozow requires that the merchant encrypts the destination account number using a unique encryption key per payout request, as described below:

  1. The Advanced Encryption Standard (AES) should be used to encrypt the destination bank account. More information can be obtained on Wikipedia and on Microsoft’s .Net Cryptography site.

  2. The following AES parameters should be used:

    • Key size: 256
    • Block Cypher Mode of operation: Cipher block chaining (CBC)
    • Padding: PKCS7
  3. The initialisation vector (IV) should be an SHA512 hash of the following (Note: the IV hash length should be 16 bytes. If the SHA512 has is longer than 16 bytes then the first 16 bytes should be used as the IV):

    • Merchant reference
    • Amount in cents (e.g 10000 for R100.00)
    • Encryption key
  4. The merchant will need to persist the encryption key per payout request.


Message integrity checks

Post hash check

Follow these steps to generate the hash check:

  1. Concatenate the post variables (excluding HashCheck) in the order they appear in the post variables table.
  2. Append your API key to the concatenated string.
  3. Convert the concatenated string to lowercase.
  4. Generate a SHA512 hash of the lowercase concatenated string.

Hash check example

    var siteCode = "[YOUR SITE CODE]";
    var amount = 17.15;
    var MerchantReference = "123";
    var CustomerBankReference = "ABC123";
    var apiKey = "[YOUR API KEY]";
    var IsRtc = false;
    var NotifyUrl = "https://requestcatcher.com/";
    var BankGroupId = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B";
    var AccountNumber = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd";
    var BranchCode = "198765";
    var inputString = string.Concat(
        siteCode,
        Convert.ToInt32(amount * 100),
        MerchantReference,
        CustomerBankReference,
        IsRtc,
        NotifyUrl,
        BankGroupId,
        AccountNumber,
        BranchCode,
        apiKey);

var calculatedHashResult = GenerateRequestHashCheck(inputString);

string GenerateRequestHashCheck(string inputString)
{
    var stringToHash = inputString.ToLower();
    Console.WriteLine($"Before Hashcheck: {stringToHash}");

    return GetSha512Hash(stringToHash);
}

string GetSha512Hash(string stringToHash)
{
    using SHA512 alg = new SHA512CryptoServiceProvider();
    var bytes = alg.ComputeHash(Encoding.UTF8.GetBytes(stringToHash));

    var sb = new StringBuilder();
    foreach (var b in bytes)
    {
        var hex = b.ToString("x2");
        sb.Append(hex);
    }

    return sb.ToString();
}

<?php

$siteCode = "[YOUR SITE CODE]";
$amount = 17.15;
$MerchantReference = "123";
$CustomerBankReference = "ABC123";
$apiKey = "[YOUR API KEY]";
$IsRtc = false;
$NotifyUrl = "https://requestcatcher.com/";
$BankGroupId = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B";
$AccountNumber = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd";
$BranchCode = "198765";

$inputString = $siteCode . (int)($amount * 100) . $MerchantReference . $CustomerBankReference . $IsRtc . $NotifyUrl . $BankGroupId . $AccountNumber . $BranchCode . $apiKey;

$calculatedHashResult = generateRequestHashCheck($inputString);

function generateRequestHashCheck($inputString)
{
    $stringToHash = strtolower($inputString);
    echo "Before Hashcheck: " . $stringToHash . "\n";

    return getSha512Hash($stringToHash);
}

function getSha512Hash($stringToHash)
{
    $bytes = hash("sha512", $stringToHash, true);
    $hex = bin2hex($bytes);

    return $hex;
}

?>

function GeneratePayoutRequestHash() {
	var siteCode = "[YOUR SITE CODE]";
  var amount = 17.15;
  var MerchantReference = "123";
  var CustomerBankReference = "ABC123";
  var apiKey = "[YOUR API KEY]";
  var IsRtc = false;
  var NotifyUrl = "https://requestcatcher.com/";
  var BankGroupId = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B";
  var AccountNumber = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd";
  var BranchCode = "198765";
  var inputString = string.Concat(
    siteCode,
    Convert.ToInt32(amount * 100),
    MerchantReference,
    CustomerBankReference,
    IsRtc,
    NotifyUrl,
    BankGroupId,
    AccountNumber,
    BranchCode,
    apiKey);

var calculatedHashResult = GenerateRequestHashCheck(inputString);
console.log("Hashcheck: " + calculatedHashResult);
}

function GenerateRequestHashCheck(inputString) {
var stringToHash = inputString.toLowerCase();
console.log("Before Hashcheck: " + stringToHash);

return GetSha512Hash(stringToHash);
}

function GetSha512Hash(stringToHash) {
var sha512 = new jsSHA("SHA-512", "TEXT");
sha512.update(stringToHash);
var hash = sha512.getHash("HEX");

return hash;
}

GeneratePayoutRequestHash();
site_code = "[YOUR SITE CODE]"
amount = 17.15
merchant_reference = "123"
customer_bank_reference = "ABC123"
api_key = "[YOUR API KEY]"
is_rtc = False
notify_url = "https://requestcatcher.com/"
bank_group_id = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B"
account_number = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd"
branch_code = "198765"

input_string = "{}{}{}{}{}{}{}{}{}{}".format(
    site_code,
    int(amount * 100),
    merchant_reference,
    customer_bank_reference,
    is_rtc,
    notify_url,
    bank_group_id,
    account_number,
    branch_code,
    api_key
)

calculated_hash_result = generate_request_hash_check(input_string)

def generate_request_hash_check(input_string):
    string_to_hash = input_string.lower()
    print("Before Hashcheck: {}".format(string_to_hash))

    return get_sha512_hash(string_to_hash)

def get_sha512_hash(string_to_hash):
    import hashlib
    sha512 = hashlib.sha512()
    sha512.update(string_to_hash.encode('utf-8'))
    return sha512.hexdigest()


Operations

Get available banks

This method can be called to retrieve the set of banks that are available for making payouts to.
The call returns a list of banks with a unique identifier (BankGroupId) per bank. The BankGroupId is utilised as an input parameter to check payout availability for a particular bank, as well as to identify the destination bank for a payout.

{baseUrl}/getavailablebanks

Response

A successful call will return a list of available banks. Each bank is represented by a BankGroup object, which is described as below.

PropertyTypeDescription
BankGroupIdGuidA unique bank identifier.
BankGroupNameString (100)The bank name.
UniversalBranchCodeString (10)The universal branch code that can be used to make when requesting a payout.