Back to Blog
dotnet

Integrating eSewa, Khalti & Dynamic FonePay QR Gateways in .NET

October 16, 202510 min min read
Integrating eSewa, Khalti & Dynamic FonePay QR Gateways in .NET

My phone buzzed. It was my brother — CSIT student, second year, probably surviving on noodles and tei dai ko sasto Mo:mo.

"Dai, 3 days bhayo. Khalti integrate hunna. Project submission Monday ho."

I knew that panic. That specific brand of developer anxiety when:

The deadline is breathing down your neck The documentation makes sense… until it doesn't You've rewritten the same code 17 times You're questioning your career choices

"Video call gar," I replied.

The Screen Share of Chaos

His screen was a beautiful disaster. 23 Brave tabs open (12 of them AI). Rider with more red squiggles than actual code.

"Dai, documentation ma yo lekheko cha. Tara mildai milena."

I smiled. Not because it was funny. But because I'd been there. We've ALL been there.

Rewind: 2022. TU Convocation Season.

It was convocation form filling season. TU students everywhere were trying to pay online. Khalti team pinged me.

"Sushil, developers are struggling to integrate our API. Can you help?"

Back then, I thought: "How hard can payment integration be?"

Spoiler alert: Hard enough that people were giving up and going back to bank deposits. In 2022. In Nepal. Where the country was desperately trying to go digital.

So I built plugin.web.eSewa.and.Khalti. Developers downloaded it. Projects shipped. Crisis averted.

I moved on. Built other stuff. Forgot about it.

My Brother's Call Was a Wake-Up Call

I pulled up my old package. The code looked like it was written by someone who just discovered async/await and went wild with it. The package name was longer than some of my functions. And the architecture? Let's just say "extensible" wasn't in the vocabulary.

"What happens when FonePay becomes popular?"

"What about that new gateway launching next month?"

The answer: copy-paste chaos. Refactor hell. Developer tears.

Nope. Not on my watch.

Introducing: Nepal.Payments.Gateways

I did what any reasonable developer would do at midnight: started a complete rewrite.

Two rules:

  • If my brother can't use it, it's too complicated
  • Support both V1 and V2 APIs — because some gateways still need V1
  • Install it in 10 seconds:

    dotnet add package Nepal.Payments.Gateways

    Step 1 — Initialize (Same for ALL Gateways)

    // using Nepal.Payments.Gateways.Models.Khalti.PaymentRequest
    var paymentManager = new PaymentManager(
        paymentMethod: PaymentMethod.Khalti,  // or Esewa, or FonePay
        paymentVersion: PaymentVersion.V2,
        paymentMode: PaymentMode.Sandbox,
        secretKey: <em>khaltiSecretKey
    );

    Step 2 — Create the Request

    var request = new PaymentRequest()
    {
        Amount = 1300,
        PurchaseOrderId = "order</em>" + DateTime.Now.Ticks,
        CustomerInfo = new CustomerInfo
        {
            Name = "Your Customer",
            Email = "customer@email.com",
            Phone = "98XXXXXXXX"
        },
        ProductDetails = new[]
        {
            new ProductDetail
            {
                Name = "Your Product",
                TotalPrice = 1300,
                Quantity = 1
            }
        }
    };

    Step 3 — Get Payment URL and Redirect

    var response = await paymentManager.InitiatePaymentAsync<dynamic>(request);
    return Redirect(response.Data.PaymentUrl);

    That's it. Three steps. You can skip the official documentation — but don't quote me on that. 😄

    "But Dai, What About Verification?"

    Even simpler:

    var paymentManager = new PaymentManager(
        paymentMethod: PaymentMethod.Khalti,
        paymentVersion: PaymentVersion.V2,
        paymentMode: PaymentMode.Sandbox,
        secretKey: <em>khaltiSecretKey
    );
    var response = await paymentManager.VerifyPaymentAsync<dynamic>(pidx);
    if (response?.Data is PaymentResponse v)
    {
        Console.WriteLine($"Paid: Rs. {v.TotalAmount}");
        Console.WriteLine($"Transaction: {v.TransactionId}");
        // Party time! vanna hunthyo — raksi khadainas... ehh piudainas
    }

    eSewa? Copy-Paste-Change-One-Line

    // using Nepal.Payments.Gateways.Models.Esewa.PaymentRequest
    // Literally just change this one line:
    paymentMethod: PaymentMethod.Esewa  // Was: PaymentMethod.Khalti
    // Everything else? EXACTLY THE SAME.

    That's the magic. Same pattern. Same structure. Different gateway.

    Version Support (V1 & V2)

    // For newer implementations
    paymentVersion: PaymentVersion.V2
    // For legacy systems still on old APIs
    paymentVersion: PaymentVersion.V1

    Just specify the version — the package handles the rest. No need to rewrite your entire codebase when APIs evolve.

    "Dai, Testing Kasari Garney?"

    Bookmark this. You'll need it at 3 AM when you're testing payments.

    Live demo: payments-demo.sksushil.info.np/payment

    eSewa Test Credentials

    Username:    9806800001 (or 2 / 3 / 4 / 5)
    Password:    Nepal@123
    Token:       123456
    Merchant ID: EPAYTEST
    Secret Key:  8gBm/:&EnhH.1/q

    Khalti Test Credentials

    Mobile Number: 9800000001 (or 2 / 3 / 4 / 5)
    Pin:           1111
    OTP:           987654
    Secret Key:    live</em>secret<em>key</em>68791341fdd94846a146f0457ff7b455
    These are sandbox credentials only. Never use them in production.

    What's Cooking Next? FonePay Dynamic QR

    var qrManager = new PaymentManager(
        paymentMethod: PaymentMethod.FonepayQR,
        paymentVersion: PaymentVersion.V1,
        paymentMode: PaymentMode.Sandbox,
        secretKey: _fonepaySecretKey
    );
    var qrCode = await qrManager.GenerateQRAsync(amount, reference);
    // Boom. QR code ready.

    Same pattern. New gateway. Zero headaches.

    The REAL Test

    I sent the package to my brother.

    Me: "Try this."
    Him (30 minutes later): "Dai, kaam bhayo!"
    Me: "Demo pathai."
    Him (sent video of successful payment)
    Me: "la mojj gares."

    If he can integrate it at midnight before a Monday deadline, I'd call that a win.

    Real Talk

    Look, I'm not building this because I'm some coding wizard.

    I'm building it because my brother was panicking about a project deadline.

    Because somewhere right now, a developer in Pokhara is staring at Khalti/eSewa documentation wondering why their callback isn't working.

    Because in Butwal, someone just spent 6 hours debugging a signature mismatch.

    Because we've ALL been that person.

    If this package saves you even one debugging session — if it helps you ship your project on time — then it was worth rebuilding from scratch.

    The package is open source and free. If it helps you, a ⭐ on GitHub goes a long way.

    Share this article

    Tags

    Payment Gateway Nepal Khalti eSewa Fonepay

    Subscribe to Newsletter

    Get notified about new articles