Webhooks

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.



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.

Monnify Webhook Events and Structure

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

  1. Successful Collection (for successful payments made on your account),
  2. Successful Disbursement (for disbursement transactions with a successful definite status),
  3. Failed Disbursement (for failed disbursement transactions),
  4. Reversed Disbursement (for reversed disbursement transactions),
  5. Successful Refund (for successfully processed initiated refunds),
  6. Failed Refund (for failed initiated refunds), and
  7. Settlement Completion (for successfully processed settlements to your bank account or wallet).
  8. Mandate Status Change (This is sent when the status of a mandate changes from PENDING to FAILED or CANCELLED or ACTIVATED etc)


Structure and Sample

A typical event notification structure is of the format:

json
Copy button
{
"eventType": "type_of_event",
"eventData": {
"prop1": "value1",
"prop2": "value2"
}
}

Transaction Hash Computation

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 Examples:

Sample Client Key: 91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y

Sample Request:

json
Copy button
{
"eventData": {
"product": {
"reference": "111222333",
"type": "OFFLINE_PAYMENT_AGENT"
},
"transactionReference": "MNFY|76|20211117154810|000001",
"paymentReference": "0.01462001097368737",
"paidOn": "17/11/2021 3:48:10 PM",
"paymentDescription": "Mockaroo Jesse",
"metaData": {},
"destinationAccountInformation": {},
"paymentSourceInformation": {},
"amountPaid": 78000,
"totalPayable": 78000,
"offlineProductInformation": {
"code": "41470",
"type": "DYNAMIC"
},
"cardDetails": {},
"paymentMethod": "CASH",
"currency": "NGN",
"settlementAmount": 77600,
"paymentStatus": "PAID",
"customer": {
"name": "Mockaroo Jesse",
"email": "[email protected]"
}
},
"eventType": "SUCCESSFUL_TRANSACTION"
}

Hashed Value:

f04fb635e04d71648bd3cc7999003da6861483342c856d05ddfa9b2dafacb87 3b0de1d0f8f67405d0010b4348b721c49fa171d317972618debba6b638aedcd3c


Computing Hash in Nodejs

javascript
Copy button
const sha512 = require('js-sha512').sha512;
const DEFAULT_MERCHANT_CLIENT_SECRET = '91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y'
const computeHash = (requestBody) => {
const result = sha512.hmac(DEFAULT_MERCHANT_CLIENT_SECRET, requestBody)
return result
}
const stringifiedRequestBody = '{"eventData":{"product":{"reference":"111222333","type":"OFFLINE_PAYMENT_AGENT"},"transactionReference":"MNFY|76|20211117154810|000001","paymentReference":"0.01462001097368737","paidOn":"17/11/2021 3:48:10 PM","paymentDescription":"Mockaroo Jesse","metaData":{},"destinationAccountInformation":{},"paymentSourceInformation":{},"amountPaid":78000,"totalPayable":78000,"offlineProductInformation":{"code":"41470","type":"DYNAMIC"},"cardDetails":{},"paymentMethod":"CASH","currency":"NGN","settlementAmount":77600,"paymentStatus":"PAID","customer":{"name":"Mockaroo Jesse","email":"[email protected]"}},"eventType":"SUCCESSFUL_TRANSACTION"}';
const computedHash = computeHash(stringifiedRequestBody);
console.log("Computed hash", computedHash);


Computing Hash in PHP

php
Copy button
<?php
class CustomTransactionHashUtil {
public static function computeSHA512TransactionHash($stringifiedData, $clientSecret) {
$computedHash = hash_hmac('sha512', $stringifiedData, $clientSecret);
return $computedHash;
}
}
$DEFAULT_MERCHANT_CLIENT_SECRET = '91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y';
$data = '{"eventData":{"product":{"reference":"111222333","type":"OFFLINE_PAYMENT_AGENT"},"transactionReference":"MNFY|76|20211117154810|000001","paymentReference":"0.01462001097368737","paidOn":"17/11/2021 3:48:10 PM","paymentDescription":"Mockaroo Jesse","metaData":{},"destinationAccountInformation":{},"paymentSourceInformation":{},"amountPaid":78000,"totalPayable":78000,"offlineProductInformation":{"code":"41470","type":"DYNAMIC"},"cardDetails":{},"paymentMethod":"CASH","currency":"NGN","settlementAmount":77600,"paymentStatus":"PAID","customer":{"name":"Mockaroo Jesse","email":"[email protected]"}},"eventType":"SUCCESSFUL_TRANSACTION"}';
$computedHash = CustomTransactionHashUtil::computeSHA512TransactionHash($data, $DEFAULT_MERCHANT_CLIENT_SECRET);
echo $computedHash;
?>


Computing Hash in Java

php
Copy button
<?php
public class TransactionHashUtil {
private static final String HMAC_SHA512 = "HmacSHA512";
private static String toHexString(byte[] bytes) {
Formatter formatter = new Formatter();
for (byte b : bytes) {
formatter.format("%02x", b);
}
return formatter.toString();
}
public String calculateHMAC512TransactionHash(String data, String merchantClientSecret)
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException
{
SecretKeySpec secretKeySpec = new SecretKeySpec(merchantClientSecret.getBytes(), HMAC_SHA512);
Mac mac = Mac.getInstance(HMAC_SHA512);
mac.init(secretKeySpec);
return toHexString(mac.doFinal(data.getBytes()));
}
}


Best Practices

It’s highly recommended you do the following when processing webhook notifications from us.

  1. Transaction Hash Validation: This is applicable by default on our transaction notification webhook. A hash of some properties in the request payload is computed, and you can validate this on your server by computing the same hash and comparing outputs.
  2. Whitelist Monnify's webhook IP address: To prevent requests from un-authorized origins, you can whitelist our IP address and only honor requests from this IP. Webhook notifications from Monnify will come from the following IP addresses - 35.242.133.146.
  3. Check for duplicate notifications: It’s important to keep track of all notifications you’ve received. When a new notification is received, always check that this has not been processed before giving value so as not to give double value to customers. A resend of already processed notification can happen if we do not get a 200 HTTP Status code, or in the case of a request time out.
  4. Process Complex Logic After Responding to Monnify: If your application will perform complex or time consuming logic with received notifications, this might lead to a time out between Monnify and your system, hence leading to a resend. For this reason, it’s recommended you immediately acknowledge receipt of the notification by returning a 200 HTTP Status code, and then perform your long processing activities.
Was this page helpful? Tell us what you think..
Copyright © 2024 Monnify