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
1{
2 "eventType": "SUCCESSFUL_TRANSACTION",
3 "eventData": {
4 "product": {
5 "reference": "1636106097661",
6 "type": "RESERVED_ACCOUNT"
7 },
8 "transactionReference": "MNFY|04|20211117112842|000170",
9 "paymentReference": "MNFY|04|20211117112842|000170",
10 "paidOn": "2021-11-17 11:28:42.615",
11 "paymentDescription": "Adm",
12 "metaData": {},
13 "paymentSourceInformation": [
14 {
15 "bankCode": "",
16 "amountPaid": 3000,
17 "accountName": "Monnify Limited",
18 "sessionId": "e6cV1smlpkwG38Cg6d5F9B2PRnIq5FqA",
19 "accountNumber": "0065432190"
20 }
21 ],
22 "destinationAccountInformation": {
23 "bankCode": "232",
24 "bankName": "Sterling bank",
25 "accountNumber": "6000140770"
26 },
27 "amountPaid": 3000,
28 "totalPayable": 3000,
29 "cardDetails": {},
30 "paymentMethod": "ACCOUNT_TRANSFER",
31 "currency": "NGN",
32 "settlementAmount": "2990.00",
33 "paymentStatus": "PAID",
34 "customer": {
35 "name": "John Doe",
36 "email": "[email protected]"
37 }
38 }
39}
1{
2 "eventType": "SUCCESSFUL_DISBURSEMENT",
3 "eventData": {
4 "amount": 10,
5 "transactionReference": "MFDS|20210317032332|002431",
6 "fee": 8,
7 "transactionDescription": "Approved or completed successfully",
8 "destinationAccountNumber": "0068687503",
9 "sessionId": "090405210317032336726272971260",
10 "createdOn": "17/03/2021 3:23:32 AM",
11 "destinationAccountName": "DAMILARE SAMUEL OGUNNAIKE",
12 "reference": "ref1615947809303",
13 "destinationBankCode": "232",
14 "completedOn": "17/03/2021 3:23:38 AM",
15 "narration": "This is a quite long narration",
16 "currency": "NGN",
17 "destinationBankName": "Sterling bank",
18 "status": "SUCCESS"
19 }
20}
1{
2 "eventType": "FAILED_DISBURSEMENT",
3 "eventData": {
4 "amount": 17100,
5 "transactionReference": "MFDS10620240708214001015343FR7PL8",
6 "fee": 20,
7 "transactionDescription": "You do not have sufficient balance to process this request. Please fund your account and try again.",
8 "destinationAccountNumber": "8088524531",
9 "sessionId": "",
10 "createdOn": "08/07/2024 9:40:02 PM",
11 "destinationAccountName": "MARVELOUS BENJI",
12 "reference": "MF240708214000166415",
13 "destinationBankCode": "305",
14 "completedOn": "08/07/2024 9:40:07 PM",
15 "narration": "AOlifepurse1260077196647628800Transaction",
16 "currency": "NGN",
17 "destinationBankName": "OPAY",
18 "status": "FAILED"
19 }
20}
1{
2 "eventType": "REVERSED_DISBURSEMENT",
3 "eventData": {
4 "transactionReference": "MFDS33920240513211815009133P47MKU",
5 "reference": "662d2dcf22132ea227db164e-1715631494637",
6 "narration": "Fund Transfer",
7 "currency": "NGN",
8 "amount": 145708,
9 "status": "REVERSED",
10 "fee": 8,
11 "destinationAccountNumber": "8088523251",
12 "destinationAccountName": "Marvelous Benji",
13 "destinationBankCode": "305",
14 "sessionId": "090405240513211816637369129129",
15 "createdOn": "13/05/2023 9:18:16 PM",
16 "completedOn": "13/05/2023 9:18:19 PM"
17 }
18}
1{
2 "eventType": "SUCCESSFUL_REFUND",
3 "eventData": {
4 "merchantReason":"defective goods",
5 "transactionReference":"MNFY|20190816083102|000021",
6 "completedOn":"14/04/2021 4:24:05 PM",
7 "refundStatus":"COMPLETED",
8 "customerNote":"defects",
9 "createdOn":"14/04/2021 4:23:37 PM",
10 "refundReference":"ref001",
11 "refundAmount":10:00
12 }
13}
1{
2 "eventType": "FAILED_REFUND",
3 "eventData": {
4 "merchantReason":"defective goods",
5 "transactionReference":"MNFY|20190816083102|000021",
6 "completedOn":"14/04/2021 4:24:05 PM",
7 "refundStatus":"FAILED",
8 "customerNote":"defects",
9 "createdOn":"14/04/2021 4:23:37 PM",
10 "refundReference":"ref001",
11 "refundAmount":10:00
12 }
13}
1{
2 "eventType": "SETTLEMENT",
3 "eventData": {
4 "amount": "1199.00",
5 "settlementTime": "11/11/2021 02:29:00 PM",
6 "settlementReference": "LB8HG1PNZT4ATJGZXQBY",
7 "destinationAccountNumber": "6000000249",
8 "destinationBankName": "Fidelity Bank",
9 "destinationAccountName": "Teamapt Limited234",
10 "transactionsCount": 1,
11 "transactions": [
12 {
13 "product": {
14 "reference": "2134565wda",
15 "type": "2134565wda"
16 },
17 "transactionReference": "MNFY|26|20211111142601|000001",
18 "paymentReference": "MNFY|26|20211111142601|000001",
19 "paidOn": "11/11/2021 02:26:02 PM",
20 "paymentDescription": "Seg",
21 "accountPayments": [
22 {
23 "bankCode": "000014",
24 "amountPaid": "1234.00",
25 "accountName": "Okeke Chimezie",
26 "accountNumber": "******1070"
27 }
28 ],
29 "amountPaid": "1234.00",
30 "totalPayable": "1234.00",
31 "accountDetails": {
32 "bankCode": "000014",
33 "amountPaid": "1234.00",
34 "accountName": "Okeke Chimezie",
35 "accountNumber": "******1070"
36 },
37 "cardDetails": {},
38 "paymentMethod": "ACCOUNT_TRANSFER",
39 "currency": "NGN",
40 "paymentStatus": "PAID",
41 "customer": {
42 "name": "Segun Adeponle",
43 "email": "[email protected]"
44 }
45 }
46 ]
47 }
48}
1{
2 "eventType": "SUCCESSFUL_TRANSACTION",
3 "eventData": {
4 "product": {
5 "reference": "MNF-Tl9Noo0G48000890",
6 "type": "OFFLINE_PAYMENT_AGENT"
7 },
8 "transactionReference": "MNFY|76|20230830171357|000252",
9 "invoiceReference": "MNF-Tl9Noo0G48000890",
10 "paymentReference": "MNF-Tl9Noo0G48000890",
11 "paidOn": "30/08/2023 5:13:57 PM",
12 "paymentDescription": "adron",
13 "metaData":{
14 "phoneNumber":"08088523241",
15 "name":"Khalid"
16 },
17 "destinationAccountInformation": {},
18 "paymentSourceInformation": {},
19 "amountPaid": 15000,
20 "totalPayable": 15000,
21 "offlineProductInformation": {
22 "amount": 15000,
23 "code": "56417",
24 "type": "INVOICE"
25 },
26 "cardDetails": {},
27 "paymentMethod": "CASH",
28 "currency": "NGN",
29 "settlementAmount": 14990,
30 "paymentStatus": "PAID",
31 "customer": {
32 "name": "David Customer",
33 "email": "[email protected]"
34 }
35 }
36}
1{
2 "eventType": "REJECTED_PAYMENT",
3 "eventData": {
4 "metaData": "{"name":"Marvelous","age":"90"}",
5 "product": {
6 "reference": "MNFY|PAYREF|GENERATED|1687798434397393735",
7 "type": "WEB_SDK"
8 },
9 "amount": 100,
10 "paymentSourceInformation": {
11 "bankCode": "50515",
12 "amountPaid": 40,
13 "accountName": "MARVELOUS BENJI",
14 "sessionId": "090405230626180003067844645188",
15 "accountNumber": "5141901487"
16 },
17 "transactionReference": "MNFY|85|20230626175354|041855",
18 "created_on": "2023-06-26 17:53:55.0",
19 "paymentReference": "MNFY|PAYREF|GENERATED|1687798434397393735",
20 "paymentRejectionInformation": {
21 "bankCode": "035",
22 "destinationAccountNumber": "7023576853",
23 "bankName": "Wema bank",
24 "rejectionReason": "UNDER_PAYMENT",
25 "expectedAmount": 100
26 },
27 "paymentDescription": "lets pay",
28 "customer": {
29 "name": "Marvelous Benji",
30 "email": "[email protected]"
31 }
32 }
33}
1{
2 "eventType": "MANDATE_UPDATE",
3 "eventData": {
4 "customerAddress": "Everywhere is an address",
5 "endDate": "2024-12-31 08:00:00.0",
6 "customerEmailAddress": "[email protected]",
7 "customerAccountName": "SAMUEL DAMILARE OGUNNAIKE",
8 "customerAccountNumber": "2191406799",
9 "customerAccountBankCode": "057",
10 "customerName": "Damilare Ogunnaike",
11 "mandateDescription": "Testing Monnify Mandate",
12 "externalMandateReference": "mfy-mandate-102",
13 "mandateStatus": "CANCELLED",
14 "mandateAmount": 100000,
15 "autoRenew": false,
16 "mandateCode": "MTDD|01J3GRJH8D58B20VNX1E6GSY1N",
17 "contractCode": "626689863141",
18 "customerPhoneNumber": "08166189142",
19 "startDate": "2024-07-24 08:00:00.0"
20 }
21}
1 {
2 "eventType": "ACCOUNT_ACTIVITY",
3 "eventData": {
4 "accountType": "MAIN",
5 "accountName": "Test01",
6 "accountNumber": "8016472829",
7 "accountNuban": null,
8 "activityType": "TRANSACTION",
9 "amount": 100,
10 "currency": "566",
11 "balanceBefore": 862.68,
12 "balanceAfter": 962.68,
13 "reference": "MFY_WTP_TRF_2MPT61CFP_1896839989128998912_CBA_CREDIT_0_CREDIT_0",
14 "narration": " MFY-WT/#/TRF|2MPT61cfp|1896839989128998912_CBA_CREDIT_0/#/2025-03-04/#/VA-6927004623/#/From-Moniepoint Microfinance Bank/#/Test User/#/5744000051",
15 "activityTime": "2025-03-04 10:27:AM"
16 },
17 "metaData": {
18 "senderAccount": "Monnify Service",
19 "sourceAccountName": null,
20 "sourceAccountNumber": null,
21 "sourceBankCode": null,
22 "sourceBankName": null
23 }
24}
1{
2"eventType": "LOW_BALANCE_ALERT",
3"eventData": {
4 "transactionTime": "2025-09-01T23:13:19Z",
5 "merchantCode": "99ZYAFM0F3CY",
6 "walletAccountNumber": "8023759978",
7 "walletBalance": 0,
8 "lowBalanceThreshold": 2000,
9 "currency": "NGN",
10 "description": "Your wallet balance has dropped below the configured threshold. Please fund your account."
11 }
12}
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:
f04fb635e04d71648bd3cc7999003da6861483342c856d05ddfa9b2dafacb873b0de1d0f8f67405d0010b4348b721c49fa171d317972618debba6b638aedcd3c
1const { sha512 } = require("js-sha512");
2
3const DEFAULT_MERCHANT_CLIENT_SECRET = "91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y";
4
5/**
6* Computes the HMAC-SHA512 hash of the given request body.
7* @param {string} requestBody - The stringified request body (JSON payload).
8* @returns {string} - The computed hash as a hex string.
9*/
10const computeHash = (requestBody) => {
11return sha512.hmac(DEFAULT_MERCHANT_CLIENT_SECRET, requestBody);
12};
13
14// Sample request body payload
15const stringifiedRequestBody = JSON.stringify(
16{
17 eventData: {
18 product: {
19 reference: "111222333",
20 type: "OFFLINE_PAYMENT_AGENT",
21 },
22 transactionReference: "MNFY|76|20211117154810|000001",
23 paymentReference: "0.01462001097368737",
24 paidOn: "17/11/2021 3:48:10 PM",
25 paymentDescription: "Mockaroo Jesse",
26 metaData: {},
27 destinationAccountInformation: {},
28 paymentSourceInformation: {},
29 amountPaid: 78000,
30 totalPayable: 78000,
31 offlineProductInformation: {
32 code: "41470",
33 type: "DYNAMIC",
34 },
35 cardDetails: {},
36 paymentMethod: "CASH",
37 currency: "NGN",
38 settlementAmount: 77600,
39 paymentStatus: "PAID",
40 customer: {
41 name: "Mockaroo Jesse",
42 email: "[email protected]",
43 },
44 },
45 eventType: "SUCCESSFUL_TRANSACTION",
46},
47null,
482 // pretty-print spacing (optional)
49);
50
51const computedHash = computeHash(stringifiedRequestBody);
52console.log("Computed hash:", computedHash);
53
1<?php
2
3class CustomTransactionHashUtil
4{
5 /**
6 * Computes an HMAC-SHA512 hash for a given JSON string and client secret.
7 *
8 * @param string $stringifiedData The stringified JSON payload.
9 * @param string $clientSecret The merchant client secret.
10 *
11 * @return string The computed HMAC-SHA512 hash.
12 */
13 public static function computeSHA512TransactionHash(string $stringifiedData, string $clientSecret): string
14 {
15 return hash_hmac('sha512', $stringifiedData, $clientSecret);
16 }
17}
18
19$DEFAULT_MERCHANT_CLIENT_SECRET = '91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y';
20
21// Build the payload as an array (safer and more readable)
22$payload = [
23 "eventData" => [
24 "product" => [
25 "reference" => "111222333",
26 "type" => "OFFLINE_PAYMENT_AGENT",
27 ],
28 "transactionReference" => "MNFY|76|20211117154810|000001",
29 "paymentReference" => "0.01462001097368737",
30 "paidOn" => "17/11/2021 3:48:10 PM",
31 "paymentDescription" => "Mockaroo Jesse",
32 "metaData" => new stdClass(),
33 "destinationAccountInformation" => new stdClass(),
34 "paymentSourceInformation" => new stdClass(),
35 "amountPaid" => 78000,
36 "totalPayable" => 78000,
37 "offlineProductInformation" => [
38 "code" => "41470",
39 "type" => "DYNAMIC",
40 ],
41 "cardDetails" => new stdClass(),
42 "paymentMethod" => "CASH",
43 "currency" => "NGN",
44 "settlementAmount" => 77600,
45 "paymentStatus" => "PAID",
46 "customer" => [
47 "name" => "Mockaroo Jesse",
48 "email" => "[email protected]",
49 ],
50 ],
51 "eventType" => "SUCCESSFUL_TRANSACTION",
52];
53
54// Convert payload to JSON (stringified body)
55$stringifiedData = json_encode($payload, JSON_UNESCAPED_SLASHES);
56
57// Compute hash
58$computedHash = CustomTransactionHashUtil::computeSHA512TransactionHash(
59 $stringifiedData,
60 $DEFAULT_MERCHANT_CLIENT_SECRET
61);
62
63echo "Computed Hash: " . $computedHash . PHP_EOL;
64
1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.nio.charset.StandardCharsets;
4import java.security.InvalidKeyException;
5import java.security.NoSuchAlgorithmException;
6import java.security.SignatureException;
7import java.util.Formatter;
8
9public class TransactionHashUtil {
10
11 private static final String HMAC_SHA512 = "HmacSHA512";
12
13 /**
14 * Converts a byte array to a lowercase hex string.
15 *
16 * @param bytes The byte array to convert.
17 * @return The hex string.
18 */
19 private static String toHexString(byte[] bytes) {
20 try (Formatter formatter = new Formatter()) {
21 for (byte b : bytes) {
22 formatter.format("%02x", b);
23 }
24 return formatter.toString();
25 }
26 }
27
28 /**
29 * Computes an HMAC-SHA512 hash for the given payload using the merchant client secret.
30 *
31 * @param data The stringified JSON payload.
32 * @param merchantClientSecret The merchant client secret.
33 * @return The computed HMAC-SHA512 hash as a lowercase hex string.
34 *
35 * @throws SignatureException If signature computation fails.
36 * @throws NoSuchAlgorithmException If HmacSHA512 algorithm is not available.
37 * @throws InvalidKeyException If the provided key is invalid.
38 */
39 public static String computeHMAC512TransactionHash(String data, String merchantClientSecret)
40 throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
41
42 SecretKeySpec secretKeySpec = new SecretKeySpec(
43 merchantClientSecret.getBytes(StandardCharsets.UTF_8),
44 HMAC_SHA512
45 );
46
47 Mac mac = Mac.getInstance(HMAC_SHA512);
48 mac.init(secretKeySpec);
49
50 byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
51 return toHexString(rawHmac);
52 }
53
54 // Example usage
55 public static void main(String[] args) {
56 String clientSecret = "91MUDL9N6U3BQRXBQ2PJ9M0PW4J22M1Y";
57 String requestBody = "{"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":"111222333@ZZAMZ4WT4Y3E.monnify"}},"eventType":"SUCCESSFUL_TRANSACTION"}";
58
59 try {
60 String hash = computeHMAC512TransactionHash(requestBody, clientSecret);
61 System.out.println("Computed Hash: " + hash);
62 } catch (Exception e) {
63 e.printStackTrace();
64 }
65 }
66}
67
How would you rate your experience?