> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usepaykit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Monnify

> Monnify provider for PayKit.

```bash theme={null}
npm install @paykit-sdk/monnify
```

## Setup

<Tabs>
  <Tab title="Environment variables">
    ```typescript theme={null}
    import { PayKit } from '@paykit-sdk/core';
    import { monnify } from '@paykit-sdk/monnify';

    export const paykit = new PayKit(monnify());
    ```

    Required env vars:

    ```bash theme={null}
    MONNIFY_API_KEY=MK_TEST_...
    MONNIFY_SECRET_KEY=your-secret-key
    MONNIFY_SANDBOX=true
    ```
  </Tab>

  <Tab title="Direct config">
    ```typescript theme={null}
    import { PayKit } from '@paykit-sdk/core';
    import { createMonnify } from '@paykit-sdk/monnify';

    export const paykit = new PayKit(
      createMonnify({
        apiKey: 'MK_TEST_...',
        secretKey: 'your-secret-key',
        isSandbox: true,
      }),
    );
    ```
  </Tab>
</Tabs>

## How it works

```typescript theme={null}
const checkout = await paykit.checkouts.create({
  customer: { email: 'user@example.com' },
  item_id: 'my-product',
  session_type: 'one_time',
  quantity: 1,
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
  provider_metadata: {
    amount: 5000, // in the standard currency unit (e.g. Naira)
    currency: 'NGN',
  },
});

// Redirect the customer to checkout.payment_url

// Once they're back, confirm the payment
const payment = await paykit.payments.retrieve(checkout.id);
```

Monnify only has one way to move money — a hosted redirect transaction — so `paykit.payments.create` uses the same flow as `paykit.checkouts.create`, just returns a `Payment` instead of a `Checkout`; it needs a `success_url` in `provider_metadata` since there's no other field for it to redirect to. Monnify doesn't support customer management or subscriptions.

## Webhooks

Monnify signs webhook payloads with HMAC-SHA512 via the `monnify-signature` header. Add a webhook endpoint in your Monnify dashboard that points to your handler.

```typescript theme={null}
const webhook = paykit.webhooks
  .setup({ webhookSecret: process.env.MONNIFY_WEBHOOK_SECRET! })
  .on('payment.created', async event => {
    /* SUCCESSFUL_TRANSACTION */
  })
  .on('payment.failed', async event => {
    /* REJECTED_PAYMENT */
  })
  .on('payment.updated', async event => {
    /* SETTLEMENT */
  })
  .on('refund.created', async event => {
    /* refund succeeded or failed */
  });

await webhook.handle({
  body: rawBody,
  headersAsObject: Object.fromEntries(request.headers),
  fullUrl: request.url,
});
```

### Raw Monnify events

Opt into any native Monnify event type — typed against Monnify's API payload:

```typescript theme={null}
paykit.webhooks
  .setup({ webhookSecret: process.env.MONNIFY_WEBHOOK_SECRET! })
  .on('monnify.SUCCESSFUL_TRANSACTION', async event => {
    // event.data is the raw Monnify transaction payload
  })
  .on('monnify.MANDATE_UPDATE', async event => {
    // event.data is the raw Monnify mandate payload
  });
```

All available raw events and their PayKit mappings:

| Raw event | PayKit event |
| - | - |
| `monnify.SUCCESSFUL_TRANSACTION` | `payment.created` |
| `monnify.SUCCESSFUL_TRANSACTION_OFFLINE` | `payment.created` |
| `monnify.REJECTED_PAYMENT` | `payment.failed` |
| `monnify.SETTLEMENT` | `payment.updated` |
| `monnify.SUCCESSFUL_REFUND` | `refund.created` |
| `monnify.FAILED_REFUND` | `refund.created` |
| `monnify.MANDATE_UPDATE` | `subscription.created` / `subscription.canceled` / `subscription.updated` |
| `monnify.CUSTOMER_CREATED` | *(ignored)* |
| `monnify.CUSTOMER_UPDATED` | *(ignored)* |
| `monnify.CUSTOMER_DELETED` | *(ignored)* |
| `monnify.SUCCESSFUL_DISBURSEMENT` | *(ignored)* |
| `monnify.FAILED_DISBURSEMENT` | *(ignored)* |
| `monnify.REVERSED_DISBURSEMENT` | *(ignored)* |

## provider\_metadata

`amount` and `currency` are required in `provider_metadata` for checkout because Monnify does not infer them from the plan or item:

```typescript theme={null}
// checkout.provider_metadata requires amount and currency
await paykit.checkouts.create({
  customer: { email: 'user@example.com' },
  item_id: 'my-product',
  session_type: 'one_time',
  quantity: 1,
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
  provider_metadata: {
    amount: 5000, // in the standard currency unit (e.g. Naira)
    currency: 'NGN',
  },
});

// payment.provider_metadata requires success_url — Monnify still redirects
// the customer even for a "direct" payment
await paykit.payments.create({
  customer: { email: 'user@example.com' },
  item_id: 'my-product',
  amount: 5000,
  currency: 'NGN',
  provider_metadata: {
    success_url: 'https://example.com/success', // required
  },
});

// refund.provider_metadata — any extra fields are forwarded to Monnify's refund endpoint
await paykit.refunds.create({
  payment_id: 'MNFY|...',
  amount: 1000,
  reason: 'Customer request',
});
```

## Escape hatch

`paykit._native` returns PayKit's [HTTP client](/concepts/http-client), already pointed at Monnify and authenticated with your credentials. Use it to call endpoints PayKit does not map yet.

```typescript theme={null}
const result = await paykit._native.get(
  '/v2/merchant/transactions/query',
);

if (result.ok) console.log(result.value);
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.