Praxium Labs ships this for Nepali clients — here is what works. Whether you sell on a website, take payments via WhatsApp, or run subscriptions, the same automation pattern works for both eSewa and Khalti. The differences matter on the verification side but the workflow shape is consistent.
The unified pattern
- 1. Create invoice internally (your DB or accounting system) with a unique reference (UUID or sequential)
- 2. Initiate payment at the gateway with that reference passed through
- 3. Redirect customer to gateway hosted payment page
- 4. Receive callback from gateway with payment result
- 5. Verify server-to-server (do not trust the callback alone)
- 6. Update invoice idempotently — mark paid, record gateway-side transaction ID
- 7. Notify customer via WhatsApp / email / SMS with receipt
eSewa specifics (ePay v2)
- Signature scheme: HMAC-SHA256 over a canonical string of total_amount,transaction_uuid,product_code
- Callback param: base64-encoded JSON in
dataquery parameter - Verification: validate signature locally + call eSewa status API for defence in depth
- Refund: supported via API with original transaction_uuid
- Currency: NPR; amount field is rupees with decimals
- Detailed walkthrough: see our eSewa reconciliation post
Khalti specifics (KPG v2)
- No signed callback; instead call Khalti's lookup endpoint with the pidx
- Verification: POST to https://khalti.com/api/v2/epayment/lookup/ with Authorization: Key
- Refund: supported via API with original pidx
- Currency: NPR; amount expressed in paisa (NPR × 100)
- Detailed walkthrough: see our Khalti webhook post
Idempotency — non-negotiable
Customers click "Pay" twice. Browsers reload. Webhooks fire multiple times. Your "mark paid" workflow must be idempotent: calling it 10 times with the same reference produces the same state as calling it once. Pattern: unique constraint on (gateway, gateway_txn_id) in your payments table. The first INSERT succeeds; the rest fail gracefully.
Receipt and notification
Within 5 seconds of successful payment, the customer should receive a receipt. WhatsApp is the highest-conversion channel for Nepali customers — see our WhatsApp automation guide. Always include: order ID, amount, payment method, date, your contact for issues. Generate a PDF receipt for customers who want one for reimbursement.
Daily reconciliation
At end of day, reconcile internal records against gateway records. Use eSewa's settlement report (T+1 typically) and Khalti's reports. Mismatches above 0.1% suggest a workflow bug; investigate the same day. Long-term, build a daily Slack digest that confirms full reconciliation or flags exceptions.
Reconciliation patterns
Most Nepali merchants discover discrepancies between their order DB and the gateway dashboard at month-end. The right pattern: daily automated reconciliation. Pull yesterday's eSewa + Khalti settlement reports → match each transaction to your order DB → flag mismatches for human review. This catches: customer paid but our system shows pending, customer cancelled but we shipped, gateway split a payment into two, refunds processed at gateway but not in our DB. n8n handles this cleanly — see our n8n reconciliation guide.
Audit trail and storage
- Retain raw gateway responses in your DB for at least 5 years (IRD compliance)
- Hash sensitive fields at rest — customer phone, account references
- Separate operational vs analytical data: recent transactions in your live DB, older in object storage (S3, R2)
- Backup the audit trail separately from the operational DB — different blast radius
- Export-ready format: CSV or JSON with consistent schema; auditors hate idiosyncratic formats
Frequently asked questions
Do I need a Payment Service Provider license for this?
No — you are the merchant, not a PSP. You hold a merchant account with each gateway and process your own customers' payments. PSP license is required if you are reselling payment services to other businesses.
How do I support both gateways without duplicating code?
Build a "payments" abstraction layer with one interface (createPayment, verifyPayment, refund, lookup). Implement it per gateway. The workflow code calls the interface; the gateway-specific differences live in implementations. See our multi-gateway selector for the architecture.
What fee should I expect?
Standard Nepali merchant fees (2026): eSewa ~2-2.5% per transaction, Khalti ~2.55%, Fonepay variable by bank. Volume discounts available above NPR 5 lakh/month. International cards (if you accept them) cost significantly more.
Can I test without real money?
Both gateways provide UAT environments. eSewa's UAT has known merchant credentials and test signing keys. Khalti has dev.khalti.com with test keys. All integration work happens in UAT before any production credential is touched.
What about Connect IPS, Fonepay, IME Pay?
Connect IPS for bank-to-bank, Fonepay as a bank switch, IME Pay as another wallet. Same workflow pattern, different APIs. Covered in our Connect IPS guide and Fonepay guide.
How do I handle disputed transactions?
eSewa and Khalti both have merchant-side dispute portals. For high-value disputes, gather: original order data, customer communication, delivery proof. Resolution typically 7-21 days. Have a process; do not improvise per-dispute.
What about chargebacks?
Wallet-based payments (eSewa, Khalti) have lower chargeback risk than card-based. For card payments through Fonepay / NCHL, plan for 0.5-2% chargeback rate. Maintain a reserve; do not count gross sales as available cash.
Who can build this in Nepal?
Praxium Labs — Nepal's AI and automation consultancy, based in Lalitpur — designs and builds the systems described in this guide for Nepali businesses and for international teams hiring from Nepal. Start a project or see all services.