Skip to main content

Payment Webhooks

A biller can be notified that a payment happened by having data posted to a URL on their site (a webhook). You can choose to receive payment data after every transaction or on a daily basis.

Contact the system admin to provide your webhook URL. Once configured, you will be provided with a secret key for verifying incoming data.

Webhook Payload

FieldData TypeDescription
PaymentsArray of PaymentPayment data (see below)
HashStringLegacy hash — use X-Signature header instead

Payment Fields

FieldData TypeDescription
PaymentIdIntegerUnique id of the payment
BillPayReferenceStringUnique reference containing the biller code
BankReferenceStringPayment gateway reference
PaidDateDate + TimePayment date
MemberNumberStringMember identifier
MemberNameStringName of the member
ProductCodeStringProduct code
ProductPriceDecimalPrice paid for the product
ProductDepartmentStringProduct department (may not be present)

The posted message includes an X-Signature HTTP request header which is an HMAC-based method of authenticating the source of the message.

Check that the X-Signature header value matches the HMAC-SHA256 of the message contents using your secret key.

using System.Security.Cryptography;

public static string ComputeHmacSHA256(string payload, string secret)
{
var keyBytes = Encoding.UTF8.GetBytes(secret);
var payloadBytes = Encoding.UTF8.GetBytes(payload);
using var hmac = new HMACSHA256(keyBytes);
var hash = hmac.ComputeHash(payloadBytes);
return Convert.ToBase64String(hash);
}

Validating with SHA256 Hash (Legacy)

note

There is no need to do this if you have already validated using the HMAC-SHA256 method above.

The posted message includes a Hash field that must be validated:

  1. Concatenate the values of all fields in each payment in the message
  2. Append the Secret Key
  3. SHA256 hash the string and output as lowercase hexadecimal
warning

The order of fields when concatenating is critical. If the wrong order is used, the hash will not validate correctly.

Example

{
"Payments": [
{
"PaymentId": 172,
"BillPayReference": "FAKE-181211122304615",
"BankReference": "9796",
"PaidDate": "11-Dec-2018 12:24:51",
"MemberNumber": "T00001",
"MemberName": "John Doe",
"ProductCode": "LN",
"ProductPrice": 3.21,
"ProductDepartment": "Sales"
},
{
"PaymentId": 245,
"BillPayReference": "FAKE-18121112212345",
"BankReference": "",
"PaidDate": "11-Dec-2018 13:14:11",
"MemberNumber": "K00123",
"MemberName": "Abby Fijngold",
"ProductCode": "MP",
"ProductPrice": 30.00,
"ProductDepartment": "Sales"
}
],
"Hash": "660ad6a83bdd9993a2ef44e3b02098a6ce62763a145eccf1f669951bdd53ce40"
}

Step 1: Concatenate all field values from each payment:

172FAKE-181211122304615979611-Dec-2018 12:24:51T00001John DoeLN3.21Sales245FAKE-1812111221234511-Dec-2018 13:14:11K00123Abby FijngoldMP30.00Sales

Step 2: Append the secret key:

172FAKE-181211122304615979611-Dec-2018 12:24:51T00001John DoeLN3.21Sales245FAKE-1812111221234511-Dec-2018 13:14:11K00123Abby FijngoldMP30.00Sales415b654f-3544-4281-a91e-051e710bfb8d

Step 3: SHA256 hash (lowercase):

660ad6a83bdd9993a2ef44e3b02098a6ce62763a145eccf1f669951bdd53ce40

PHP Code Snippet

<?php
// Takes raw data from the request
$json = file_get_contents('php://input');

// Converts it into a PHP object
$data = json_decode($json);

$payments = new Payments;
$payments->addPayments($data);

Class Payments {
private $payments = array();

public function addPayments($data){
$hash = $data->Hash;
$plainText = '';

foreach($data->Payments as $payment) {
$paymentId = $payment->PaymentId;
$billpayRef = $payment->BillPayReference;
$bankReference = $payment->BankReference;
$paidDate = $payment->PaidDate;
$memberNumber = $payment->MemberNumber;
$memberName = $payment->MemberName;
$productCode = $payment->ProductCode;
$productPrice = number_format($payment->ProductPrice, 2, '.', '');

// department field may or may not be present
if(isset($payment->ProductDepartment)){
$productDepartment = $payment->ProductDepartment;
} else {
$productDepartment = '';
}

$plainText .= $paymentId.$billpayRef.$bankReference
.$paidDate.$memberNumber.$memberName
.$productCode.$productPrice.$productDepartment;

$sqlData[] = "('".$paymentId."', '".$billpayRef."', '"
.$bankReference."', '".$paidDate."', '".$memberNumber
."', '".$memberName."', '".$productCode."', '"
.$productPrice."', '".$productDepartment."')";
}

/* Read your Secret Key from config */
$verified = Hash::verify($plainText, SECRETKEY, $hash);

if ($verified) {
$query = "INSERT INTO tblPayment
(PaymentId, BillPayRef, BankRef, PaidDate,
MemberNumber, MemberName, ProductCode,
ProductPrice, ProductDepartment)
VALUES ".implode(',', $sqlData);
}
}
}

class Hash
{
public static function make($plainText, $secretKey) {
$string = $plainText.$secretKey;
$hash = hash("sha256", $string);
return strtolower($hash);
}

public static function verify($values, $key, $hash)
{
return self::make($values, $key) === $hash;
}
}
?>