PHPackages                             monnify/monnify-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. monnify/monnify-laravel

ActiveLibrary[Payment Processing](/categories/payments)

monnify/monnify-laravel
=======================

A Laravel Monnify Package

1.1.0(1mo ago)41.7k—8.3%1[1 PRs](https://github.com/Monnify/monnify-laravel/pulls)MITPHPPHP ^8.1CI passing

Since Apr 16Pushed 3w agoCompare

[ Source](https://github.com/Monnify/monnify-laravel)[ Packagist](https://packagist.org/packages/monnify/monnify-laravel)[ Docs](https://github.com/monnify/monnify-laravel)[ RSS](/packages/monnify-monnify-laravel/feed)WikiDiscussions main Synced 3d ago

READMEChangelog (2)Dependencies (9)Versions (5)Used By (0)

Monnify Laravel
===============

[](#monnify-laravel)

[![Tests](https://github.com/Monnify/monnify-laravel/actions/workflows/tests.yml/badge.svg)](https://github.com/Monnify/monnify-laravel/actions/workflows/tests.yml)[![codecov](https://camo.githubusercontent.com/2e691c4196fbe852f456e18de75a6942f87b9e5489c3c79d44ccdd0d9c6dad6e/68747470733a2f2f636f6465636f762e696f2f67682f4d6f6e6e6966792f6d6f6e6e6966792d6c61726176656c2f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://codecov.io/gh/Monnify/monnify-laravel)[![Latest Version on Packagist](https://camo.githubusercontent.com/9aa4936fbdbc7baf0884eb14ebb365cb76327080ec7ac9cdbee22fe168640803/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6f6e6e6966792f6d6f6e6e6966792d6c61726176656c2e737667)](https://packagist.org/packages/monnify/monnify-laravel)[![Total Downloads](https://camo.githubusercontent.com/ac231e4e054771e78d81398b0ee09b7905ade4742aafdd86d9a392fcf898b923/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d6f6e6e6966792f6d6f6e6e6966792d6c61726176656c2e737667)](https://packagist.org/packages/monnify/monnify-laravel)

A Laravel package for integrating the [Monnify](https://monnify.com) payment gateway into your Laravel application. It covers collections, disbursements, virtual accounts, bills payment, verification, and more — all through a clean, consistent API.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#configuration)
- [Quick Start](#quick-start)
- [How Responses Work](#how-responses-work)
- [Error Handling](#error-handling)
- [Webhooks](#webhooks)
- [Services](#services)
    - [Transactions](#transactions)
    - [Customer Reserved Accounts](#customer-reserved-accounts)
    - [Invoices](#invoices)
    - [Disbursements (Transfers)](#disbursements-transfers)
    - [Wallets](#wallets)
    - [Verification](#verification)
    - [Sub Accounts](#sub-accounts)
    - [Refunds](#refunds)
    - [Settlements](#settlements)
    - [Limit Profiles](#limit-profiles)
    - [Pay Codes](#pay-codes)
    - [Direct Debit](#direct-debit)
    - [Recurring Payments](#recurring-payments)
    - [Bills Payment](#bills-payment)
    - [Helper / Utilities](#helper--utilities)
- [Contributing](#contributing)
- [Credits](#credits)
- [License](#license)

---

Requirements
------------

[](#requirements)

- PHP **8.1** or higher
- Laravel **8.x – 12.x**
- A Monnify merchant account ([sign up here](https://app.monnify.com))

---

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require monnify/monnify-laravel
```

Publish the config file:

```
php artisan vendor:publish --provider="Monnify\MonnifyLaravel\MonnifyServiceProvider"
```

This creates a `config/monnify.php` file in your application.

---

Configuration
-------------

[](#configuration)

Add the following to your `.env` file:

```
MONNIFY_API_KEY=your_api_key
MONNIFY_SECRET_KEY=your_secret_key
MONNIFY_CONTRACT_CODE=your_contract_code
MONNIFY_WALLET_ACCOUNT_NUMBER=your_wallet_account_number
MONNIFY_ACCOUNT_NUMBER=your_account_number
MONNIFY_ENVIRONMENT=SANDBOX   # Use LIVE when going to production
```

> **Where do I find these?** Log in to your [Monnify dashboard](https://app.monnify.com), go to **Developers → API Keys &amp; Contract** to get your API key, secret, and contract code.

> **Tip:** Always start with `MONNIFY_ENVIRONMENT=SANDBOX` while building and testing. Switch to `LIVE` only when you are ready for production.

---

Quick Start
-----------

[](#quick-start)

The most common flow: collect a payment and verify it when the customer returns.

**Step 1 — Initialize the payment and redirect the customer:**

```
use Monnify\MonnifyLaravel\Facades\Monnify;

// In your checkout controller
$response = Monnify::transactions()->initialise([
    'amount'           => 5000.00,
    'customerEmail'    => 'jane@example.com',
    'paymentReference' => 'ORDER-' . uniqid(),   // must be unique per transaction
    'currencyCode'     => 'NGN',
    'contractCode'     => config('monnify.contract_code'),
    'redirectUrl'      => route('payment.callback'),
]);

return redirect($response['body']['responseBody']['checkoutUrl']);
```

**Step 2 — Verify the payment in your callback handler:**

```
// In your callback controller
public function callback(Request $request)
{
    $reference = $request->query('paymentReference');

    $result = Monnify::transactions()->statusByReference($reference, 'payment');
    $status = $result['body']['responseBody']['paymentStatus'] ?? null;

    if ($status === 'PAID') {
        // Payment confirmed server-side — safe to fulfil the order
    }
}
```

> **Important:** Never rely solely on the redirect URL parameters to confirm a payment. Always call `statusByReference()` (or `status()`) from your callback handler to verify the payment directly with Monnify before fulfilling orders. For an additional layer of reliability — especially for payments that complete after a timeout or network drop — subscribe to [Monnify webhook notifications](#webhooks) and treat them as a second source of truth.

---

How Responses Work
------------------

[](#how-responses-work)

Every method in this package returns an array with two keys:

```
[
    'status' => 200,       // HTTP status code from Monnify
    'body'   => [ ... ],   // The parsed response data from Monnify
]
```

> **Why `$response['body']['responseBody']`?** Monnify's API always wraps the actual data inside a `responseBody` key within `body`. This is a Monnify API convention — the package returns it as-is so nothing is hidden from you. When you see `$response['body']['responseBody']['checkoutUrl']`, the two levels are: `body` (this package's wrapper) → `responseBody` (Monnify's wrapper) → the actual data.

On a failed request (e.g. a network error or a 4xx/5xx response), the array will look like:

```
[
    'status' => 400,
    'error'  => [ ... ],   // Error details returned by Monnify
]
```

**Practical example — reading a response:**

```
$response = Monnify::transactions()->initialise($data);

if ($response['status'] === 200) {
    $checkoutUrl = $response['body']['responseBody']['checkoutUrl'];
    return redirect($checkoutUrl);
}

// Something went wrong
logger()->error('Monnify error', $response['error'] ?? []);
```

---

Error Handling
--------------

[](#error-handling)

Wrap your calls in a `try/catch` block to handle validation errors (thrown before the request is made) and unexpected network failures:

```
use Exception;
use Monnify\MonnifyLaravel\Facades\Monnify;

try {
    $response = Monnify::transactions()->initialise($data);
} catch (InvalidArgumentException $e) {
    // A required field was missing or invalid — the request was never sent
    return response()->json(['message' => $e->getMessage()], 422);
} catch (Exception $e) {
    // Something unexpected happened (network issue, etc.)
    return response()->json(['message' => 'Payment service unavailable'], 503);
}
```

---

Webhooks
--------

[](#webhooks)

Monnify sends an HTTP `POST` to your server whenever a significant event occurs (payment received, transfer completed, refund processed, etc.). Webhooks are the most reliable way to keep your system in sync — they fire even if your customer closes the browser mid-flow or a network timeout prevents the redirect from completing.

Full details: [Webhook Overview](https://developers.monnify.com/docs/webhooks) · [Event Types](https://developers.monnify.com/docs/webhooks/event-types)

---

### Setting Up

[](#setting-up)

1. Log in to your [Monnify dashboard](https://app.monnify.com)
2. Go to **Developers → Webhook URLs**
3. Enter your endpoint URL for each notification type (Transaction Completion, Disbursement, Refund, Settlement)

---

### Event Types

[](#event-types)

EventWhen it firesRelated service`SUCCESSFUL_TRANSACTION`Payment confirmed on a reserved account or offlineTransactions, Reserved Accounts`SUCCESSFUL_DISBURSEMENT`A transfer completes successfullyDisbursements`FAILED_DISBURSEMENT`A transfer failsDisbursements`REVERSED_DISBURSEMENT`A transfer is reversedDisbursements`SUCCESSFUL_REFUND`A refund is processedRefunds`FAILED_REFUND`A refund attempt failsRefunds`SETTLEMENT`Funds settled to your bank account or walletSettlements`ACCOUNT_ACTIVITY`Credit or debit on a walletWallets`REJECTED_PAYMENT`Payment rejected (e.g. under-payment)Transactions`MANDATE_UPDATE`Direct debit mandate status changesDirect Debit`LOW_BALANCE_ALERT`Wallet balance drops below your configured thresholdWallets`OFFLINE_PAYMENT_AGENT`An offline agent payment completesTransactionsEvery webhook payload follows this structure:

```
{
  "eventType": "SUCCESSFUL_TRANSACTION",
  "eventData": {
    "transactionReference": "MNFY|...",
    "paymentStatus": "PAID",
    ...
  }
}
```

---

### Verifying the Signature

[](#verifying-the-signature)

Monnify signs every webhook request with a `monnify-signature` header — an HMAC-SHA512 hash of the raw request body, keyed with your **secret key**.

**Always verify this before processing any webhook.** Skipping this check means anyone who knows your endpoint URL could send fake events.

```
$signature = $request->header('monnify-signature');
$expected  = hash_hmac('sha512', $request->getContent(), config('monnify.secret_key'));

if (! hash_equals($expected, $signature)) {
    return response()->json(['message' => 'Invalid signature'], 401);
}
```

> **Note:** Use `hash_equals()` instead of `===` to prevent timing attacks.

---

### Handling Webhooks in Laravel

[](#handling-webhooks-in-laravel)

**1. Create the controller:**

```
