.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.
Installation
.NET CLI
dotnet add package QPayNuGet Package Manager
Install-Package QPayPackageReference
<PackageReference Include="QPay" Version="1.0.0" />Configuration
Environment Variables
| Variable | Description |
|---|---|
QPAY_BASE_URL | QPay API base URL (e.g., https://merchant.qpay.mn) |
QPAY_USERNAME | QPay merchant username |
QPAY_PASSWORD | QPay merchant password |
QPAY_INVOICE_CODE | Default invoice code |
QPAY_CALLBACK_URL | Payment 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.
| Method | Description | Returns |
|---|---|---|
GetTokenAsync() | Authenticate and get token pair | Task<TokenResponse> |
RefreshTokenAsync() | Refresh access token | Task<TokenResponse> |
CreateInvoiceAsync(request) | Create detailed invoice | Task<InvoiceResponse> |
CreateSimpleInvoiceAsync(request) | Create simple invoice | Task<InvoiceResponse> |
CreateEbarimtInvoiceAsync(request) | Create invoice with ebarimt | Task<InvoiceResponse> |
CancelInvoiceAsync(invoiceId) | Cancel invoice | Task |
GetPaymentAsync(paymentId) | Get payment details | Task<PaymentDetail> |
CheckPaymentAsync(request) | Check payment status | Task<PaymentCheckResponse> |
ListPaymentsAsync(request) | List payments | Task<PaymentListResponse> |
CancelPaymentAsync(paymentId, request?) | Cancel payment (card) | Task |
RefundPaymentAsync(paymentId, request?) | Refund payment (card) | Task |
CreateEbarimtAsync(request) | Create ebarimt | Task<EbarimtResponse> |
CancelEbarimtAsync(paymentId) | Cancel ebarimt | Task<EbarimtResponse> |
Dispose() | Release resources | void |
Links
- NuGet: nuget.org/packages/QPayÂ
- GitHub: github.com/qpay-sdk/qpay-dotnetÂ
Last updated on