Nepal.Payments.Gateways

Easy-peasy payments gateways package for Nepal — integrate eSewa, Khalti, and Fonepay (Dynamic QR) without jhanjhat.
Features
- eSewa payment integration
- Khalti payment gateway
- Fonepay Dynamic QR support
- Unified API for all gateways
- Async/await support
- Callback/ webhook handling
- Mobile and web checkout support
- Comprehensive error handling
Installation
dotnet add package Nepal.Payments.GatewaysQuick Start
Register Services
using Nepal.Payments.Gateways;
builder.Services.AddPaymentGateways(options =>{ options.eSewa.MerchantId = "your-merchant-id"; options.eSewa.SecretKey = "your-secret-key";
options.Khalti.PublicKey = "your-public-key"; options.Khalti.SecretKey = "your-secret-key";
options.Fonepay.MerchantId = "your-merchant-id"; options.Fonepay.Password = "your-password";});eSewa Integration
Initialize Payment
public class PaymentController : ControllerBase{ private readonly IEsewaService _esewaService;
public PaymentController(IEsewaService esewaService) { _esewaService = esewaService; }
[HttpPost("initiate")] public async Task<IActionResult> InitiatePayment(PaymentRequest request) { var response = await _esewaService.InitiatePaymentAsync(new EsewaPaymentRequest { Amount = request.Amount, TaxAmount = 0, ProductServiceCharge = 0, ProductDeliveryCharge = 0, TotalAmount = request.Amount, ProductId = request.OrderId, SuccessUrl = "https://yoursite.com/payment/success", FailureUrl = "https://yoursite.com/payment/failure" });
return Ok(new { paymentUrl = response.PaymentUrl }); }}Verify Payment
[HttpPost("verify")]public async Task<IActionResult> VerifyPayment(string transactionId){ var result = await _esewaService.VerifyPaymentAsync(transactionId);
if (result.IsSuccess) { // Payment verified successfully // Update your database return Ok(new { status = "success", transaction = result.Transaction }); }
return BadRequest(new { status = "failed", message = result.Message });}eSewa supports both live and sandbox environments. Use sandbox for testing by setting UseSandbox = true in options.
Khalti Integration
Initialize Payment
public class KhaltiPaymentController : ControllerBase{ private readonly IKhaltiService _khaltiService;
public KhaltiPaymentController(IKhaltiService khaltiService) { _khaltiService = khaltiService; }
[HttpPost("initiate")] public async Task<IActionResult> InitiatePayment(PaymentRequest request) { var response = await _khaltiService.InitiatePaymentAsync(new KhaltiPaymentRequest { ReturnUrl = "https://yoursite.com/payment/callback", WebsiteUrl = "https://yoursite.com", Amount = request.Amount * 100, // Amount in paisa PurchaseOrderId = request.OrderId, PurchaseOrderName = request.ProductName, CustomerInfo = new KhaltiCustomerInfo { Name = request.CustomerName, Email = request.CustomerEmail, Phone = request.CustomerPhone } });
return Ok(new { paymentUrl = response.PaymentUrl, pidx = response.Pidx }); }}Verify Khalti Payment
[HttpPost("verify")]public async Task<IActionResult> VerifyPayment(string pidx){ var result = await _khaltiService.VerifyPaymentAsync(pidx);
if (result.IsSuccess && result.Status == "Completed") { return Ok(new { status = "success", transaction = result.TransactionId }); }
return BadRequest(new { status = "failed", message = result.Message });}Fonepay Integration
Generate Dynamic QR
public class FonepayController : ControllerBase{ private readonly IFonepayService _fonepayService;
public FonepayController(IFonepayService fonepayService) { _fonepayService = fonepayService; }
[HttpPost("generate-qr")] public async Task<IActionResult> GenerateQR(PaymentRequest request) { var response = await _fonepayService.GenerateDynamicQRAsync(new FonepayQRRequest { Amount = request.Amount, ReferenceId = request.OrderId, ProductName = request.ProductName, Remarks = $"Payment for {request.ProductName}" });
return Ok(new { qrCode = response.QRCode, qrImage = response.QRImageUrl, expiresAt = response.ExpiresAt }); }}Verify Fonepay Payment
[HttpPost("verify")]public async Task<IActionResult> VerifyPayment(string referenceId){ var result = await _fonepayService.VerifyPaymentAsync(referenceId);
if (result.IsSuccess) { return Ok(new { status = "success", transaction = result.Transaction }); }
return BadRequest(new { status = "failed" });}Fonepay Dynamic QR allows customers to scan and pay from any banking app that supports Fonepay.
Unified API
Use a single interface for all gateways:
public class UnifiedPaymentController : ControllerBase{ private readonly IPaymentGatewayFactory _factory;
public UnifiedPaymentController(IPaymentGatewayFactory factory) { _factory = factory; }
[HttpPost("pay/{gateway}")] public async Task<IActionResult> Pay(string gateway, PaymentRequest request) { var service = _factory.GetGateway(gateway);
var response = await service.InitiatePaymentAsync(new PaymentInitiationRequest { Amount = request.Amount, OrderId = request.OrderId, SuccessUrl = "https://yoursite.com/success", FailureUrl = "https://yoursite.com/failure" });
return Ok(new { paymentUrl = response.PaymentUrl }); }}Configuration Options
builder.Services.AddPaymentGateways(options =>{ // eSewa Configuration options.Esewa.MerchantId = "EPAYTEST"; // Test merchant options.Esewa.SecretKey = "8gBm/:&EnhH.1/q"; options.Esewa.UseSandbox = true; // Set to false for production
// Khalti Configuration options.Khalti.PublicKey = "test_public_key"; options.Khalti.SecretKey = "test_secret_key"; options.Khalti.UseSandbox = true;
// Fonepay Configuration options.Fonepay.MerchantId = "TEST123"; options.Fonepay.Password = "Test@123"; options.Fonepay.UseSandbox = true;});Never commit production credentials to source control. Use environment variables or Azure Key Vault.
Using Environment Variables
builder.Services.AddPaymentGateways(options =>{ options.Esewa.MerchantId = Environment.GetEnvironmentVariable("ESEWA_MERCHANT_ID")!; options.Esewa.SecretKey = Environment.GetEnvironmentVariable("ESEWA_SECRET_KEY")!; options.Esewa.UseSandbox = false;
options.Khalti.PublicKey = Environment.GetEnvironmentVariable("KHALTI_PUBLIC_KEY")!; options.Khalti.SecretKey = Environment.GetEnvironmentVariable("KHALTI_SECRET_KEY")!;
options.Fonepay.MerchantId = Environment.GetEnvironmentVariable("FONEPAY_MERCHANT_ID")!; options.Fonepay.Password = Environment.GetEnvironmentVariable("FONEPAY_PASSWORD")!;});Callback/Webhook Handling
[HttpPost("callback/{gateway}")]public async Task<IActionResult> PaymentCallback(string gateway, [FromBody] PaymentCallback callback){ var service = _factory.GetGateway(gateway); var result = await service.HandleCallbackAsync(callback);
if (result.IsVerified) { // Update order status await _orderService.MarkAsPaid(result.OrderId, result.TransactionId); return Ok(); }
return BadRequest();}Error Handling
try{ var response = await _esewaService.InitiatePaymentAsync(request); return Ok(response);}catch (PaymentGatewayException ex){ // Gateway-specific error _logger.LogError(ex, "Payment failed for order {OrderId}", request.ProductId); return BadRequest(new { error = ex.Message, code = ex.ErrorCode });}catch (Exception ex){ _logger.LogError(ex, "Unexpected error during payment"); return StatusCode(500, "An error occurred");}API Reference
IEsewaService
| Method | Description |
|---|---|
InitiatePaymentAsync | Start a new payment |
VerifyPaymentAsync | Verify payment status |
GetTransactionAsync | Get transaction details |
IKhaltiService
| Method | Description |
|---|---|
InitiatePaymentAsync | Start ePayment |
VerifyPaymentAsync | Verify by pidx |
GetTransactionAsync | Get transaction details |
IFonepayService
| Method | Description |
|---|---|
GenerateDynamicQRAsync | Generate QR code |
VerifyPaymentAsync | Check payment status |
GetTransactionAsync | Get transaction details |
Testing
Use these test credentials in sandbox:
| Gateway | Test Credential |
|---|---|
| eSewa | ID: 9806800001/2/3/4/5 |
| Khalti | Use test keys from dashboard |
| Fonepay | Contact Fonepay for test credentials |
Support
- NuGet Package
- Report issues via GitHub