Skip to content

Nepal Payment Gateway SDK (eSewa & Khalti)

A developer-first, typesafe, server-side TypeScript SDK for integrating Nepal’s leading digital payment gateways: eSewa (ePay v2) and Khalti (ePayment v2). Built for modern backend runtimes with strict integer paisa arithmetic, mandatory server-to-server (S2S) verification, and zero client-side credential leaks.

Install via your preferred package manager (Node 18+ required):

Terminal window
npm install @bivekkk/nepay
Terminal window
# Alternative package managers
pnpm add @bivekkk/nepay
yarn add @bivekkk/nepay
bun add @bivekkk/nepay

The SDK provides two core functions: initiatePayment() and verifyPayment(). Passing method: "esewa" or method: "khalti" automatically discriminates input parameters and return types with full TypeScript intellisense.

import { initiatePayment } from "@bivekkk/nepay";
// 1. Initiate eSewa payment
const checkout = await initiatePayment({
method: "esewa",
config: {
productCode: process.env.ESEWA_PRODUCT_CODE || "EPAYTEST",
secretKey: process.env.ESEWA_SECRET_KEY || "8gBm/:&EnhH.1/q",
successUrl: "https://yourdomain.com/api/payment/callback/esewa",
failureUrl: "https://yourdomain.com/api/payment/callback/esewa",
env: "sandbox",
},
order: {
orderId: "INV-2024-001",
amountPaisa: 10000, // NPR 100.00 (float-safe integer paisa)
},
});
// Returns form target URL and cryptographic HMAC-SHA256 signature fields:
// { url: "https://rc-epay.esewa.com.np/api/epay/main/v2/form", fields: { ... } }
  • method: "esewa": Discriminates the input schema using Zod, ensuring only eSewa-specific options are accepted and returning { url, fields }.
  • config.productCode: Your merchant identifier ("EPAYTEST" in UAT sandbox).
  • config.secretKey: The HMAC secret key used for signing payload fields. Keep this server-side only.
  • order.orderId: Unique transaction ID in your database (transaction_uuid in eSewa).
  • order.amountPaisa: Always integer paisa. 10000 paisa represents exactly NPR 100.00.

Step 2: Mandatory Server-to-Server Verification

Section titled “Step 2: Mandatory Server-to-Server Verification”

When the customer finishes payment, eSewa redirects back to your successUrl with a base64 encoded payload. verifyPayment() validates the signature and queries eSewa’s status endpoint to confirm receipt before marking the order complete.

import { verifyPayment } from "@bivekkk/nepay";
// 2. Verify eSewa callback on your server
const result = await verifyPayment({
method: "esewa",
config: {
productCode: process.env.ESEWA_PRODUCT_CODE || "EPAYTEST",
secretKey: process.env.ESEWA_SECRET_KEY || "8gBm/:&EnhH.1/q",
env: "sandbox",
},
data: req.query.data as string, // Base64 payload from eSewa redirect
expectedOrderId: "INV-2024-001",
expectedAmountPaisa: 10000,
});
if (result.status === "completed") {
// Safe to mark paid in your database
console.log("Payment confirmed! Gateway Ref ID:", result.traceId);
} else {
console.error("Verification failed:", result.status);
}

Browser redirects can be intercepted, spoofed, or replayed by malicious users. The SDK guarantees:

  1. Timing-Safe HMAC Check: Validates the cryptographic digest with crypto.timingSafeEqual against the received raw string.
  2. Gateway Lookup: Queries the gateway’s server endpoint to verify the transaction status and amount.
  3. Exact Amount Match: Checks that the paid amount matches expectedAmountPaisa to the exact paisa.

Gateway Protocol / Version Initiate Mechanism Verification Mechanism Environment
eSewa ePay v2 HTML Form POST + HMAC-SHA256 S2S Status API + Base64 Callback Sandbox (UAT) & Production
Khalti ePayment v2 (KPG-2) REST JSON (/epayment/initiate/) S2S Lookup API (/epayment/lookup/) Sandbox & Production
FonePay Dynamic QR / Web Direct Merchant API Webhook + S2S Query Phase 2 Roadmap

  • Float-Safe Integer Paisa: All amounts in the API are integer paisa (1 NPR = 100 Paisa). Eliminates JavaScript float drift (0.1 + 0.2 !== 0.3).
  • Server-Only Security: Secret keys never leak to frontend bundles. All cryptographic operations run inside Node.js or server runtimes.
  • Resilient Formatting: Automatic parsing of eSewa callback quirks (e.g. comma strings "1,000.0"), GET vs POST callback normalization, and Khalti HTTP 400 Expired body handling.
  • Dual ESM & CommonJS: Ships with dual entrypoints and TypeScript definition files for complete compatibility with Next.js, Remix, Express, NestJS, and Fastify.