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

mbpay

v0.1.0

Published

Official Node.js SDK for the mbpay payment gateway — accept bKash, Nagad, Rocket, Upay, and Tap payments by verifying user-submitted transaction IDs against the merchant's SMS feed.

Downloads

145

Readme

mbpay

Official Node.js SDK for the mbpay payment gateway. Accept bKash, Nagad, Rocket, Upay, and Tap payments from your Bangladeshi customers by verifying their user-submitted transaction IDs against the merchant device's SMS feed. No funds custody — mbpay just confirms the payment happened and notifies your server.

npm install mbpay

Requires Node.js 18+ (uses globalThis.fetch). Server-side only — keep the secret key off the browser.

Quick start

import { Mbpay } from 'mbpay';

const mbpay = new Mbpay({
  publicKey:  process.env.MBPAY_PUBLIC_KEY!,
  secretKey:  process.env.MBPAY_SECRET_KEY!,
  hmacSecret: process.env.MBPAY_HMAC_SECRET!,   // for verifyWebhook()
  // apiBaseUrl defaults to https://api.mbpay.bd
});

// 1. On every deposit-page render, ask what payment options are live for your brand.
//    The wallet is randomly load-balanced across your active MFS pool.
const options = await mbpay.getPaymentOptions();
// → [{ provider: 'bkash', channel: 'personal', channel_label: 'Send Money',
//      wallet: { id, phone_number, label }, wallet_count }, ...]

// 2. When the user picks a method and submits their deposit amount:
const payment = await mbpay.initiatePayment({
  mfsAccountId: options[0].wallet.id,
  amount: 500.00,
  endUserRef: 'order_42',                  // echoed back on the webhook
  successUrl: 'https://example.com/paid',  // optional
  failUrl:    'https://example.com/failed' // optional
});

// 3. Redirect the user to mbpay's hosted checkout.
return Response.redirect(payment.checkout_url, 303);

The user lands on mbpay's checkout, sees the wallet number + amount, sends from their MFS app, and pastes the TrxID. mbpay matches against the merchant's incoming SMS and fires a signed webhook to your server.

Webhook handler (Express)

import express from 'express';
import { mbpay } from './mbpay';

const app = express();

// IMPORTANT: capture the RAW body before any JSON parsing — the signature
// is computed over the exact bytes mbpay sent.
app.post(
  '/mbpay/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const rawBody = req.body.toString('utf8');
    const sig = req.header('x-mbpay-signature');

    if (!mbpay.verifyWebhook(rawBody, sig)) {
      return res.status(401).send('bad signature');
    }

    const event = JSON.parse(rawBody) as import('mbpay').WebhookPayload;
    // event.status === 'matched'
    // event.end_user_ref === your `endUserRef` from initiatePayment
    // event.txn_id is the TrxID the user submitted
    //
    // Idempotency: mbpay retries up to 10 times. Dedupe on event.id.
    creditOrderIdempotent(event.end_user_ref, event.amount);

    res.status(200).send('ok');
  },
);

Webhook handler (Next.js Route Handler)

// app/api/mbpay-webhook/route.ts
import type { NextRequest } from 'next/server';
import { mbpay } from '@/lib/mbpay';
import type { WebhookPayload } from 'mbpay';

export async function POST(req: NextRequest): Promise<Response> {
  const rawBody = await req.text();           // raw bytes for signature
  const sig = req.headers.get('x-mbpay-signature');

  if (!mbpay.verifyWebhook(rawBody, sig)) {
    return new Response('bad signature', { status: 401 });
  }

  const event = JSON.parse(rawBody) as WebhookPayload;
  await creditOrderIdempotent(event.end_user_ref, event.amount);

  return new Response('ok', { status: 200 });
}

Polling fallback

If your endpoint can't accept inbound webhooks (e.g. behind a corporate firewall), call getPaymentStatus until the status is final (matched, expired, or failed):

const status = await mbpay.getPaymentStatus(paymentId);
// status.status, status.submittedTxnId, status.matchedAt

API reference

new Mbpay(config)

| Field | Type | Notes | |---|---|---| | publicKey | string | Required. pk_test_… or pk_live_… | | secretKey | string | Required. Never expose to a browser. | | hmacSecret | string? | Required only if you call verifyWebhook(). | | apiBaseUrl | string? | Defaults to https://api.mbpay.bd. Override for self-hosted. | | fetch | typeof fetch? | Inject a custom fetch (useful for tests). |

Methods

| Method | Returns | |---|---| | getPaymentOptions() | Promise<PaymentOption[]> — currently active (provider, channel) groups with one randomly-picked wallet each | | initiatePayment(input) | Promise<InitiateResponse> — includes checkout_url to redirect the user to | | getPaymentStatus(paymentId) | Promise<PaymentStatusResponse> | | verifyWebhook(rawBody, signatureHeader) | booleantrue iff the HMAC matches AND timestamp is within ±5 min |

All network methods throw MbpayError on non-2xx responses (the error carries .status and .body).

Authentication recipe (for reference)

Every outgoing request is signed with three headers:

X-Brand-Key:  pk_test_xxxxxxxxxxxx
X-Timestamp:  1716906000
X-Signature:  hex(HMAC_SHA256(secretKey, `${timestamp}.${rawBody}`))

GET requests sign over an empty body (""). The SDK handles all of this for you.

Webhook signature format

X-Mbpay-Signature: t=1716906321,v1=ab12cd34...

v1 = hex(HMAC_SHA256(hmacSecret, "${t}.${rawBody}")). Verify against the raw bytes you received — re-stringifying parsed JSON will fail the signature.

TypeScript

Full types are bundled. Import what you need:

import type {
  MbpayConfig,
  PaymentOption,
  InitiateInput,
  InitiateResponse,
  PaymentStatusResponse,
  WebhookPayload,
  MbpayProvider,
  MbpayChannel,
  MbpayStatus,
} from 'mbpay';

License

MIT