> ## 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

Monnify uses OAuth 2.0 client credentials (API key + secret key) to obtain a short-lived access token before every API call. `createCheckout` calls `POST /v1/merchant/transactions/init-transaction` and returns a `checkoutUrl`. After payment, retrieve the transaction status with `retrievePayment(transactionReference)`. Amounts are in the standard currency unit (e.g. Naira for NGN).

<Warning>
  Monnify does not support customer management, subscriptions, or direct
  payment creation. Use `createCheckout` and `retrievePayment` for the
  core payment flow.
</Warning>

## 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',
  },
});

// 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',
});
```
