
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
If you are building a SaaS platform or e-commerce store targeting Nepali customers, you must accept online payments in Nepal: eSewa and Khalti integration is effectively mandatory. While international gateways like Stripe remain inaccessible for domestic NPR transactions, these two wallets dominate the local market with over 90% combined digital wallet share. This guide provides the architectural patterns, security validations, and implementation details needed to integrate them reliably into your application stack.
How Do You Architect Secure Payment Flows for eSewa and Khalti?
Payment integration fails when developers treat the frontend redirect as the source of truth. A robust architecture for accept online payments in Nepal: eSewa and Khalti integration always centers on server-to-server validation. The browser merely initiates the flow; your backend confirms it. Whether you are deploying on a Laravel VPS setup or a Node.js container, the sequence remains identical.
The critical path involves four distinct phases. First, your server generates a signed request containing the amount, transaction UUID, and callback URLs. Second, the user is redirected to the wallet’s hosted checkout or completes payment via SDK. Third—and most importantly—the gateway sends an asynchronous IPN (Instant Payment Notification) or webhook directly to your backend. Fourth, your server verifies the signature against your secret key before updating order status. Never skip step three. Client-side JavaScript can be manipulated; server-side HMAC verification cannot.
Environment Configuration Best Practices
Store credentials outside your codebase. For teams managing infrastructure with Terraform or similar IaC tools, inject secrets via environment variables or a vault at deploy time. Your .env should contain:
ESEWA_MERCHANT_ID=EPAYTEST
ESEWA_SECRET_KEY=8gBm/:&EnhH.1/q
KHALTI_PUBLIC_KEY=test_public_key_xxxx
KHALTI_SECRET_KEY=test_secret_key_yyyy
PAYMENT_WEBHOOK_URL=https://api.yoursite.com/payments/verify Always use separate keys for sandbox and production. Test transactions in sandbox do not move real money but exercise the exact same verification logic you will rely on during peak sales periods.
What Are the Key Differences Between eSewa and Khalti APIs?
Choosing between providers—or deciding to support both—requires understanding their technical trade-offs. Both allow you to accept online payments in Nepal: eSewa and Khalti integration differs primarily in authentication style, webhook reliability, and ecosystem reach.
| Criteria | eSewa | Khalti |
|---|---|---|
| Authentication | HMAC-SHA256 Signature | Bearer Token / API Key |
| Verification Method | IPN POST + Signature Check | Server-Side Lookup API |
| Sandbox Stability | Moderate (occasional downtime) | High (consistent test env) |
| User Base (2026) | Larger mass-market adoption | Stronger urban/youth demographic |
| Documentation Quality | Adequate but dated examples | Modern REST docs + SDKs |
| Settlement Time | T+1 to T+2 business days | T+1 business days |
In practice, eSewa’s signature-based approach feels more traditional but aligns well with banking-grade audit trails. Khalti’s lookup-based verification simplifies initial coding but adds an extra API call per transaction. I recommend integrating both if your revenue depends on conversion rate; dropping either leaves 30–40% of potential customers without their preferred payment method.
How Do You Implement Server-Side Verification Correctly?
This is where most integrations fail silently. Developers see a "success" screen and mark orders paid without cryptographic proof. When you accept online payments in Nepal: eSewa and Khalti integration demands rigorous backend validation.
eSewa Signature Verification Example
eSewa signs payloads using HMAC-SHA256. Reconstruct the message string exactly as documented, then compare:
import hmac, hashlib, base64
def verify_esewa_signature(payload: dict, secret: str) -> bool:
message = f"transaction_code={payload['transaction_code']},status={payload['status']},total_amount={payload['total_amount']}"
expected = base64.b64encode(
hmac.new(secret.encode(), message.encode(), hashlib.sha256).digest()
).decode()
return hmac.compare_digest(expected, payload.get('signature', '')) Note the use of hmac.compare_digest instead of ==. This prevents timing attacks that could leak signature bytes. Always validate total_amount matches your stored order value—never trust the amount sent by the gateway alone.
Khalti Verification Lookup Pattern
Khalti uses a simpler token-authenticated lookup. After receiving the pidx from the client or webhook, query their verification endpoint server-side:
import requests
def verify_khalti_payment(pidx: str, secret_key: str, expected_amount: int) -> dict | None:
resp = requests.post(
"https://khalti.com/api/v2/payment/lookup/",
headers={"Authorization": f"Key {secret_key}"},
json={"pidx": pidx},
timeout=10
)
if resp.status_code != 200:
return None
data = resp.json()
if data.get("status") == "Completed" and data.get("total_amount") == expected_amount:
return data
return None Cache negative results briefly to avoid hammering the API during retry storms, but never cache successful verifications across different requests. Each transaction must be validated independently.
How Should You Handle Webhook Failures and Idempotency?
Networks in Nepal can be unpredictable. Gateways may retry webhooks, or your server might acknowledge receipt but crash before writing to disk. Designing for failure is non-negotiable when you accept online payments in Nepal: eSewa and Khalti integration must survive real-world conditions.
- Idempotent Processing: Store each transaction ID in a dedicated
payment_eventstable with a unique constraint. Before processing any webhook, check if that ID already exists. If yes, return 200 immediately without re-updating the order. - Atomic Updates: Wrap verification and order status changes in a single database transaction. Partial states cause orphaned payments and customer support nightmares.
- Retry Logic: Return HTTP 200 only after successful persistence. Return 5xx for transient failures so the gateway retries. Configure exponential backoff awareness—most Nepali gateways retry at 1min, 5min, 30min intervals.
- Audit Logging: Log every raw webhook payload before processing. When disputes arise (and they will), having immutable request logs saves hours of reconciliation. Teams using Prometheus and Grafana should expose webhook latency and failure rates as metrics.
A common mistake is returning 200 prematurely because the framework auto-responds before your async handler finishes. In Laravel, use synchronous jobs or database transactions within the controller. In Node.js, await the DB write before sending the response. Async fire-and-forget patterns lose payments.
When Is Dual-Gateway Integration Worth the Complexity?
Supporting both eSewa and Khalti increases maintenance burden but significantly improves conversion. My rule of thumb: if monthly transaction volume exceeds NPR 500,000 or your user base spans multiple demographics, dual integration pays for itself within weeks. For early-stage MVPs serving Kathmandu-centric tech users, Khalti alone may suffice initially. For mass-market goods, government services, or remittance-adjacent products, eSewa is non-negotiable.
Abstract the gateway behind a unified interface. Create a PaymentProvider contract with initiate(), verify(), and refund() methods. Concrete implementations handle provider-specific quirks. This lets you add IME Pay or Fonepay later without rewriting business logic. Teams already practicing clean architecture through REST API design with Sanctum will find this pattern natural.
Compliance and Security Reminders
Nepal Rastra Bank regulations require merchants to retain transaction records for five years. Ensure your database retention policies align. Never log full card numbers or wallet PINs. Mask sensitive fields in logs. If you handle recurring billing, obtain explicit user consent per NRB directives. Security isn’t optional—it’s the foundation of trust in Nepal’s growing digital economy.
Next Steps for Production Deployment
You now have the architectural blueprint to accept online payments in Nepal: eSewa and Khalti integration done correctly. Start with sandbox testing, implement idempotent webhook handlers, and verify every transaction server-side before marking orders complete. Monitor failure rates closely during your first month—gateway behavior in production often differs from documentation.
If your team needs hands-on implementation support, security review, or help designing a scalable payment microservice, reach out directly. I’ve helped Nepali startups and enterprises build audit-ready payment systems that handle peak loads without losing a single rupee. Let’s make sure yours does too.