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

@mopay/node-sdk

v0.1.6

Published

Official TypeScript SDK for the MoPay payment API.

Readme

MoPay Node SDK

TypeScript SDK for creating and verifying MoPay hosted payment sessions from Node.js.

MoPay's flow is:

  1. Create a payment session from your backend.
  2. Redirect the customer to the returned paymentUrl.
  3. When MoPay redirects the customer back, verify the final state with the session API.

Installation

pnpm add @mopay/node-sdk

Usage

import { MoPay } from "@mopay/node-sdk";

const mopay = new MoPay({
  apiKey: process.env.MOPAY_API_KEY!,
});

const session = await mopay.createPaymentSession({
  amount: "100.00",
  reference: "ORDER12345",
  redirectUrl: "https://yourwebsite.com/payment-complete",
  description: "Order #12345",
  customerEmail: "[email protected]",
  customerName: "John Doe",
});

console.log(session.paymentUrl);

Browser Checkout

Use the secret-key SDK only on the merchant backend. The frontend should receive a checkout URL or token from the merchant backend, then call MoPayCheckout.open({ checkoutUrl }).

Merchant backend:

import { MoPay } from "@mopay/node-sdk";

const mopay = new MoPay({
  apiKey: process.env.MOPAY_API_KEY!,
});

const session = await mopay.createPaymentSession({
  amount: "100.00",
  reference: "ORDER12345",
  redirectUrl: "https://yourwebsite.com/payment-complete",
});

return {
  sessionId: session.sessionId,
  checkoutUrl: session.paymentUrl,
};

Merchant frontend:

import { MoPayCheckout } from "@mopay/node-sdk";

const session = await fetch("/api/create-mopay-checkout", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ amount: "100.00", reference: "ORDER12345" }),
}).then((response) => response.json());

MoPayCheckout.open({
  checkoutUrl: session.checkoutUrl,
  allowedOrigins: [window.location.origin],
  width: 520,
  height: 760,
  resizable: true,
  closeOnRedirect: true,
  redirectAfterClose: true,
  onSuccess: () => {
    // UI feedback only. Do not deliver value here.
  },
  onClose: async () => {
    await fetch(`/api/verify-mopay-session/${session.sessionId}`);
  },
});

Supported modes:

  • dialog: opens an iframe dialog inside your UI. This is the default.
  • popup: opens the hosted MoPay page in a centered browser popup.
  • redirect: sends the current page to paymentUrl.

For dialog mode, width, height, minWidth, minHeight, maxWidth, and maxHeight control the SDK-owned frame around the hosted MoPay page. Set resizable: true if customers should be able to drag-resize the checkout frame.

For iframe checkout, MoPay's hosted page must allow embedding from approved merchant sites. The MoPay checkout server should set Content-Security-Policy: frame-ancestors ... dynamically from the merchant's registered allowed origins and avoid X-Frame-Options: DENY or SAMEORIGIN for embeddable checkout pages.

The SDK only accepts postMessage events from the checkout URL origin, and only from the opened iframe or popup window. Frontend callbacks are for UI feedback only; merchants must confirm payment through signed webhooks or mopay.getTransaction(sessionId) before delivering value.

If your checkout iframe can redirect back to a merchant-owned completion page, pass that exact origin in allowedOrigins. The checkout URL origin is always trusted automatically.

Closing Checkout After Redirect

The SDK cannot inspect or interrupt a cross-origin iframe while it is still on MoPay, 3DS, or a bank page. The standard pattern is to make your redirectUrl a small merchant-owned bridge page. Once the iframe or popup reaches that page, the bridge page notifies the parent SDK, then the SDK can close the dialog or popup and optionally redirect the main merchant page.

Merchant page:

MoPayCheckout.open({
  checkoutUrl: session.checkoutUrl,
  allowedOrigins: [window.location.origin],
  closeOnRedirect: true,
  redirectAfterClose: true,
});

Completion bridge page:

<script type="module">
  import { MoPayCheckout } from "/sdk/index.js";

  MoPayCheckout.completeRedirect({
    redirectTo: `/payment-success${window.location.search}`,
  });
</script>

If redirectAfterClose is true, the SDK uses the redirectTo value from the bridge message. You can also pass a fixed URL string or a function:

MoPayCheckout.open({
  checkoutUrl,
  closeOnRedirect: true,
  redirectAfterClose: (message) => `/orders/${message.reference}`,
});

Use closeOnRedirect: true without redirectAfterClose if you want to close the iframe or popup but keep the main merchant page where it is.

Redirect URL Handling

Your redirectUrl belongs to your merchant application, not the browser SDK. Make the route accept both redirect shapes:

  • GET /payment-complete?status=success&sessionId=...
  • POST /payment-complete with Content-Type: application/x-www-form-urlencoded

Some checkout or 3DS flows return with a form POST, so a GET-only route can show a broken iframe or browser error even when the payment completed.

Express example:

import express from "express";
import { MoPay, parseRedirect } from "@mopay/node-sdk";

const app = express();
const mopay = new MoPay({ apiKey: process.env.MOPAY_API_KEY! });

app.use(express.urlencoded({ extended: false }));

app.all("/payment-complete", async (req, res) => {
  const params = new URLSearchParams();

  for (const [key, value] of Object.entries(req.query)) {
    if (value !== undefined) params.set(key, String(value));
  }

  if (req.body && typeof req.body === "object") {
    for (const [key, value] of Object.entries(req.body)) {
      if (value !== undefined) params.set(key, String(value));
    }
  }

  const redirect = parseRedirect(params);
  const verified = await mopay.getTransaction(redirect.sessionId);

  if (mopay.isSuccessful(verified)) {
    // Mark the order paid. Do not rely only on the redirect parameters.
  }

  res.redirect(303, `/payment-complete?${params.toString()}`);
});

Next.js App Router example:

import { NextResponse } from "next/server";
import { MoPay, parseRedirect } from "@mopay/node-sdk";

const mopay = new MoPay({ apiKey: process.env.MOPAY_API_KEY! });

export async function GET(request: Request) {
  return handleRedirect(request);
}

export async function POST(request: Request) {
  return handleRedirect(request);
}

async function handleRedirect(request: Request) {
  const url = new URL(request.url);
  const params = new URLSearchParams(url.searchParams);

  if (request.method === "POST") {
    const body = await request.formData();
    for (const [key, value] of body.entries()) {
      params.set(key, String(value));
    }
  }

  const redirect = parseRedirect(params);
  const verified = await mopay.getTransaction(redirect.sessionId);

  if (mopay.isSuccessful(verified)) {
    // Mark the order paid. Do not deliver value from frontend callbacks alone.
  }

  return NextResponse.redirect(new URL(`/payment-complete?${params.toString()}`, url), 303);
}

For iframe checkout, your completion page can call MoPayCheckout.completeRedirect(...) or window.parent.postMessage(...) so the checkout dialog updates the merchant UI. Treat that message as UI feedback only; fulfilment should still depend on webhooks or mopay.getTransaction(sessionId).

Verify A Redirect

Redirect parameters are useful for routing your user, but they should not be treated as the final source of truth. Always verify with MoPay from your backend.

import { MoPay, parseRedirect } from "@mopay/node-sdk";

const redirect = parseRedirect(
  "https://yourwebsite.com/payment-complete?status=success&reference=ORDER12345&transactionId=TXN123&sessionId=MOP_abc123_ORDER12345&amount=100.00&paymentMethod=mpesa",
);

const mopay = new MoPay({ apiKey: process.env.MOPAY_API_KEY! });
const verified = await mopay.retrieveSession(redirect.sessionId);

if (verified.session.status === "COMPLETED") {
  // Fulfil the order.
}

API

new MoPay(options)

const mopay = new MoPay({
  apiKey: "YOUR_API_KEY",
  baseUrl: "https://mopay.co.ls",
  timeoutMs: 30000,
});

apiKey is required. baseUrl is optional and defaults to https://mopay.co.ls.

mopay.createPaymentSession(params)

Creates a hosted payment session with POST /api/external/payment.

Required fields:

  • amount: string or number, for example "100.00".
  • reference: unique alphanumeric order reference, for example "ORDER12345".
  • redirectUrl: absolute URL where MoPay should send the customer after payment.

Optional fields:

  • description
  • customerEmail
  • customerName
  • paymentFrequency: ONCE, MONTHLY, or ANNUALLY
  • productName
  • productDetails
  • productImage
  • payWhatYouWant
  • minimumAmount

mopay.retrieveSession(sessionId)

Retrieves session details with GET /api/external/session/v1/{sessionId}.

mopay.parseRedirect(input) or parseRedirect(input)

Parses MoPay redirect query parameters from a URL, URLSearchParams, or plain object.

MoPayCheckout.open({ checkoutUrl, ...options })

Opens the hosted checkout page in popup, dialog, or redirect mode.

Accepts checkoutUrl, checkout_url, checkoutToken, or checkout_token. checkoutUrl is the same value as the documented paymentUrl returned by createPaymentSession.

Set closeOnRedirect: true to close the dialog or popup when a trusted merchant completion page sends a mopay.checkout.redirect, mopay.checkout.close, or terminal status message. Set redirectAfterClose to true, a URL string, a URL, or a function if the main merchant page should navigate after the checkout frame closes.

MoPayCheckout.completeRedirect(options)

Browser helper for the merchant-owned completion page. It reads MoPay redirect query parameters from window.location.search, posts a mopay.checkout.redirect message to window.parent and window.opener, and returns the posted message.

mopay.checkout(params, options)

Convenience method that creates a session and opens the checkout dialog. Use this only from a trusted environment because it requires the secret API key.

mopay.getSession(sessionId) / mopay.getTransaction(sessionId)

Retrieves the latest MoPay session and transaction data for any session ID.

mopay.isSuccessful(session)

Returns true when a retrieved session is completed or has transactionStatus: "success".

mopay.isPaymentSuccessful(sessionId)

Retrieves the session by ID and returns true when the payment is successful.

mopay.waitForSession(sessionId, options)

Polls MoPay until a session reaches a terminal state such as COMPLETED, FAILED, CANCELLED, or EXPIRED.

Sandbox Mobile Money Numbers

Use these only while your MoPay project is in sandbox mode.

| Method | Number | Expected result | | --- | --- | --- | | M-Pesa | 52211111 | Immediate success | | M-Pesa | 52222222 | Pending first, then success | | M-Pesa | 52233333 | Failed | | M-Pesa | 52244444 | Cancelled | | M-Pesa | 52255555 | Expired | | EcoCash | 63211111 | Immediate success | | EcoCash | 63222222 | Pending first, then success | | EcoCash | 63233333 | Failed | | EcoCash | 63244444 | Cancelled | | EcoCash | 63255555 | Expired |