Step 3: Verify Payout webhook (merchant implementation)
Ozow requires that each integrating merchant implements a payout verification POST webhook will be used for the following:
- To verify a payout request
- To provide Ozow with the AES decryption key which will be used to decrypt the payout destination account number.
Note:
If Ozow fails to decrypt the account number with the decryption key provided, a payout cancelled notification will be send back to the merchant.
Webhook authentication
Note:
At a minimum, the webhook should implement bearer/auth header type authentication. The service call will use the access token provided by the merchant where it is passed in the header as a AccessToken field to the webhook endpoint.
Post variables
Property | Type | Req. | Description |
---|---|---|---|
1.PayoutId | Guid | Yes | A unique identifier that should be used to identify the payout. |
2.SiteCode | String (50) | Yes | The merchant’s site code. [Please contact support for SiteCode - [email protected]] |
3.Amount | Decimal (9,2) | Yes | The payout amount. |
4.MerchantReference | String (20) | Yes | The merchant's reference for the payout. |
5.CustomerBankReference | String (20) | Yes | The reference that will appear on the merchant’s bank statement and can be used for recon purposes. |
6.IsRtc | bool | Yes | Whether the payout should be processed as an RTC payout. |
7.NotifyUrl | String (150) | No | The URL that we should use to post the all payout notifications. |
8.BankingDetails | Object | Yes | Payout destination banking details. Refer to table below. |
9.HashCheck | String (250) | Yes | SHA512 hash used to ensure that certain fields in the message have not been altered after the hash was generated. Check the generate hash section below for more details on how to generate the hash. |
BankDetails Object
Property | Type | Req. | Description |
---|---|---|---|
BankGroupId | Guid | Yes | The unique bank identifier. |
AccountNumber | String (32) | Yes | The bank account number the payment should be made to. The account number should be encrypted by the encryption method detailed above. |
BranchCode | String (10) | Yes | The destination bank branch code. |
Verify webhook hash check
These steps will be used to generate the hash check, which should be validated to ensure a legitimate webhook verification has been initiated from Ozow:
- Concatenate the post variables (excluding HashCheck) in the order they appear in the post variables table.
- Your API key will be appended to the concatenated string.
- Convert the concatenated string to lowercase.
- Generate a SHA512 hash of the lowercase concatenated string.
Verification Request Hash Calculation
using System.Security.Cryptography;
using System.Text;
GeneratePayoutVerificationRequestHash();
void GeneratePayoutVerificationRequestHash()
{
var PayoutId = "00000000-0000-0000-0000-000000000000";
var siteCode = "[YOUR SITE CODE]";
var amount = 17.15;
var MerchantReference = "123";
var CustomerBankReference = "ABC123";
var IsRtc = false;
var NotifyUrl = "https://requestcatcher.com/";
var BankGroupId = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B";
var AccountNumber = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd";
var BranchCode = "198765";
var apiKey = "[YOUR API KEY]";
var inputString = string.Concat(PayoutId,
SiteCode,
Convert.ToInt32(Amount * 100),
MerchantReference,
CustomerBankReference,
IsRtc,
NotifyUrl,
BankGroupId,
AccountNumber,
BranchCode,
apiKey);
var calculatedHashResult = GenerateRequestHashCheck(inputString);
Console.WriteLine($"Hashcheck: {calculatedHashResult}");
}
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
function GeneratePayoutVerificationRequestHash() {
$PayoutId = "00000000-0000-0000-0000-000000000000";
$siteCode = "[YOUR SITECODE]";
$amount = 17.15;
$MerchantReference = "123";
$CustomerBankReference = "ABC123";
$IsRtc = false;
$NotifyUrl = "https://requestcatcher.com/";
$BankGroupId = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B";
$AccountNumber = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd";
$BranchCode = "198765";
$apiKey = "[YOUR API KEY]";
$inputString = $PayoutId . $siteCode . (int)($amount * 100) . $MerchantReference . $CustomerBankReference . $IsRtc . $NotifyUrl . $BankGroupId . $AccountNumber . $BranchCode . $apiKey;
$calculatedHashResult = GenerateRequestHashCheck($inputString);
echo "Hashcheck: " . $calculatedHashResult;
}
function GenerateRequestHashCheck($inputString) {
$stringToHash = strtolower($inputString);
echo "Before Hashcheck: " . $stringToHash;
return GetSha512Hash($stringToHash);
}
function getSha512Hash($stringToHash) {
$bytes = hash('sha512', $stringToHash, true);
$hexString = bin2hex($bytes);
return $hexString;
}
GeneratePayoutVerificationRequestHash();
?>
function GeneratePayoutVerificationRequestHash() {
var PayoutId = "00000000-0000-0000-0000-000000000000";
var siteCode = "[YOUR SITE CODE]";
var amount = 17.15;
var MerchantReference = "123";
var CustomerBankReference = "ABC123";
var IsRtc = false;
var NotifyUrl = "https://requestcatcher.com/";
var BankGroupId = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B";
var AccountNumber = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd";
var BranchCode = "198765";
var apiKey = "[YOUR API KEY]";
var inputString = PayoutId + siteCode + Math.floor(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;
}
GeneratePayoutVerificationRequestHash();
import hashlib
def GeneratePayoutVerificationRequestHash():
PayoutId = "00000000-0000-0000-0000-000000000000"
siteCode = "[YOUR SITE CODE]"
amount = 17.15
MerchantReference = "123"
CustomerBankReference = "ABC123"
IsRtc = False
NotifyUrl = "https://requestcatcher.com/"
BankGroupId = "13999FA-3A32-4E3D-82F0-A1DF7E9E4F7B"
AccountNumber = "ff313a955ad9a8ddff32cb734d49fbcddd8eeb1e235009d59a801bc5af78270cfd"
BranchCode = "198765"
apiKey = "[YOUR API KEY]"
inputString = "{}{}{}{}{}{}{}{}{}{}".format(PayoutId, siteCode, int(amount*100), MerchantReference, CustomerBankReference, IsRtc, NotifyUrl, BankGroupId, AccountNumber, BranchCode,apiKey)
calculatedHashResult = GenerateRequestHashCheck(inputString)
print("Hashcheck: {}".format(calculatedHashResult))
def GenerateRequestHashCheck(inputString):
stringToHash = inputString.lower()
print("Before Hashcheck: {}".format(stringToHash))
return GetSha512Hash(stringToHash)
def GetSha512Hash(stringToHash):
sha512 = hashlib.sha512()
sha512.update(stringToHash.encode("utf-8"))
return sha512.hexdigest()
GeneratePayoutVerificationRequestHash()
Response
A successful verification should return a JSON object as described below.
Property | Type | Description |
---|---|---|
PayoutId | Guid | The unique identifier that should be used to identify the payout. |
IsVerified | bool | Whether payout has been verified. |
AccountNumberDecryptionKey | String | The AES decryption key which will be used to decrypt the destination account number. |
Reason | String (50) | The reason why the payout verification was not valid. |
{
"PayoutId": "00000000-0000-0000-0000-000000000000",
"IsVerified": true,
"AccountNumberDecryptionKey": "C@OQN8oW9I8DSuKS$jfd",
"Reason": ""
}
Updated 11 months ago