Skip to main content
All posts
DjangoPythonPayments

Integrating eSewa Payment Gateway in Django

A practical walkthrough of integrating Nepal's most popular payment gateway — including HMAC-SHA256 signature verification, webhook handling, and the production pitfalls to avoid.

Munal Poudel6 min read

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.

python
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.