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/sdk

v0.1.0

Published

Morapay merchant integration SDK (payment links, open checkout, webhooks)

Readme

@morapay/sdk

Merchant integration SDK for Morapay — payment links, products, quotes, webhooks, and browser checkout helpers.

Version: 0.1.0 (first public release)

| Package | Install | |---------|---------| | @morapay/sdk | npm install @morapay/sdk | | @morapay/sdk-nextjs | npm install @morapay/sdk-nextjs (Next.js BFF helpers) | | create-morapay-app | npx create-morapay-app |

See CHANGELOG.md.

TypeScript client for the Morapay Merchant API v1.

Install

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

Scaffold an app

npx create-morapay-app
# or: pnpm dlx | yarn dlx | bunx create-morapay-app

| Template | Stack | Checkout | |----------|-------|----------| | Next.js (fiat) | App Router + API routes | Widget modal or redirect | | Next.js (crypto) | + Dynamic/wagmi wallet bridge | Widget + on-site wallet | | React | Vite + Express API | Widget modal or redirect (fiat — no Dynamic) | | Express | API only | Your frontend calls the API | | Python | FastAPI API only | venv + pip — no Node deps in generated app |

Prompts: pick a template; Next.js also asks whether customers connect a crypto wallet on your site.

Run the scaffolder with any of: npx · pnpm dlx · yarn dlx · bunx create-morapay-app.

@morapay/sdk has no wallet dependencies — wallets ship only in the Next.js crypto template.

Crypto payments — when do you need Dynamic?

| What you're building | NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID? | |----------------------|---------------------------------------| | Fiat only (MoMo, card, bank) — any template | No | | Redirect customers to checkout.morapay.io for crypto | No — wallet UI runs on Morapay checkout | | Widget live mode (iframe to hosted checkout) | No — same hosted checkout handles crypto | | Widget preview with crypto quotes + connect wallet on your site | Yes — Next.js crypto template only |

Dynamic is a merchant-site wallet bridge for preview UX. Real crypto charges still settle through Morapay checkout; you do not need a Dynamic project just to accept crypto payments via redirect or live widget iframe.

Python backends

No Python package on PyPI yet. Scaffold a FastAPI backend sample with create-morapay-app (option 4), or browse the reference app:

https://github.com/morapay-app/sdk-examples/tree/main/python-api

After scaffolding, use Python only (venv + pip) — npm/pnpm/yarn/bun are only for running the scaffolder.

Quick start

Server-side only. Never embed sk_* in browsers, mobile apps, or checkout bundles.

Only API keys are required. baseUrl and checkoutBaseUrl default to production Morapay hosts.

import { Morapay } from "@morapay/sdk";

const morapay = new Morapay({
  publicKey: process.env.MORAPAY_PUBLIC_KEY!,
  secretKey: process.env.MORAPAY_SECRET_KEY!,
});

const qr = await morapay.qr.get();
console.log(qr.checkoutUrl, qr.qrPayload);

const { data: links } = await morapay.link();
const link = await morapay.link.create({
  title: "Invoice #42",
  amount: 100,
  currency: "USD",
});

Optional overrides when not using Morapay production:

const morapay = new Morapay({
  publicKey: process.env.MORAPAY_PUBLIC_KEY!,
  secretKey: process.env.MORAPAY_SECRET_KEY!,
  baseUrl: process.env.MORAPAY_BASE_URL,           // default: https://api.morapay.io
  checkoutBaseUrl: process.env.MORAPAY_CHECKOUT_BASE_URL, // default: https://checkout.morapay.io
});

Template apps read the same env vars server-side — omit them to hit production; set them explicitly for a local Core/checkout stack.

Callable resources

List and get share one call signature — pass a query object (or nothing) to list, pass an ID string to fetch one:

const { data: products } = await morapay.products({ limit: 10 });
const product = await morapay.products("prod_123");

const { data: links } = await morapay.link();
const link = await morapay.link("link_abc");

const { data: batches } = await morapay.disbursement();
const batch = await morapay.disbursement("batch_xyz");

const { data: subs } = await morapay.subscriptions();
const sub = await morapay.subscriptions("sub_456");

const { data: plans } = await morapay.subscriptions.plans();
const plan = await morapay.subscriptions.plans("plan_789");

Create, update, and scoped helpers attach to the same function object:

await morapay.products.create({ name: "API Access", price: 500 });
await morapay.products.ensureCheckoutLink("prod_123"); // catalog — reuse or create once
await morapay.products.link("prod_123", { isOneTime: true }); // invoice — always new
await morapay.products.links("prod_123");

await morapay.link.sessions("link_abc");
await morapay.link.quote("link_abc", { chainId: 8453 });

await morapay.disbursement.account();
await morapay.disbursement.quote({ chainId: 8453, token: "USDC", payoutTotal: 250, recipientCount: 10 });
await morapay.disbursement.ledger();

Authentication

Every request is signed with HMAC-SHA256:

| Header | Value | |--------|--------| | Morapay-Key | Public key (pk_live_* or pk_test_*) | | Morapay-Timestamp | Unix seconds | | Morapay-Signature | v1=<hex> |

The SDK builds the canonical payload and signs automatically. TEST/LIVE is determined by the key pair — do not send x-merchant-environment with API keys.

Create key pairs in the dashboard under Developers → API keys. Each key is pinned to TEST or LIVE at creation time.

Resources

| SDK property | API path | |--------------|-----------| | morapay.link() / .create / .update / .sessions / .quote | /links | | morapay.products() / .create / .update / .link / .links | /products, /links | | morapay.disbursement() / .account / .quote / .ledger / .cancel | /disbursement/* | | morapay.qr | /qr | | morapay.quotes.fiat | /quotes/fiat | | morapay.quotes.ramp | /quotes/ramp | | morapay.subscriptions() / .plans / .create / .pause / .cancel | /subscriptions | | morapay.webhooks.endpoints() / .create / .update / .delete / .revealSecret | /webhooks/endpoints | | morapay.utils | local helpers (no network) |

Products and payment tracking

Morapay links fall into two integration patterns:

| Pattern | SDK call | When | |---------|----------|------| | Catalog / product checkout | products.ensureCheckoutLink(productId) | Ecommerce, SaaS plans, fixed-price SKUs — one reusable link per product | | Invoice / order checkout | products.link(productId, { isOneTime: true }) | One payment per order — new link per checkout |

isOneTime: false (unlimited) means the same publicCode accepts many payments. isOneTime: true means the link is consumed after one successful payment.

const product = await morapay.products.create({
  name: "Annual membership",
  price: 49,
  currency: "GHS",
  type: "DIGITAL",
});

// Ecommerce: reuse existing catalog link or create once
const catalog = await morapay.products.ensureCheckoutLink(product.id);
console.log(morapay.buildCheckoutUrl(catalog.publicCode)); // stable for this SKU

// Invoice: fresh link per order (retries dedupe via orderId / idempotencyKey)
const invoice = await morapay.products.link(product.id, {
  isOneTime: true,
  customerEmail: "[email protected]",
  metadata: { orderId: "ord_123" },
  idempotencyKey: "ord_123",
});

Store publicCode on your product row (or in Morapay link metadata) so Buy now opens checkout without creating links on every click.

Invoice retries dedupe server-side when you send Morapay-Idempotency-Key (or metadata.orderId / idempotencyKey in the body). The SDK sets the header automatically from products.link({ idempotencyKey }).

console.log(morapay.utils.isPaid(checkout)); // false until payer completes checkout

const { data: links } = await morapay.products.links(product.id);
for (const link of links) {
  console.log(link.metadata, link.paidAt, link.paidByWalletAddress);
}

Sync utilities

Pure helpers live under morapay.utils — no await, no network:

morapay.utils.isPaid(link);
morapay.utils.verifyWebhook({ payload, signature, secret });

Business QR checkout

const qr = await morapay.qr.get();
// qr.checkoutUrl → https://checkout.morapay.io/pay/{slug}

morapay.buildBusinessPayCheckoutUrl("acme-store");
morapay.buildBusinessPayWidgetUrl("acme-store"); // adds ?embed=1

Frontends

Two integration patterns — pick one with checkoutMode: "widget" | "redirect". Server SDK calls stay the same.

| Pattern | checkoutMode | When to use | |---------|----------------|-------------| | Widget modal | "widget" | Checkout on your page — fiat + crypto via Morapay iframe | | Hosted redirect | "redirect" | Email/SMS links, simple “Pay now” — opens checkout.morapay.io |

import { openPaymentLinkCheckout } from "@morapay/sdk/widget";

await openPaymentLinkCheckout({
  publicCode: link.publicCode,
  checkoutMode: "widget",       // or "redirect"
  widgetExperience: "live",     // widget only: "preview" | "live"
  checkoutBaseUrl: "https://checkout.morapay.io",
  // widgetScriptUrl optional — defaults to Morapay CDN (drop-in, no extra install)
});

Install @morapay/sdk once — server API + browser widget helpers (@morapay/sdk/widget). The widget bundle (morapay-checkout.js) loads from Morapay CDN by default, or self-host at /widget/ in your app.

Checkout modals (hybrid architecture)

Morapay uses a hybrid model like RainbowKit / Web3Modal — not a full-page outer iframe:

  1. Native modal (<morapay-checkout-modal>, Shadow DOM) on the host page — backdrop, scroll lock, wallet list, access to window.ethereum
  2. Secure inner iframe (?hybrid=1&canvas=1) only for card / MoMo / KYC — PCI-sensitive flows stay on checkout.morapay.io
<script src="https://checkout.morapay.io/widget/morapay-checkout.js"></script>
<script>
  MorapayCheckout.openModal({
    mode: "payment-link",
    publicCode: "abc123",
    onSuccess: (payload) => console.log("paid", payload),
  });
</script>

Server-side URL helpers:

morapay.buildPaymentLinkEmbedUrl("abc123", { flow: "onramp" });

Expose a thin server proxy (Next.js route, Cloudflare Worker, etc.) that holds sk_* and forwards signed requests.

Error handling

Every failed API call throws a typed MorapayError subclass with six consultant fields:

| Property | Audience | Purpose | |----------|----------|---------| | message | Developer logs | Technical diagnostic (RFC details, field paths) | | safeMessage | End-customer UI | Safe toast/modal copy — no secrets or internals | | displayMessage | UI components | Intent-driven display copy (@morapay/react, MorapayUi.toast) | | uiHint | UI components | { severity, placement, actionText? } rendering blueprint | | code | Programmatic | Semantic namespace code (links.amount.below_minimum) | | requestId | Support | Correlate with API logs (Morapay-Request-Id header) | | docUrl | Self-serve | Direct link to docs.morapay.io/codes/... | | details | Validation | Field-level Zod or business-rule breakdown |

import {
  Morapay,
  MorapayInvalidRequestError,
  MorapayAuthenticationError,
  MorapayErrorCode,
  isMorapayError,
} from "@morapay/sdk";

try {
  await morapay.products.link("prod_123", { amount: 0.1, currency: "GHS" });
} catch (err) {
  if (err instanceof MorapayInvalidRequestError) {
    showToast(err.safeMessage); // safe for checkout UI
    console.error(err.format()); // consultant layout in terminal
  } else if (err instanceof MorapayAuthenticationError) {
    alertDevOps(err.requestId);
  } else if (isMorapayError(err) && err.code === MorapayErrorCode.PayoutsMomoInsufficientBalance) {
    await queuePayoutRetry(err.requestId);
  }
}

Use MorapayErrorCode for compile-time-safe comparisons — your IDE autocompletes every documented code and catches typos before production.

Browser toast (vanilla JS)

import { MorapayUi, isMorapayError } from "@morapay/sdk";

try {
  await morapay.disbursement.quote({ ... });
} catch (err) {
  MorapayUi.toast(err, { onAction: () => location.reload() });
}

Morapay.ui.toast is also available on the client instance.

Docs & examples

  • API reference: https://docs.morapay.io
  • Starter apps: npx create-morapay-app (Next.js, React, Express, or Python)
  • Python reference: https://github.com/morapay-app/sdk-examples/tree/main/python-api
  • Next.js integration helpers: https://github.com/morapay-app/sdk/tree/main/nextjs

License

MIT — see LICENSE.