MenuPay logoMenuPay.

Developer Integration Guide

Integrate bKash, Nagad & Rocket checkout into your site in under 5 minutes.

Your API Base URL (auto-detected)https://your-domain.com/api

Step 1 — Get Your API Keys

Go to Dashboard → API Keys and generate a key pair. You will receive:

Public Keypk_live_xxxxxxxxxxxxxxxxxxxx

Safe to expose in frontend / checkout embeds.

Secret Keysk_live_xxxxxxxxxxxxxxxxxxxx

Server-side only. Never expose in frontend code.

Step 2 — Payment Flow

Step 1POST /initiate

Your backend calls initiate with secret key to get a checkout URL.

Step 2Redirect Customer

Redirect customer to the returned checkout_url.

Step 3Customer Pays

Customer selects bKash / Nagad / Rocket and completes payment.

Step 4Webhook Fired

MenuPay posts a signed callback to your callback_url.

Step 3 — API Endpoints

POST
https://your-domain.com/api/payment/initiate

Create a new checkout session. Returns checkout_url to redirect customer.

GET
https://your-domain.com/api/payment/status?id=:payment_id

Check the current status of any payment session.

POST
https://your-domain.com/api/payment/verify

Manually verify a customer-submitted TxnID against a payment session.

Step 4 — Webhook Verification

After a successful payment, MenuPay sends a signed HTTP POST to your callback_url. Verify the signature in the header before trusting the payload.

Signature Header:x-menupay-signature
Algorithm:HMAC-SHA256 of raw JSON body using your Webhook Secret
webhook-handler.ts
// Webhook Verification (NodeJS / Express / Next.js API Route)
import crypto from 'crypto';

export async function POST(req: Request) {
  const body = await req.json();
  const signature = req.headers.get('x-menupay-signature') ?? '';
  const webhookSecret = process.env.MENUPAY_WEBHOOK_SECRET!;

  // Generate local HMAC SHA256 signature
  const localSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(JSON.stringify(body))
    .digest('hex');

  // Constant-time comparison to prevent timing attacks
  const isValid = crypto.timingSafeEqual(
    Buffer.from(localSignature, 'utf-8'),
    Buffer.from(signature, 'utf-8')
  );

  if (!isValid) {
    return Response.json({ error: 'Invalid signature' }, { status: 401 });
  }

  // Process the event
  const { event, payment_id, status, amount, reference } = body;
  if (event === 'payment.success') {
    // Mark the order as paid in your database
    await markOrderPaid(reference, payment_id);
  }

  return Response.json({ received: true });
}
POST /api/payment/initiate
// Initiate Payment (NodeJS / Fetch API)
const initiatePayment = async () => {
  const response = await fetch('https://your-domain.com/api/payment/initiate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer sk_live_your_secret_key_here'
    },
    body: JSON.stringify({
      amount: 1250.00,
      currency: 'BDT',
      reference: 'inv_982711',
      customer_name: 'Rahat Kabir',
      customer_phone: '01729112233',
      customer_email: 'rahat@gmail.com',
      success_url: 'https://yoursite.com/success',
      cancel_url: 'https://yoursite.com/cancel',
      callback_url: 'https://yoursite.com/api/webhook'
    })
  });

  const data = await response.json();
  if (data.success) {
    // Redirect customer to the hosted checkout page
    window.location.href = data.checkout_url;
  } else {
    console.error('Initiation failed:', data.error);
  }
};
Response — 200 OKsuccess: true
{
  "success": true,
  "payment_id": "pay_cfbc4c4be67f39",
  "checkout_url": "https://your-domain.com/checkout/pay_cfbc4c4b...",
  "amount": 1250.00,
  "currency": "BDT",
  "reference": "inv_982711",
  "expires_at": "2026-06-04T20:00:00Z"
}
Response — 401 Unauthorizedsuccess: false
{
  "success": false,
  "error": "Invalid or missing API secret key"
}
Webhook Payloadx-menupay-signature: ...
{
  "event": "payment.success",
  "payment_id": "pay_cfbc4c4be67f39",
  "reference": "inv_982711",
  "amount": 1250.00,
  "currency": "BDT",
  "gateway": "bkash",
  "txn_id": "8K2ML9P8D1",
  "status": "completed",
  "timestamp": "2026-06-04T19:55:22Z"
}