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
| Field | Data Type | Description |
|---|---|---|
| Payments | Array of Payment | Payment data (see below) |
| Hash | String | Legacy hash — use X-Signature header instead |
Payment Fields
| Field | Data Type | Description |
|---|---|---|
| PaymentId | Integer | Unique id of the payment |
| BillPayReference | String | Unique reference containing the biller code |
| BankReference | String | Payment gateway reference |
| PaidDate | Date + Time | Payment date |
| MemberNumber | String | Member identifier |
| MemberName | String | Name of the member |
| ProductCode | String | Product code |
| ProductPrice | Decimal | Price paid for the product |
| ProductDepartment | String | Product department (may not be present) |
Validating with HMAC-SHA256 (Recommended)
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.
- C#
- Java
- PHP
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);
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public static String computeHmacSHA256(String payload, String secret)
throws Exception {
SecretKeySpec secretKeySpec = new
SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKeySpec);
byte[] hmacBytes = mac.doFinal(payload.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(hmacBytes);
}
function computeHmacSHA256($payload, $secret) {
$hash = hash_hmac('sha256', $payload, $secret, true);
return base64_encode($hash);
}
Validating with SHA256 Hash (Legacy)
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:
- Concatenate the values of all fields in each payment in the message
- Append the Secret Key
- SHA256 hash the string and output as lowercase hexadecimal
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;
}
}
?>