Webhooks is an API concept that enables applications to automatically communicate with each other without constant polling. Monnify integration sends notifications to a URL on the merchants’ server when specific events such as when payments are being received or when settlements are made to your account, allowing further actions such as sending an email or providing value to the user.
Scroll down to the Developer page on the left navigation menu and then proceed to the Webhook URLs section to input your URL’s i.e Transaction Completion, Refund Completion, Disbursement and Settlement. Once you've pasted your webhook details click save and you are good to go!
Below is a sample image on how to input your urls on the monnify dashboard
Monnify supports webhooks for various events like card transactions, settlement and disbursement completion, and refunds.
To implement webhooks on your Monnify integration, it is recommended to follow certain best practices such as validating transaction hash, whitelisting Monnify's IP address, checking for duplicate notifications, and processing complex logic after acknowledging receipt of the notification with a 200 HTTP status code. These practices ensure the integrity and security of the payload, prevent unauthorized requests, avoid redundant processing, and prevent time-out issues.
As part of the Monnify integration, notifications are automatically sent to your system when certain actions are completed. These notifications trigger corresponding activities on your system, and you can specify URLs for certain activities on your integration.
The notifications include an event-type property that indicates what action has taken place, as well as event data containing details of the event.
Supported notification event types on Monnify include:
Successful Disbursement (for disbursement transactions with a successful definite status).
Settlement Completion (for successfully processed settlements to your bank account or wallet).
Mandate Status Change (This is sent when the status of a mandate changes from PENDING to FAILED or CANCELLED or ACTIVATED etc).
Wallet activity notification (For notifying merchants of credits and debits to their Main or SubWallets).
A typical event notification structure is of the format:
1{
2 "eventType": "type_of_event",
3 "eventData": {
4 "prop1": "value1",
5 "prop2": "value2"
6 }
7}
As a security measure, Monnify computes a hash of the request body whenever it sends a notification and includes it in the request header with the key 'monnify-signature'. To ensure the notification is valid and authorized, you should also calculate the hash and compare it to the one sent by Monnify before accepting or acting on the notification.
To calculate the hash, you can use a SHA-512 encoding of your client secret key and the object of the request body. The formula is: SHA-512(client secret key + object of request body).
Sample Client Key: 91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y
Sample Request:
1{
2 "eventData": {
3 "product": {
4 "reference": "111222333",
5 "type": "OFFLINE_PAYMENT_AGENT"
6 },
7 "transactionReference": "MNFY|76|20211117154810|000001",
8 "paymentReference": "0.01462001097368737",
9 "paidOn": "17/11/2021 3:48:10 PM",
10 "paymentDescription": "Mockaroo Jesse",
11 "metaData": {},
12 "destinationAccountInformation": {},
13 "paymentSourceInformation": {},
14 "amountPaid": 78000,
15 "totalPayable": 78000,
16 "offlineProductInformation": {
17 "code": "41470",
18 "type": "DYNAMIC"
19 },
20 "cardDetails": {},
21 "paymentMethod": "CASH",
22 "currency": "NGN",
23 "settlementAmount": 77600,
24 "paymentStatus": "PAID",
25 "customer": {
26 "name": "Mockaroo Jesse",
27 "email": "[email protected]"
28 }
29 },
30 "eventType": "SUCCESSFUL_TRANSACTION"
31}
Hashed Value:
f04fb635e04d71648bd3cc7999003da6861483342c856d05ddfa9b2dafacb87 3b0de1d0f8f67405d0010b4348b721c49fa171d317972618debba6b638aedcd3c
1const { sha512 } = require("js-sha512");
2
3const DEFAULT_MERCHANT_CLIENT_SECRET = "91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y";
4
5const computeHash = (requestBody) => {
6const result = sha512.hmac(DEFAULT_MERCHANT_CLIENT_SECRET, requestBody);
7return result;
8};
9
10const stringifiedRequestBody = JSON.stringify({
11eventData: {
12 product: {
13 reference: "111222333",
14 type: "OFFLINE_PAYMENT_AGENT",
15 },
16 transactionReference: "MNFY|76|20211117154810|000001",
17 paymentReference: "0.01462001097368737",
18 paidOn: "17/11/2021 3:48:10 PM",
19 paymentDescription: "Mockaroo Jesse",
20 metaData: {},
21 destinationAccountInformation: {},
22 paymentSourceInformation: {},
23 amountPaid: 78000,
24 totalPayable: 78000,
25 offlineProductInformation: {
26 code: "41470",
27 type: "DYNAMIC",
28 },
29 cardDetails: {},
30 paymentMethod: "CASH",
31 currency: "NGN",
32 settlementAmount: 77600,
33 paymentStatus: "PAID",
34 customer: {
35 name: "Mockaroo Jesse",
36 email: "[email protected]",
37 },
38},
39eventType: "SUCCESSFUL_TRANSACTION",
40});
41
42const computedHash = computeHash(stringifiedRequestBody);
43console.log("Computed hash:", computedHash);
1<?php
2
3class CustomTransactionHashUtil
4{
5 public static function computeSHA512TransactionHash($stringifiedData, $clientSecret)
6 {
7 return hash_hmac('sha512', $stringifiedData, $clientSecret);
8 }
9}
10
11$DEFAULT_MERCHANT_CLIENT_SECRET = '91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y';
12
13$data = json_encode([
14 "eventData" => [
15 "product" => [
16 "reference" => "111222333",
17 "type" => "OFFLINE_PAYMENT_AGENT",
18 ],
19 "transactionReference" => "MNFY|76|20211117154810|000001",
20 "paymentReference" => "0.01462001097368737",
21 "paidOn" => "17/11/2021 3:48:10 PM",
22 "paymentDescription" => "Mockaroo Jesse",
23 "metaData" => new stdClass(),
24 "destinationAccountInformation" => new stdClass(),
25 "paymentSourceInformation" => new stdClass(),
26 "amountPaid" => 78000,
27 "totalPayable" => 78000,
28 "offlineProductInformation" => [
29 "code" => "41470",
30 "type" => "DYNAMIC",
31 ],
32 "cardDetails" => new stdClass(),
33 "paymentMethod" => "CASH",
34 "currency" => "NGN",
35 "settlementAmount" => 77600,
36 "paymentStatus" => "PAID",
37 "customer" => [
38 "name" => "Mockaroo Jesse",
39 "email" => "[email protected]",
40 ],
41 ],
42 "eventType" => "SUCCESSFUL_TRANSACTION",
43], JSON_UNESCAPED_SLASHES);
44
45$computedHash = CustomTransactionHashUtil::computeSHA512TransactionHash(
46 $data,
47 $DEFAULT_MERCHANT_CLIENT_SECRET
48);
49
50echo $computedHash;
51
1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.security.InvalidKeyException;
4import java.security.NoSuchAlgorithmException;
5import java.security.SignatureException;
6import java.util.Formatter;
7
8public class TransactionHashUtil {
9
10 private static final String HMAC_SHA512 = "HmacSHA512";
11
12 private static String toHexString(byte[] bytes) {
13 try (Formatter formatter = new Formatter()) {
14 for (byte b : bytes) {
15 formatter.format("%02x", b);
16 }
17 return formatter.toString();
18 }
19 }
20
21 /**
22 * Computes an HMAC-SHA512 hash for the given data using the merchant client secret.
23 *
24 * @param data The stringified JSON payload
25 * @param merchantClientSecret The merchant's client secret key
26 * @return The computed HMAC-SHA512 hash as a lowercase hex string
27 * @throws SignatureException If the signature computation fails
28 * @throws NoSuchAlgorithmException If HmacSHA512 is not available
29 * @throws InvalidKeyException If the provided key is invalid
30 */
31 public static String computeHMAC512TransactionHash(String data, String merchantClientSecret)
32 throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
33
34 SecretKeySpec secretKeySpec = new SecretKeySpec(merchantClientSecret.getBytes(), HMAC_SHA512);
35 Mac mac = Mac.getInstance(HMAC_SHA512);
36 mac.init(secretKeySpec);
37
38 byte[] rawHmac = mac.doFinal(data.getBytes());
39 return toHexString(rawHmac);
40 }
41}
42
It’s highly recommended you do the following when processing webhook notifications from us.
How would you rate your experience?