Between them, eSewa and Khalti handle the overwhelming majority of online consumer payments in Nepal. If you sell online here, integrating at least one of them is not optional — international gateways solve a different problem for a different customer.
Most integration guides stop at “install the plugin and paste your keys.” That part is easy. The parts that cause real damage — double charges, orders marked paid that were never paid, verification you cannot reproduce during a dispute — are what this covers.
Choose the integration path first
| Your setup | Path | Effort | Watch out for |
|---|---|---|---|
| WooCommerce | Official / vendor plugin | Low | Abandoned plugins using deprecated APIs |
| Custom app (Laravel, Node, Next.js) | Direct API | Medium | Signature and verification handling |
| Static site | Needs a backend | Medium | Keys cannot live in the browser |
| Mobile app | SDK or in-app browser | Medium | Deep-link return handling |
One rule regardless of path: your secret key never goes anywhere the browser can reach it. If a key appears in client-side JavaScript, anyone can read it and forge requests against your merchant account. It belongs on your server, in an environment variable, and nowhere else.
Before you write any code
Both providers require a merchant account, and approval takes longer than people plan for. Expect to supply business registration, PAN, and bank details. Start this early — it is routinely the thing that delays an e-commerce launch, not the development.
You will receive a merchant code or public key plus a secret key, and access to a sandbox. Build entirely against sandbox first.
Use the current eSewa API, not the old one
This matters more than anything else in this article.
eSewa’s older integration passed transaction details as plain form fields with no cryptographic signature. That means the values could be altered in the browser before submission — including the amount. Any guide, plugin or Stack Overflow answer showing that pattern is describing a deprecated integration you should not deploy.
The current approach signs the request with HMAC-SHA256 using your secret key. The signature covers the fields that matter — total amount, transaction UUID, product code — so tampering invalidates it.
Two things follow, and both get missed:
- Sign exactly the field list, in exactly the order, that the documentation specifies. A signature over the right values in the wrong order fails, and the error message rarely tells you that is why.
- Verify the response signature too. Signing your outbound request while trusting whatever comes back defeats the point.
If you inherited a site with an eSewa integration and are not sure which generation it uses, look for an HMAC signature in the request. If there is none, treat it as a security issue rather than a to-do item.
Khalti: the double-charge problem
Khalti’s flow is a callback plus a server-side lookup: the customer pays, you receive a token or pidx, and your server calls Khalti to confirm the payment is genuine and the amount matches.
The failure mode here is duplicate charges, and it comes from ordinary network behaviour rather than anything exotic. A customer on patchy mobile data submits, the response times out, they hit the button again. Two payment attempts now exist for one order.
The fix is idempotency: generate a unique order reference before the payment starts, send it with the request, and make your handler safe to run repeatedly for that reference. If a confirmation arrives for an order you have already marked paid, log it and stop — do not create a second order or fulfil twice.
Design the handler so that running it five times has the same effect as running it once. That single property removes most payment bugs before they happen.
Never trust the redirect
The most common serious mistake in Nepali e-commerce builds: marking an order paid because the customer landed back on your success URL.
That URL is just a URL. A customer can bookmark it. A curious visitor can type it. It proves nothing.
The order status must only change after a server-to-server verification call in which the provider confirms the transaction reference, the status, and — critically — the amount. Check the amount against what you expected. A verification that confirms “a payment happened” without confirming “a payment of NPR 4,500 happened for order 1182” is not verification.
The failure cases to build for
A payment integration is mostly failure handling. Plan for each of these before launch:
| Scenario | Correct behaviour |
|---|---|
| Customer abandons at the gateway | Order stays pending; cart preserved; no email |
| Payment succeeds, your server is down | Reconcile later from provider records; never lose the payment |
| Timeout, customer retries | Idempotency key prevents a second charge |
| Amount mismatch on verification | Do not fulfil; flag for manual review |
| Duplicate callback | Recognised and ignored |
| Refund needed | Documented process; status reflected in your system |
Log every request and response, with the transaction reference, and keep them. When a customer says they paid and you have no record, those logs are the only thing that resolves it.
Testing that actually means something
Sandbox success is the beginning, not the end. Before launch, deliberately break things:
- Close the browser mid-payment
- Submit the same order twice in quick succession
- Alter the amount in the request and confirm the signature rejects it
- Call your success URL directly without paying — the order must not change status
- Fire the same callback twice and confirm only one order results
Then do one small real transaction in production, on a real phone, on mobile data. Sandbox environments are more forgiving than the real network.
Should you support both?
Generally yes. Customers are loyal to whichever wallet they already have topped up, and a missing option is a lost sale rather than a preference. The additional work is mostly duplicated verification logic, which is manageable if you built the first one cleanly.
Add international card payments only if you genuinely sell abroad — the compliance and reconciliation overhead is not worth it for a domestic-only store.
Frequently asked questions
Can I add eSewa or Khalti to a WordPress site?
Yes, through WooCommerce plugins for both. Check when the plugin was last updated and whether it uses the current signed API. An unmaintained payment plugin is a liability in a way that an unmaintained gallery plugin is not.
How long does merchant approval take?
It varies, and it is a documentation process rather than a technical one. Apply as soon as you decide to sell online, not when development finishes — this is the most common cause of a delayed launch.
Do I need an SSL certificate?
Yes, and this is non-negotiable. Payment data over an unencrypted connection is unacceptable, providers will not approve it, and browsers will warn your customers.
What happens if my site is down when a payment completes?
The payment still succeeds at the provider. Your system misses the notification. This is exactly why you need a reconciliation routine that compares provider transactions against your orders — daily is enough for most stores.
Is it safe to store card or wallet details?
Do not store them. Both wallets handle authentication on their side, which is the point. Store your own transaction reference and the provider’s — nothing more.
Cash on delivery as well?
For most Nepali stores, yes. A meaningful share of customers still prefer it. Treat it as a separate order state with its own reconciliation, not as a payment method that skips verification.
Getting it built properly
Payment integration is the part of an e-commerce build where shortcuts cost real money — either in double charges you refund, or in orders you fulfil that were never paid for.
If you are planning a store, our e-commerce development work covers gateway integration, reconciliation and the failure handling above. For budgeting, what a website costs in Nepal breaks down where e-commerce pricing comes from, and starting an e-commerce business in Nepal covers the commercial decisions around it.
