npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@morapay/moolre

v0.1.1

Published

A wrapper for Moolre payments, SMS, and bank rails (Ghana & Nigeria)

Readme

@morapay/moolre

A wrapper for Moolre payments, SMS, bank collection, and payout transfers in Ghana and Nigeria.

Most Moolre integrations fail because of subtle API details — wrong channel ids (collection 13 vs transfer 1), missing OTP follow-up (TP14TP17TR099), incorrect phone formats, or mixing sandbox/live credentials. This SDK handles those details so you pass simple props and get predictable results.

Before you install — SMS setup (Moolre dashboard)

SMS is not enabled by default on a Moolre account. Before using moolre.sms, complete these steps in your Moolre dashboard:

  1. Create an SMS service on your Moolre account (VAS / SMS product).
  2. Top up SMS credits — at least ~5 GHS (or less if Moolre allows a smaller minimum on your plan). Without credits, sends will fail.
  3. Register a Sender ID (e.g. YourBrand) and wait for approval if required.
  4. Copy your SMS API key (X-API-VASKEY) and approved Sender ID into env:
MOOLRE_SMS_API_KEY=your_vas_key
MOOLRE_SMS_SENDER_ID=YourBrand

SMS always calls the live Moolre SMS API (api.moolre.com), even when MOOLRE_MODE=sandbox for payments.

Modes — live vs sandbox

Set MOOLRE_MODE or MOOLRE_ENV (or pass mode to the client):

| Value | API base | When to use | Keys required | |-------|----------|-------------|---------------| | live (default) | https://api.moolre.com | Production payments, payment links, bank accounts, payouts, status | MOOLRE_PRIVATE_API_KEY, MOOLRE_PUBLIC_API_KEY, MOOLRE_ACCOUNT_NUMBER | | sandbox (alias: test) | https://sandbox.moolre.com | Safe integration testing without real money | MOOLRE_USERNAME + MOOLRE_ACCOUNT_NUMBER (or MOOLRE_SANDBOX_ACCOUNT_NUMBER) |

# Production (default)
MOOLRE_MODE=live

# Sandbox
MOOLRE_MODE=sandbox
import { Moolre } from "@morapay/moolre";

const live = Moolre.fromEnv();              // MOOLRE_MODE unset → live
const sandbox = live.sandbox();             // same creds, sandbox base URL
const explicit = live.withMode("live");     // switch back to production

Morapay CLI equivalents (internal test scripts — same APIs this SDK wraps):

| Script | SDK equivalent | Typical mode | |--------|----------------|--------------| | pnpm moolre:sms-test | moolre.sms.send() | live (SMS API only) | | pnpm moolre:payment-link-test | moolre.links.create() | live | | pnpm moolre:bank-test | moolre.virtualAccounts.create() + moolre.banks.list() | live | | pnpm moolre:payment-test | moolre.payments.initiate() / submitOtp() | live | | pnpm moolre:transfer-test | moolre.transfers.initiate() | live |

Install

Works with npm, pnpm, yarn, and bun:

npm install @morapay/moolre
pnpm add @morapay/moolre
yarn add @morapay/moolre
bun add @morapay/moolre

Requires Node.js 18+ (Bun 1.0+ also supported).

Quick start

1. Set environment variables

# Required
MOOLRE_USERNAME=your_moolre_username
MOOLRE_ACCOUNT_NUMBER=your_wallet_account_number

# Live transact (MoMo collection, transfers, validate)
MOOLRE_PRIVATE_API_KEY=your_private_key

# Live payment links, status, virtual bank accounts
MOOLRE_PUBLIC_API_KEY=your_public_key

# Sandbox (integration testing — sandbox.moolre.com)
MOOLRE_MODE=sandbox
MOOLRE_SANDBOX_ACCOUNT_NUMBER=your_sandbox_account

# SMS (optional — always hits live SMS API)
MOOLRE_SMS_API_KEY=your_vas_key
MOOLRE_SMS_SENDER_ID=YourBrand

# Callbacks (optional)
MOOLRE_PAYMENT_CALLBACK_URL=https://yourapp.com/webhooks/moolre/payment
MOOLRE_TRANSFER_CALLBACK_URL=https://yourapp.com/webhooks/moolre/transfer

2. Create a client

import { Moolre } from "@morapay/moolre";

// From env (recommended)
const moolre = Moolre.fromEnv();

// Or explicit config
const moolreLive = new Moolre({
  username: process.env.MOOLRE_USERNAME!,
  accountNumber: process.env.MOOLRE_ACCOUNT_NUMBER!,
  privateKey: process.env.MOOLRE_PRIVATE_API_KEY!,
  publicKey: process.env.MOOLRE_PUBLIC_API_KEY!,
  mode: "live",
});

3. Switch sandbox ↔ live

const sandbox = moolre.sandbox(); // MOOLRE_MODE=sandbox or .withMode("sandbox")
const live = moolre.live();       // MOOLRE_MODE=live (production)

// Or
const client = moolre.withMode("sandbox");

Bank flows

The SDK covers three bank-related flows:

1. Inbound collection — virtual bank accounts

Customer receives a dedicated account number and pays by bank transfer (Ghana / Nigeria):

const banks = await moolre.banks.list({ country: "ghana" });

const account = await moolre.virtualAccounts.create({
  currency: "GHS",
  email: "[email protected]",
  firstName: "Jeffery",
  lastName: "Mintah",
  phone: "0244123456",
  amount: 10, // optional expected amount
});

console.log(account.bankname, account.accountno, account.accountname);
// Customer transfers to accountno for the exact amount

Confirm payment via webhook or moolre.transactions.getStatus({ reference: account.uref }).

2. Hosted payment link (Web POS)

Card / bank checkout on Moolre's hosted page — no OTP step on your side:

const link = await moolre.links.create({
  amount: 5,
  currency: "GHS",
  email: "[email protected]",
  redirectUrl: "https://yourapp.com/success",
});

console.log(link.authorizationUrl); // open in browser

3. Outbound bank payouts

Send to Nigeria or Ghana bank accounts:

const banks = await moolre.banks.list({ country: "nigeria" });

const name = await moolre.validate.accountName({
  receiver: "0123456789",
  channel: 2,
  currency: "NGN",
  bankCode: "058",
});

const payout = await moolre.transfers.initiate({
  railId: "ng_bank_transfer",
  receiver: "0123456789",
  bankCode: "058",
  amount: 5000,
  currency: "NGN",
});

Ghana bank payouts use railId: "gh_bank_transfer" with the bank code from moolre.banks.list({ country: "ghana" }).


Mobile money collection (MoMo)

Supported: Ghana (GHS) and Nigeria (NGN) mobile money.

The SDK auto-generates:

  • externalRef (Morapay-XXXXXX) when omitted
  • currency from phone country (GHS/NGN)
  • channel from phone + optional provider hint

OTP flow (recommended for production)

Most Ghana/Nigeria MoMo collections require OTP:

Step 1: initiate()  → TP14 (otp_required) — Moolre sends OTP SMS to payer
Step 2: submitOtp() → TP17 (otp_verified) → TR099 (payment_requested)
// Step 1 — start collection
const init = await moolre.payments.initiate({
  payerPhone: "0541234567",   // or +233541234567
  amount: 50,                 // major units (50.00 GHS)
  referenceMessage: "Order #1234",
});

if (init.otpRequired) {
  // Show OTP input in your UI — payer receives SMS from Moolre
  console.log("Reference:", init.externalRef);

  const result = await moolre.payments.submitOtp({
    payerPhone: "0541234567",
    amount: 50,
    externalRef: init.externalRef, // must match step 1
    otp: "123456",
  });

  console.log(result.completed, result.code); // true, TR099
}

Non-OTP / one-shot flow

If you already have the OTP (or Moolre returns TR099 directly):

const result = await moolre.payments.collect({
  payerPhone: "0541234567",
  amount: 50,
  externalRef: "Morapay-ABC123",
  otp: "123456", // when provided, runs full OTP → payment flow
});

// Without OTP — initiate only
const pending = await moolre.payments.collect({
  payerPhone: "08031234567",
  amount: 1000,
  currency: "NGN",
  provider: "MTN", // required for Nigeria if channel can't be inferred
});

Nigeria provider hints

| Provider | Pass as provider | |-------------|--------------------| | MTN | "MTN" | | AirtelTigo | "AirtelTigo" | | Telecel | "Telecel" |


Hosted payment links

For card/bank checkout pages hosted by Moolre:

const link = await moolre.links.create({
  amount: 1000,
  currency: "NGN",
  email: "[email protected]",
  callbackUrl: "https://yourapp.com/webhooks/moolre/payment",
  redirectUrl: "https://yourapp.com/payment/success",
});

console.log(link.authorizationUrl); // send customer here
console.log(link.reference);        // Morapay-XXXXXX

Check payment status:

const status = await moolre.transactions.getStatus({
  reference: link.reference,
});

import { mapTxStatusLabel } from "@morapay/moolre";
console.log(mapTxStatusLabel(status.txstatus)); // pending | success | failed

Outbound transfers (payouts)

Send money to Ghana MoMo or Ghana/Nigeria bank accounts.

Important: Transfer channels differ from collection channels. Ghana MTN collection uses channel 13; transfer uses channel 1.

// Ghana mobile money
const payout = await moolre.transfers.initiate({
  railId: "gh_mobile_money",
  receiver: "0537161293",
  amount: 5,
  currency: "GHS",
  provider: "MTN", // optional — defaults to MTN (channel 1)
});

// Nigeria bank
const bankPayout = await moolre.transfers.initiate({
  railId: "ng_bank_transfer",
  receiver: "0123456789",      // account number
  bankCode: "058",             // bank code from moolre.banks.list()
  amount: 5000,
  currency: "NGN",
});

console.log(payout.accepted, payout.phase, payout.moolreId);

Available rails: gh_mobile_money, ng_bank_transfer, gh_bank_transfer.

List banks:

const banks = await moolre.banks.list({ country: "nigeria" });

Validate account name before payout:

const name = await moolre.validate.accountName({
  receiver: "0123456789",
  channel: 2,
  currency: "NGN",
  bankCode: "058",
});

SMS

SMS uses the live Moolre SMS API regardless of mode (sandbox/live for payments).

await moolre.sms.send({
  to: "+233541234567",
  message: "Your order has shipped.",
});

// Payment OTP helper (your own OTP, not Moolre's MoMo OTP)
await moolre.sms.sendPaymentOtp({
  to: "0541234567",
  otp: "482910",
  externalRef: "Morapay-ABC123",
  brandName: "MyStore",
});

Virtual bank accounts

Create dedicated bank account numbers for inbound bank transfers:

const account = await moolre.virtualAccounts.create({
  currency: "NGN",
  email: "[email protected]",
  firstName: "Jane",
  lastName: "Doe",
  phone: "08031234567",
  amount: 5000, // optional expected amount
});

console.log(account.accountno, account.bankname);

Error handling

All API failures throw MoolreError:

import { MoolreError, MoolreValidationError } from "@morapay/moolre";

try {
  await moolre.payments.initiate({ payerPhone: "invalid", amount: 10 });
} catch (error) {
  if (error instanceof MoolreValidationError) {
    // bad input before API call
  }
  if (error instanceof MoolreError) {
    console.log(error.moolreCode, error.statusCode, error.message);
  }
}

Validation errors (invalid phone, missing bank code, etc.) throw MoolreValidationError before any network request.


API reference

| Resource | Methods | Description | |----------|---------|-------------| | moolre.payments | initiate, submitOtp, collect | MoMo collection | | moolre.links | create | Hosted payment links | | moolre.transactions | getStatus | Transaction status lookup | | moolre.transfers | initiate | Outbound payouts | | moolre.banks | list | Bank directory | | moolre.validate | accountName | Account name lookup | | moolre.virtualAccounts | create | Virtual bank accounts | | moolre.sms | send, sendPaymentOtp | SMS via Moolre VAS |

Helpers

import {
  generateExternalRef,
  normalizePhone,
  classifyPaymentInitCode,
  MOOLRE_PAYMENT_INIT_OTP_REQUIRED,
  MOOLRE_TX_STATUS,
} from "@morapay/moolre";

Environment variable reference

| Variable | Required | Description | |----------|----------|-------------| | MOOLRE_USERNAME | Yes | X-API-USER header | | MOOLRE_ACCOUNT_NUMBER | Yes | Wallet account number | | MOOLRE_PRIVATE_API_KEY | Live transact | Private key for payment/transfer/validate | | MOOLRE_PUBLIC_API_KEY | Live links/status | Public key for links, status, virtual bank | | MOOLRE_MODE or MOOLRE_ENV | No | live (default, production) or sandbox / test (integration testing) | | MOOLRE_SANDBOX_ACCOUNT_NUMBER | Sandbox | Override account in sandbox | | MOOLRE_SMS_API_KEY | SMS | VAS key (X-API-VASKEY) | | MOOLRE_SMS_SENDER_ID | SMS | Registered sender id | | MOOLRE_PAYMENT_CALLBACK_URL | No | Default callback for payment links | | MOOLRE_TRANSFER_CALLBACK_URL | No | Default callback for transfers | | MOOLRE_API_BASE_URL | No | Override live API base | | MOOLRE_SANDBOX_API_BASE_URL | No | Override sandbox API base | | MOOLRE_TIMEOUT_MS | No | Request timeout (default 15000) |


Common pitfalls (and how this SDK fixes them)

  1. Wrong channel id — Collection MTN is 13, transfer MTN is 1. The SDK picks the right channel per operation.
  2. Missing OTP follow-up — After TP17, you must call payment again for TR099. submitOtp() and collect({ otp }) do this automatically.
  3. Phone format — Pass 054…, +233…, 080…, or +234…. The SDK normalizes to Moolre's expected local format.
  4. Amount format — Pass 50 or "50.00". The SDK formats to two decimal places.
  5. Sandbox vs live keys — Sandbox only needs username + account number. Live requires private/public keys.
  6. External references — Must be unique per transaction. Auto-generated when omitted.

License

MIT © Morapay