PayBridgeNP

PayBridgeNP

Nepal payments

Back to Blog
payment-gatewaynepaldeveloper

Online Payment Security in Nepal: What Merchants Need to Know

A practical guide to payment security for Nepali merchants - webhook signatures, HTTPS, fraud prevention, and PCI compliance.

March 5, 20264 min readPayBridgeNP Team
Online Payment Security in Nepal: What Merchants Need to Know

Accepting online payments comes with security responsibilities. This guide covers the practical security measures every Nepali merchant should have in place - whether you're a developer building a custom integration or a business owner using a plugin.

The Most Common Security Mistakes in Nepal

Before diving into best practices, here are the mistakes we see most often:

  1. Verifying screenshots as payment proof - Easily faked. Never manually verify payments based on screenshots alone.
  2. Not verifying webhook signatures - Allows anyone to trigger fake payment notifications to your system.
  3. Hardcoding API keys in frontend code - Your secret key becomes public, anyone can make charges or access your data.
  4. Not using HTTPS - Payment data transmitted over HTTP is readable to network attackers.
  5. Trusting the amount from the redirect - Always verify the amount server-side via the API, not from URL parameters.

Always Use HTTPS

This is table stakes. Every page that handles payment data - including your order confirmation page and webhook endpoint - must be served over HTTPS.

Free SSL certificates are available via Let's Encrypt. Most hosting providers (SiteGround, Cloudflare, etc.) include SSL by default.

If your webhook endpoint isn't on HTTPS, PayBridgeNP will refuse to send events to it.

Verify Webhook Signatures

The most common security vulnerability in payment integrations is not verifying webhook signatures. Without verification, anyone can POST fake events to your webhook endpoint and trigger order fulfillment without payment.

PayBridgeNP signs every webhook with your webhook secret using HMAC-SHA256:

import { createHmac } from "crypto";

function verifyWebhookSignature(
  rawBody: string,
  signature: string,
  secret: string
): boolean {
  const expected = createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return `sha256=${expected}` === signature;
}

// In your webhook handler:
const isValid = verifyWebhookSignature(
  req.rawBody,       // Must be the raw body string, not parsed JSON
  req.headers["paybridge-signature"],
  process.env.PAYBRIDGE_WEBHOOK_SECRET!
);

if (!isValid) {
  return res.status(401).end(); // Reject invalid requests
}

Important: You need the raw request body (before JSON parsing) for signature verification. In Express, use express.raw() on your webhook route, not express.json().

Keep API Keys Server-Side

Your PayBridgeNP secret key (sk_live_...) must never appear in:

  • Client-side JavaScript
  • Mobile app source code
  • Public GitHub repositories
  • Browser DevTools

If a secret key is compromised, rotate it immediately in your PayBridgeNP dashboard.

What's never safe to expose:

  • Your secret key (sk_live_... or sk_test_...) must remain server-side only - PayBridgeNP has no client-safe publishable key

Use environment variables:

# .env (never commit this file)
PAYBRIDGE_SECRET_KEY=sk_live_your_key_here
PAYBRIDGE_WEBHOOK_SECRET=whsec_your_secret_here
// Reference via environment variables only
const paybridge = new PayBridge({
  apiKey: process.env.PAYBRIDGE_API_KEY!,
});

Verify Payment Amounts Server-Side

Never trust the amount from a redirect URL or client-side code. Always verify via the API:

// BAD: trusting amount from URL parameter
const paidAmount = req.query.amount; // attacker can modify this!

// GOOD: verify via API
const payment = await paybridge.payments.retrieve(paymentId);
if (payment.amount !== expectedAmount) {
  throw new Error("Payment amount mismatch");
}

Idempotent Order Fulfillment

Handle duplicate webhook deliveries gracefully. PayBridgeNP guarantees at-least-once delivery, meaning you may receive the same event more than once:

case "payment.succeeded": {
  const order = await db.orders.findById(event.data.metadata.orderId);
  
  // Check before acting
  if (order.status === "paid" || order.status === "fulfilled") {
    return res.json({ received: true }); // Already handled, no-op
  }
  
  await db.orders.markAsPaid(order.id, event.data.id);
  await fulfillOrder(order.id);
  break;
}

Protect Your Admin Panel

Your PayBridgeNP dashboard has full access to your payment data and can trigger refunds. Protect it:

  1. Use a strong, unique password
  2. Enable two-factor authentication (2FA) if available
  3. Don't share access credentials
  4. Review API key access regularly under Settings > API Keys

What PayBridgeNP Handles For You

PayBridgeNP is designed to handle the hardest security problems so you don't have to:

  • PCI compliance - Card data (if any) is handled on PayBridgeNP's certified infrastructure, not your servers
  • Provider authentication - Khalti and eSewa credentials are stored encrypted in our vault
  • Fraud detection - Transaction monitoring flags suspicious activity
  • TLS 1.2+ enforcement - All API traffic is encrypted in transit
  • Signed webhooks - HMAC-SHA256 signatures on every event

Security Checklist for Merchants

Before going live, verify:

  • HTTPS enabled on your site
  • Webhook signature verification implemented
  • API secret key in environment variable, not hardcoded
  • Payment amounts verified server-side, not from client/URL
  • Order fulfillment is idempotent (handles duplicate webhooks)
  • Webhook endpoint returns 200 quickly (don't do slow work before responding)
  • Test webhook delivery in PayBridgeNP dashboard

For developers building on PayBridgeNP, our security documentation has more depth. If you're using the WooCommerce checkout, the plugin handles webhook registration automatically. For custom API integration, implement the checklist above before going live. Questions? Email security@paybridgenp.com.


Related Articles

Ready to accept payments in Nepal?

Connect Khalti, eSewa, and ConnectIPS with a single API. Free to start.