Skip to Content
SDKs.NET (C#)

.NET SDK (C#)

QPay V2 API client for .NET. Full async/await support, automatic token management with SemaphoreSlim, System.Text.Json serialization, and implements IDisposable for proper resource cleanup.

NuGet GitHub

Installation

.NET CLI

dotnet add package QPay

NuGet Package Manager

Install-Package QPay

PackageReference

<PackageReference Include="QPay" Version="1.0.0" />

Configuration

Environment Variables

VariableDescription
QPAY_BASE_URLQPay API base URL (e.g., https://merchant.qpay.mn)
QPAY_USERNAMEQPay merchant username
QPAY_PASSWORDQPay merchant password
QPAY_INVOICE_CODEDefault invoice code
QPAY_CALLBACK_URLPayment callback URL
var config = QPayConfig.LoadFromEnvironment();

Manual Configuration

var config = new QPayConfig { BaseUrl = "https://merchant.qpay.mn", Username = "your_username", Password = "your_password", InvoiceCode = "YOUR_INVOICE_CODE", CallbackUrl = "https://yoursite.com/api/qpay/callback", };

Custom HttpClient

var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(60) }; using var client = new QPayClient(config, httpClient);

Quick Start

using QPay; using QPay.Models; var config = QPayConfig.LoadFromEnvironment(); using var client = new QPayClient(config); var invoice = await client.CreateSimpleInvoiceAsync(new CreateSimpleInvoiceRequest { InvoiceCode = config.InvoiceCode, SenderInvoiceNo = "ORDER-001", InvoiceReceiverCode = "terminal", InvoiceDescription = "Payment for Order #001", Amount = 50000, CallbackUrl = config.CallbackUrl, }); Console.WriteLine($"Invoice ID: {invoice.InvoiceId}"); Console.WriteLine($"QR Image: {invoice.QRImage}"); Console.WriteLine($"Short URL: {invoice.QPayShortUrl}");

Create Invoice

Simple Invoice

var invoice = await client.CreateSimpleInvoiceAsync(new CreateSimpleInvoiceRequest { InvoiceCode = "YOUR_INVOICE_CODE", SenderInvoiceNo = "ORDER-002", InvoiceReceiverCode = "terminal", InvoiceDescription = "Simple invoice", Amount = 25000, CallbackUrl = "https://yoursite.com/api/qpay/callback", });

Cancel Invoice

await client.CancelInvoiceAsync("invoice-id-here");

Check Payment

var check = await client.CheckPaymentAsync(new PaymentCheckRequest { ObjectType = "INVOICE", ObjectId = "invoice-id-here", }); if (check.Count > 0) { Console.WriteLine($"Payment received! Paid: {check.PaidAmount} MNT"); foreach (var row in check.Rows) { Console.WriteLine($" {row.PaymentId}: {row.PaymentStatus} ({row.PaymentAmount} {row.PaymentCurrency})"); } } else { Console.WriteLine("No payment yet"); }

List Payments

var list = await client.ListPaymentsAsync(new PaymentListRequest { ObjectType = "INVOICE", ObjectId = "invoice-id-here", StartDate = "2024-01-01", EndDate = "2024-12-31", Offset = new Offset { PageNumber = 1, PageLimit = 20 }, }); Console.WriteLine($"Total: {list.Count}"); foreach (var item in list.Rows) { Console.WriteLine($" {item.PaymentId}: {item.PaymentAmount} {item.PaymentCurrency} - {item.PaymentStatus}"); }

Get details for a single payment:

var detail = await client.GetPaymentAsync("payment-id-here"); Console.WriteLine($"Payment {detail.PaymentId}: {detail.PaymentStatus}");

Webhook Handling

QPay sends a POST request to your CallbackUrl when a payment completes.

ASP.NET Core Minimal API

var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); var config = QPayConfig.LoadFromEnvironment(); using var qpayClient = new QPayClient(config); app.MapPost("/api/qpay/callback", async (HttpRequest request) => { var body = await request.ReadFromJsonAsync<Dictionary<string, string>>(); var paymentId = body?["payment_id"]; if (paymentId is null) return Results.BadRequest(); var result = await qpayClient.CheckPaymentAsync(new PaymentCheckRequest { ObjectType = "INVOICE", ObjectId = paymentId, }); if (result.Count > 0) { // Payment verified -- update your order status Console.WriteLine($"Payment confirmed: {paymentId} ({result.PaidAmount} MNT)"); } return Results.Ok(new { status = "ok" }); }); app.Run();

ASP.NET Core Controller

[ApiController] [Route("api/qpay")] public class QPayCallbackController : ControllerBase { private readonly QPayClient _client; public QPayCallbackController() { var config = QPayConfig.LoadFromEnvironment(); _client = new QPayClient(config); } [HttpPost("callback")] public async Task<IActionResult> Callback([FromBody] Dictionary<string, string> body) { var paymentId = body["payment_id"]; var result = await _client.CheckPaymentAsync(new PaymentCheckRequest { ObjectType = "INVOICE", ObjectId = paymentId, }); if (result.Count > 0) { // Payment verified } return Ok(new { status = "ok" }); } }

Error Handling

All API errors throw QPayException with structured error information:

try { await client.CancelInvoiceAsync("nonexistent-id"); } catch (QPayException ex) { Console.WriteLine($"Status Code: {ex.StatusCode}"); // e.g., 404 Console.WriteLine($"Error Code: {ex.Code}"); // e.g., "INVOICE_NOTFOUND" Console.WriteLine($"Message: {ex.Message}"); Console.WriteLine($"Raw Body: {ex.RawBody}"); }

Error Code Constants

using QPay; try { await client.CancelInvoiceAsync("some-id"); } catch (QPayException ex) when (ex.Code == ErrorCodes.InvoiceNotFound) { Console.WriteLine("Invoice does not exist"); } catch (QPayException ex) when (ex.Code == ErrorCodes.InvoicePaid) { Console.WriteLine("Invoice has already been paid"); } catch (QPayException ex) when (ex.Code == ErrorCodes.AuthenticationFailed) { Console.WriteLine("Invalid credentials"); } catch (QPayException ex) when (ex.Code == ErrorCodes.PermissionDenied) { Console.WriteLine("Permission denied"); }

API Reference

All methods accept an optional CancellationToken parameter.

MethodDescriptionReturns
GetTokenAsync()Authenticate and get token pairTask<TokenResponse>
RefreshTokenAsync()Refresh access tokenTask<TokenResponse>
CreateInvoiceAsync(request)Create detailed invoiceTask<InvoiceResponse>
CreateSimpleInvoiceAsync(request)Create simple invoiceTask<InvoiceResponse>
CreateEbarimtInvoiceAsync(request)Create invoice with ebarimtTask<InvoiceResponse>
CancelInvoiceAsync(invoiceId)Cancel invoiceTask
GetPaymentAsync(paymentId)Get payment detailsTask<PaymentDetail>
CheckPaymentAsync(request)Check payment statusTask<PaymentCheckResponse>
ListPaymentsAsync(request)List paymentsTask<PaymentListResponse>
CancelPaymentAsync(paymentId, request?)Cancel payment (card)Task
RefundPaymentAsync(paymentId, request?)Refund payment (card)Task
CreateEbarimtAsync(request)Create ebarimtTask<EbarimtResponse>
CancelEbarimtAsync(paymentId)Cancel ebarimtTask<EbarimtResponse>
Dispose()Release resourcesvoid
Last updated on