mtn-momo-kit
v0.2.1
Published
Cross-platform TypeScript kit for the MTN MoMo API (Collections, Disbursements, Remittances). Works with Node.js, React Native, Expo, and browsers.
Maintainers
Readme
MTN MoMo Kit
Universal TypeScript Kit for the MTN Mobile Money (MoMo) API. Compatible with Node.js, React Native, Expo, and browsers (Vite, Next.js, etc.).
Table of Contents
- Installation
- Getting your credentials
- Quick Start
- Provisioning
- Configuration
- Usage
- Webhooks
- API Reference
- Sandbox Testing
- Going to Production
- Error Handling
- FAQ
Prerequisites
Before you start, make sure you have:
- Node.js 18+ (or React Native 0.71+, or a modern browser)
- npm (or your package manager of choice)
- An MTN MoMo developer account — create one for free
- A Primary Key from subscribing to a product on the portal (see Getting your credentials)
Don't have an account yet? Follow steps 1–3 in Getting your credentials first, then come back here.
Installation
npm install mtn-momo-kitThe Kit only depends on base-64 (1kB) for cross-platform Base64 encoding. All other APIs used (fetch, crypto) are native, available in Node.js 18+, React Native 0.71+, and all modern browsers.
Getting your credentials
Before using the Kit, you need to get your API credentials from MTN. Follow these steps:
1. Create an account
Go to momodeveloper.mtn.com and create a developer account.
2. Subscribe to a product
Once logged in, click on Products and subscribe to the product you need:
| Product | Use case | |---|---| | Collections | Receive payments from customers | | Disbursements | Send money to users (refunds, payouts) | | Remittances | Cross-border transfers |
Each product has its own Primary Key. Subscribe to each one you need.
3. Get your Primary Key
After subscribing, go to your Profile (top-right menu) → scroll down to the bottom of the page. You will see your product keys:
Primary Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Secondary Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| Key | Usage |
|---|---|
| Primary Key → subscriptionKey | Used in your code |
| Secondary Key | Backup for key rotation — same permissions as Primary |
The Secondary Key exists so you can rotate your Primary Key without downtime: switch to Secondary, regenerate Primary, then switch back.
subscriptionKey = Primary Key
4. Create an API User & Key
You have two options:
import { v4 as uuid } from 'uuid'
import { Momo } from 'mtn-momo-kit'
// Create API User
const apiUser = uuid()
await Momo.createApiUser('YOUR_PRIMARY_KEY', apiUser, 'https://your-site.com/webhook')
console.log('API User:', apiUser)
// Generate API Key
const apiKey = await Momo.generateApiKey('YOUR_PRIMARY_KEY', apiUser)
console.log('API Key:', apiKey)apiUser = the UUID you generated
apiKey = the key returned by generateApiKey()
- Go to API User section in the portal
- Click Create API User and generate an API Key
- Copy both the API User UUID and the API Key
5. You're ready!
You now have all three credentials:
subscriptionKey → Primary Key
apiUser → UUID from step 4
apiKey → Key from step 4Quick Start
import { Momo } from 'mtn-momo-kit'
const momo = new Momo({
subscriptionKey: 'your_primary_key', // Default key for all products
// Or per-product keys:
// collectionSubscriptionKey: '...',
// disbursementSubscriptionKey: '...',
// remittanceSubscriptionKey: '...',
apiUser: 'your_api_user',
apiKey: 'your_api_key',
environment: 'sandbox',
})
// Check your balance
const balance = await momo.collections.getBalance()
console.log(`${balance.availableBalance} ${balance.currency}`)Provisioning
Before using the Kit, you need an API User and an API Key. In the sandbox environment, you can create them directly with the Kit. In production, MTN provides them after KYC.
⚠️ Important: Each product (Collections, Disbursements, Remittances) has its own Primary Key. Subscribe to each product separately on momodeveloper.mtn.com. A Disbursements Primary Key will not work on Collections endpoints (
momo.collections.*).
1. Create an API User
import { Momo } from 'mtn-momo-kit'
const referenceId = uuid()
await Momo.createApiUser(
'your_primary_key',
referenceId,
'https://your-domain.com/webhook', // Callback URL for notifications
'sandbox', // 'sandbox' | 'production'
)
console.log('API User created:', referenceId)| Parameter | Type | Description |
|---|---|---|
| subscriptionKey | string | Your product Primary Key |
| referenceId | string | UUID v4 — becomes your API User ID |
| callbackHost | string | URL where MTN sends transaction notifications |
| environment | 'sandbox' \| 'production' | Defaults to 'sandbox' |
2. Generate an API Key
const apiKey = await Momo.generateApiKey(
'your_primary_key',
referenceId, // Same UUID used for createApiUser
'sandbox',
)
console.log('API Key:', apiKey)
// Store this key securely — it won't be shown againFull provisioning script
import { v4 as uuid } from 'uuid'
import { Momo } from 'mtn-momo-kit'
async function setup() {
const ref = uuid()
// Step 1 — Create API User
await Momo.createApiUser('YOUR_SUBSCRIPTION_KEY', ref, 'https://your-site.com/webhook')
console.log('API User:', ref)
// Step 2 — Generate API Key
const apiKey = await Momo.generateApiKey('YOUR_SUBSCRIPTION_KEY', ref)
console.log('API Key:', apiKey)
// Step 3 — Use the Kit
const momo = new Momo({
subscriptionKey: 'YOUR_SUBSCRIPTION_KEY',
apiUser: ref,
apiKey,
environment: 'sandbox',
})
const balance = await momo.collections.getBalance()
console.log('Balance:', balance)
}
setup().catch(console.error)Note: In production, MTN provides the API User and API Key directly — skip steps 1 and 2.
Configuration
Create a .env file at your project root:
# At least one subscription key is required
MOMO_SUBSCRIPTION_KEY=your_primary_key # Default key for all products
MOMO_COLLECTION_KEY=your_collection_key # Collections key (overrides SUBSCRIPTION)
MOMO_DISBURSEMENTS_KEY=your_disbursement_key # Disbursements key (overrides SUBSCRIPTION)
MOMO_REMITTANCE_KEY=your_remittance_key # Remittances key (overrides SUBSCRIPTION)
MOMO_API_USER=your_api_user # API User UUID
MOMO_API_KEY=your_api_key # API Key
MOMO_ENVIRONMENT=sandbox # sandbox | production
MOMO_CALLBACK_HOST=https://your-site.com/webhook # Webhook callback URLReading .env by platform
npm install react-native-dotenv --save-dev// babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
['module:react-native-dotenv', {
moduleName: '@env',
path: '.env',
}],
],
}import { MOMO_SUBSCRIPTION_KEY, MOMO_API_USER, MOMO_API_KEY } from '@env'// app.json
{
"expo": {
"extra": {
"momoSubscriptionKey": process.env.MOMO_SUBSCRIPTION_KEY,
"momoApiUser": process.env.MOMO_API_USER,
"momoApiKey": process.env.MOMO_API_KEY
}
}
}import Constants from 'expo-constants'
const { momoSubscriptionKey, momoApiUser, momoApiKey } = Constants.expoConfig?.extra ?? {}# .env (must start with VITE_)
VITE_MOMO_SUBSCRIPTION_KEY=xxx
VITE_MOMO_API_USER=xxx
VITE_MOMO_API_KEY=xxxconst momo = new Momo({
subscriptionKey: import.meta.env.VITE_MOMO_SUBSCRIPTION_KEY,
apiUser: import.meta.env.VITE_MOMO_API_USER,
apiKey: import.meta.env.VITE_MOMO_API_KEY,
environment: import.meta.env.VITE_MOMO_ENVIRONMENT ?? 'sandbox',
})# .env.local
NEXT_PUBLIC_MOMO_SUBSCRIPTION_KEY=xxx
NEXT_PUBLIC_MOMO_API_USER=xxx
NEXT_PUBLIC_MOMO_API_KEY=xxxconst momo = new Momo({
subscriptionKey: process.env.NEXT_PUBLIC_MOMO_SUBSCRIPTION_KEY!,
apiUser: process.env.NEXT_PUBLIC_MOMO_API_USER!,
apiKey: process.env.NEXT_PUBLIC_MOMO_API_KEY!,
environment: 'sandbox',
})npm install dotenvimport 'dotenv/config'
const momo = new Momo({
subscriptionKey: process.env.MOMO_SUBSCRIPTION_KEY!,
apiUser: process.env.MOMO_API_USER!,
apiKey: process.env.MOMO_API_KEY!,
environment: process.env.MOMO_ENVIRONMENT ?? 'sandbox',
})Important: Never commit your
.envfile. Add it to.gitignore.
Usage
import { Momo } from 'mtn-momo-kit'
const momo = new Momo({
subscriptionKey: 'your_primary_key',
apiUser: 'your_api_user',
apiKey: 'your_api_key',
environment: 'sandbox', // 'sandbox' | 'production'
})The constructor initializes three modules and supports per-product Primary Keys:
const momo = new Momo({
// Single key for all products
subscriptionKey: 'primary_key_collections',
// Or specific keys per product:
collectionSubscriptionKey: 'primary_key_collections',
disbursementSubscriptionKey: 'primary_key_disbursements',
remittanceSubscriptionKey: 'primary_key_remittances',
apiUser: 'your_api_user',
apiKey: 'your_api_key',
environment: 'sandbox',
})| Module | Access | Description |
|---|---|---|
| Collections | momo.collections | Incoming payments (Request to Pay) |
| Disbursements | momo.disbursements | Outgoing payments (Transfer) |
| Remittances | momo.remittances | Cross-border transfers |
Collections
Receive payments from your customers.
import { v4 as uuid } from 'uuid'
const referenceId = uuid()
// 1. Request a payment
await momo.collections.requestToPay(
{
amount: '5000',
currency: 'EUR',
externalId: 'invoice-2024-001',
payer: {
partyIdType: 'MSISDN',
partyId: '256772123456',
},
payerMessage: 'Payment for January 2024 invoice',
payeeNote: 'Thank you for your payment',
},
referenceId,
)
// 2. Check transaction status
const status = await momo.collections.getTransactionStatus(referenceId)
console.log(status.status) // 'SUCCESSFUL' | 'FAILED' | 'PENDING'
// 3. Check balance
const balance = await momo.collections.getBalance()
console.log(`${balance.availableBalance} ${balance.currency}`)
// 4. Check if an account is active
const active = await momo.collections.isAccountHolderActive('MSISDN', '256772123456')
console.log(active.result) // true | falseDisbursements
Send money to your users.
import { v4 as uuid } from 'uuid'
const referenceId = uuid()
// 1. Make a transfer
await momo.disbursements.transfer(
{
amount: '2500',
currency: 'EUR',
externalId: 'refund-2024-001',
payee: {
partyIdType: 'MSISDN',
partyId: '256772123456',
},
payerMessage: 'Refund for order #1234',
payeeNote: 'Your refund has been processed',
},
referenceId,
)
// 2. Check transaction status
const status = await momo.disbursements.getTransactionStatus(referenceId)
// 3. Check balance
const balance = await momo.disbursements.getBalance()
// 4. Validate a recipient account
const active = await momo.disbursements.isAccountHolderActive('MSISDN', '256772123456')Remittances
International money transfers.
const referenceId = uuid()
await momo.remittances.transfer(
{
amount: '100000',
currency: 'EUR',
externalId: 'transfer-2024-001',
payee: {
partyIdType: 'MSISDN',
partyId: '256772123456',
},
},
referenceId,
)
const status = await momo.remittances.getTransactionStatus(referenceId)
const balance = await momo.remittances.getBalance()Webhooks
MTN MoMo sends asynchronous notifications about transaction status changes to your callbackHost. Use Momo.parseWebhookPayload() to validate and parse incoming requests.
import { Momo } from 'mtn-momo-kit'
// Example: Express.js webhook endpoint
app.post('/webhook', (req, res) => {
const payload = Momo.parseWebhookPayload(req.body)
if (!payload) {
return res.status(400).send('Invalid payload')
}
console.log('Transaction:', payload.referenceId, payload.status)
switch (payload.status) {
case 'SUCCESSFUL':
// Update your database, deliver goods, etc.
break
case 'FAILED':
// Notify the user, retry logic, etc.
break
case 'PENDING':
// Wait for final status
break
}
res.status(200).send('OK')
})Webhook payload structure
interface MomoWebhookPayload {
referenceId: string // Transaction reference UUID
status: 'SUCCESSFUL' | 'FAILED' | 'PENDING'
amount?: string
currency?: string
financialTransactionId?: string // MTN transaction ID
externalId?: string // Your business ID
payer?: Party
reason?: Record<string, unknown>
payeeNote?: string
payerMessage?: string
}API Reference
Momo constructor
constructor(config: MomoConfig)| Property | Type | Default | Description |
|---|---|---|---|
| subscriptionKey | string | — | Default Primary Key (used for all products if no specific key is set) |
| collectionSubscriptionKey | string | — | Primary Key for Collections only (overrides subscriptionKey) |
| disbursementSubscriptionKey | string | — | Primary Key for Disbursements only (overrides subscriptionKey) |
| remittanceSubscriptionKey | string | — | Primary Key for Remittances only (overrides subscriptionKey) |
| apiUser | string | — | API user ID (UUID v4) |
| apiKey | string | — | Generated API key |
| environment | 'sandbox' \| 'production' | 'sandbox' | Target environment |
| callbackHost | string | — | Callback URL for notifications (optional) |
Momo.createApiUser()
static createApiUser(subscriptionKey, referenceId, callbackHost, environment): Promise<void>Creates an API User in the sandbox environment. See Provisioning.
Momo.generateApiKey()
static generateApiKey(subscriptionKey, referenceId, environment): Promise<string>Generates an API Key for an existing API User. See Provisioning.
Momo.parseWebhookPayload()
static parseWebhookPayload(body: unknown): MomoWebhookPayload | nullValidates and parses an incoming MTN webhook payload. Returns null for invalid payloads. See Webhooks.
collections.requestToPay(params, referenceId)
| Parameter | Type | Description |
|---|---|---|
| params.amount | string | Amount (e.g. "5000") |
| params.currency | string | Currency (e.g. "EUR", "XAF") |
| params.externalId | string | Business transaction ID |
| params.payer.partyIdType | 'MSISDN' \| 'EMAIL' \| 'PARTY_CODE' | Payer identifier type |
| params.payer.partyId | string | Identifier value (phone number, email, etc.) |
| params.payerMessage | string | Message visible to the payer (optional) |
| params.payeeNote | string | Internal note for the payee (optional) |
| params.callbackUrl | string | URL for per-transaction callback (optional, overrides callbackHost) |
| referenceId | string | Unique UUID v4 for this transaction |
Returns: Promise<void> (status 202 = accepted)
transfer(params, referenceId)
Same parameters as requestToPay (except payer → payee).
Sandbox Testing
Provision credentials with the Kit
import { v4 as uuid } from 'uuid'
import { Momo } from 'mtn-momo-kit'
async function sandboxSetup() {
const ref = uuid()
// 1. Create API User (this is your apiUser)
await Momo.createApiUser(
'YOUR_SUBSCRIPTION_KEY',
ref,
'https://your-site.com/webhook',
)
// 2. Generate API Key
const apiKey = await Momo.generateApiKey('YOUR_SUBSCRIPTION_KEY', ref)
// 3. Initialize the Kit
const momo = new Momo({
subscriptionKey: 'YOUR_SUBSCRIPTION_KEY',
apiUser: ref,
apiKey,
environment: 'sandbox',
})
// 4. Test
const balance = await momo.collections.getBalance()
console.log('Sandbox balance:', balance)
}Test numbers
| Number | Behavior |
|---|---|
| 256772123456 | Payment accepted |
| 256772654321 | Payment rejected |
| 256772999999 | Transaction pending (timeout) |
Going to Production
Prerequisites
- Complete the KYC process with MTN
- Sign the service agreement
- Get production credentials (API User, API Key) from the portal
Switch environment
const momo = new Momo({
subscriptionKey: 'prod_subscription_key',
apiUser: 'prod_api_user',
apiKey: 'prod_api_key',
environment: 'production',
})Sandbox vs Production
| Aspect | Sandbox | Production |
|---|---|---|
| URL | sandbox.momodeveloper.mtn.com | momoapi.mtn.com |
| API User | Self-created via createApiUser() | Provided by MTN |
| API Key | Self-generated via generateApiKey() | Provided by MTN |
| Transactions | Simulated | Real |
| Callbacks | Simulated webhook | Real webhook |
| Limits | Unlimited | Per contract |
Error Handling
import { Momo } from 'mtn-momo-kit'
const momo = new Momo(config)
try {
await momo.collections.requestToPay(params, refId)
} catch (error) {
if (error instanceof Error) {
console.error('Code:', error.message.split(':')[0])
console.error('Detail:', error.message)
}
}Common HTTP error codes
| Code | Cause |
|---|---|
| 400 | Invalid request (missing or incorrect parameters) |
| 401 | Authentication failed (invalid or expired token) |
| 403 | Access denied (check your subscription key) |
| 404 | Transaction not found |
| 409 | Conflict (reference ID already used) |
| 429 | Too many requests (rate limit) |
| 500 | MTN internal error |
Token management
The Kit automatically handles OAuth2 token acquisition and renewal. The token is fetched lazily (on the first API call) and cached for subsequent requests. No manual action required.
FAQ
Does this Kit work with React Native?
Yes. It only uses fetch (available since RN 0.71+) and base-64 (cross-platform Base64). No dependency on Node.js native modules (fs, crypto, path, stream).
Can I use it in a browser?
Yes. The Kit works in all modern browsers that support fetch.
How do I handle callbacks/notifications?
Set callbackHost when creating the API User. MTN will call this URL to notify the final transaction status. Use Momo.parseWebhookPayload() to validate incoming requests on your webhook endpoint.
Are sandbox test numbers the same for all countries?
No. Test numbers may vary by country. Check the official MTN documentation for your target country.
Is the exchange rate included?
No. The Kit processes amounts in the specified currency. Currency conversion is handled by MTN at their current rate.
License
ISC
