react-monnify
v1.0.0
Published
React hooks and components for accepting payments with the Monnify payment gateway
Downloads
571
Maintainers
Readme
react-monnify
React hooks and components for accepting payments with the Monnify payment gateway.

⚠️ Upgrading from 0.0.x — read this
0.0.x had a bug where the isTestMode check in useMonnifyScript had identical URLs in both branches, so the sandbox URL was requested regardless of the flag.
In practice this had no runtime effect: sandbox.sdk.monnify.com/plugin/monnify.js and sdk.monnify.com/plugin/monnify.js are byte-for-byte the same file, and the SDK picks its environment from your API key prefix (MK_PROD_), not from the script host. Live payments were not being routed to sandbox. The fix is still worth taking — relying on two URLs staying identical is fragile — but you do not have a historical incident to investigate.
The changes most likely to affect you are the React peer range, initializePayment now throwing instead of silently doing nothing, and the new build output. See Migrating from 0.0.x.
Requirements
- React 16.8 or later (17, 18 and 19 are supported)
- Node 18+ to build from source
0.0.x declared support for React 15, but the library is built on hooks and has never worked below 16.8.
Install
npm install react-monnifyyarn add react-monnifyQuick start
import {MonnifyButton} from 'react-monnify';
export default function Checkout() {
return (
<MonnifyButton
text="Make Payment"
className="payButton"
apiKey="MK_TEST_SAF7HR5F3F"
contractCode="4934121693"
amount={5000}
currency="NGN"
reference={`ref-${Date.now()}`}
customerFullName="John Doe"
customerEmail="[email protected]"
customerMobileNumber="08121281921"
paymentDescription="Test Pay"
isTestMode
onSuccess={(response) => console.log('paid', response)}
onClose={(response) => console.log('closed', response)}
/>
);
}isTestMode controls which SDK is loaded
| isTestMode | Script loaded |
| --- | --- |
| true | https://sandbox.sdk.monnify.com/plugin/monnify.js |
| false (default) | https://sdk.monnify.com/plugin/monnify.js |
Use your test API key with isTestMode, and your live key without it. Passing a live key while isTestMode is set will fail.
Usage
useMonnifyPayment hook
import {useMonnifyPayment} from 'react-monnify';
function DonateButton() {
const config = {
apiKey: 'MK_TEST_SAF7HR5F3F',
contractCode: '4934121693',
amount: 5000,
currency: 'NGN',
reference: `ref-${Date.now()}`,
customerFullName: 'John Doe',
customerEmail: '[email protected]',
customerMobileNumber: '08121281921',
isTestMode: true,
};
const initializePayment = useMonnifyPayment(config);
return (
<button onClick={() => initializePayment(onSuccess, onClose)}>
Donate
</button>
);
}initializePayment throws if it is called before the Monnify script has loaded, or if the script failed to load. To avoid that, gate your button on the script state:
import {useMonnifyScript, useMonnifyPayment} from 'react-monnify';
function PayButton(config) {
const [ready, failed] = useMonnifyScript(config.isTestMode);
const initializePayment = useMonnifyPayment(config);
if (failed) return <p>Payment is unavailable right now.</p>;
return (
<button disabled={!ready} onClick={() => initializePayment(onSuccess, onClose)}>
{ready ? 'Pay' : 'Loading…'}
</button>
);
}MonnifyConsumer render prop
import {MonnifyConsumer} from 'react-monnify';
<MonnifyConsumer {...config} onSuccess={onSuccess} onClose={onClose}>
{({initializePayment}) => (
<button onClick={initializePayment}>Pay with Monnify</button>
)}
</MonnifyConsumer>;MonnifyConsumer forwards refs. The ref is handed to the render prop:
<MonnifyConsumer {...config} ref={myRef}>
{({initializePayment, ref}) => (
<button ref={ref} onClick={initializePayment}>Pay</button>
)}
</MonnifyConsumer>MonnifyProvider
Wrap a subtree so nested components share one payment configuration.
import {MonnifyProvider} from 'react-monnify';
<MonnifyProvider {...config} onSuccess={onSuccess} onClose={onClose}>
<Checkout />
</MonnifyProvider>;API
Payment options
Accepted by useMonnifyPayment, MonnifyButton, MonnifyConsumer and MonnifyProvider.
| Option | Type | Required | Description |
| --- | --- | --- | --- |
| apiKey | string | ✅ | Merchant API key from the Monnify dashboard |
| contractCode | string | ✅ | Merchant contract code |
| amount | number | ✅ | Amount to charge |
| reference | string | ✅ | Your unique reference for this transaction |
| currency | string | ✅ | Defaults to NGN |
| customerFullName | string | ✅ | Customer's full name |
| customerEmail | string | ✅ | Customer's email |
| customerMobileNumber | string | ✅ | Customer's phone number |
| isTestMode | boolean | | Load the sandbox SDK. Defaults to false |
| paymentDescription | string | | Used as the account name for bank-transfer payments |
| redirectUrl | string | | Where to send the customer after payment |
| metadata | object | | Extra data passed through to the API |
| incomeSplitConfig | MonnifySplitOptions[] | | Split-payment configuration |
| transactionHash | object | | Transaction hash, for response verification |
MonnifyButton props
Everything above, plus:
| Prop | Type | Description |
| --- | --- | --- |
| text | string | Button label |
| children | ReactNode | Used when text is absent |
| className | string | CSS class |
| disabled | boolean | Disable the button |
| type | 'button', 'submit' or 'reset' | Defaults to 'button' |
| onSuccess | Function | Called on completion |
| onClose | Function | Called when the modal closes |
Exports
import {
useMonnifyPayment,
useMonnifyScript,
MonnifyButton,
MonnifyConsumer,
MonnifyProvider,
getMonnifyScriptUrl,
MONNIFY_SANDBOX_SDK_URL,
MONNIFY_PRODUCTION_SDK_URL,
} from 'react-monnify';TypeScript types MonnifyProps, MonnifySplitOptions, MonnifySDK and MonnifyRenderProps are exported too.
Migrating from 0.0.x
| Change | Impact |
| --- | --- |
| The correct host is now requested per isTestMode | No behavioural change — both hosts serve the same file (see above). Cosmetic in the Network tab. |
| customerName is now sent to Monnify | The SDK builds its transaction payload from customerName, but the library only ever set customerFullName, so the customer's name never reached Monnify. It does now. |
| initializePayment throws instead of silently doing nothing when the script is not ready | Gate the call on useMonnifyScript, or catch the error. |
| Script error no longer reports loaded: true | useMonnifyScript returns [false, true] on failure instead of [true, true]. |
| MonnifyConsumer ref actually reaches the render prop | It was always undefined before. |
| Peer range is now react@^16.8 || ^17 || ^18 || ^19 | React 15 was never actually supported. |
| react-dom peer dependency removed | The library never imported it. |
| Build output is dist/index.cjs + dist/index.mjs with an exports map | Deep imports into dist/ are no longer supported. |
Development
npm install
npm run typecheck
npm test
npm run buildRunning the example app
npm run build # the example consumes the built library
cd example
cp .env.example .env # add your sandbox API key + contract code
npm install
npm run dev # http://localhost:3000Monnify's current public sandbox credentials are on the developer docs. Note that the demo key and contract code printed in this README before v1.0.0 have been retired — Monnify's API now answers Could not find specified contract for them.
Only the API key and contract code belong in a frontend integration. Never put your Secret Key in browser code — anything in a VITE_-prefixed variable is compiled into the public bundle. The Secret Key is for server-to-server calls such as verifying a transaction.
Each payment attempt needs a fresh reference; Monnify rejects one it has already seen. The example regenerates it per attempt.
Documentation
See the Monnify developer documentation for the full list of SDK options and for how to verify a transaction server-side.
Always verify a payment on your server before fulfilling an order. Never trust the browser callback alone.
Contributing
- Fork it
- Create your feature branch:
git checkout -b feature-name - Commit your changes:
git commit -am 'Some commit message' - Push to the branch:
git push origin feature-name - Submit a pull request 😉
How can I thank you?
Star the repo, and share it! Don't forget to follow me on twitter.
Thanks! Adekanbi Dansteve.
License
MIT — see LICENSE.md.
