When building BookMyTest — a platform for booking standardised test vouchers like IELTS, GRE, GMAT, and TOEFL — integrating a payment gateway was one of the highest-stakes engineering decisions I made. eSewa is the dominant digital wallet in Nepal, so it was the obvious choice, but the documentation leaves a lot of gaps that can trip up developers. Here’s what I learned.
How eSewa's Payment Flow Works
eSewa uses a redirect-based payment flow. Your server generates a signed payment request, the user is redirected to eSewa's hosted checkout, and eSewa calls back your success/failure URLs after the transaction. The critical piece is the HMAC-SHA256 signature — it proves to eSewa that the request genuinely came from your server, and it proves to you that the callback genuinely came from eSewa.
Generating the HMAC Signature
The signature is generated by concatenating specific transaction fields into a message string, then signing it with your secret key using HMAC-SHA256 and Base64-encoding the result. Getting the field order wrong silently produces an invalid signature — eSewa will reject the request without a clear error.
import hmac
import hashlib
import base64
def generate_esewa_signature(secret_key: str, message: str) -> str:
"""
Generate an HMAC-SHA256 signature for an eSewa transaction.
message = "total_amount,transaction_uuid,product_code"
"""
key = secret_key.encode("utf-8")
msg = message.encode("utf-8")
digest = hmac.new(key, msg, hashlib.sha256).digest()
return base64.b64encode(digest).decode("utf-8")Verifying the Webhook Callback
After a successful payment, eSewa sends a callback to your server containing a Base64-encoded JSON payload. You must decode it, verify its signature against your secret key, and only then update the order status in your database. Never trust the callback blindly — always verify the signature server-side before marking a payment as complete.
Key Lessons Learned
- Test with eSewa's sandbox environment thoroughly — the production behaviour differs slightly from docs.
- Always verify signatures on your server before updating order status. Never rely on client-supplied success flags.
- Log every incoming webhook payload before processing it — debugging payment failures is much easier with a full audit trail.
- Handle idempotency: eSewa may retry callbacks. Use the transaction UUID to detect and skip duplicate processing.
- Set up a dead-letter queue or admin alert for failed webhook processing so no payment silently falls through.