Skip to Content
SDKsRust

Rust SDK

QPay V2 API SDK for Rust. Fully async with tokio, automatic token management via tokio::sync::Mutex, reqwest HTTP, and serde serialization. All methods return Result<T, QPayError>.

crates.io docs.rs GitHub

Installation

Add to your Cargo.toml:

[dependencies] qpay = "1.0.0" tokio = { version = "1", features = ["full"] }

Or using cargo add:

cargo add qpay cargo add tokio --features full

Configuration

From Environment Variables

let config = QPayConfig::from_env()?;
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

Manual Configuration

let config = QPayConfig::new( "https://merchant.qpay.mn", "your_username", "your_password", "YOUR_INVOICE_CODE", "https://example.com/callback", );

Custom HTTP Client

use std::time::Duration; let http = reqwest::Client::builder() .timeout(Duration::from_secs(60)) .build() .unwrap(); let client = QPayClient::with_http_client(config, http);

Quick Start

use qpay::{QPayClient, QPayConfig, models::CreateSimpleInvoiceRequest}; #[tokio::main] async fn main() -> Result<(), qpay::QPayError> { let config = QPayConfig::from_env()?; let client = QPayClient::new(config); let req = CreateSimpleInvoiceRequest { invoice_code: "YOUR_INVOICE_CODE".to_string(), sender_invoice_no: "INV-001".to_string(), invoice_receiver_code: "terminal".to_string(), invoice_description: "Payment for order #001".to_string(), sender_branch_code: None, amount: 10000.0, callback_url: "https://example.com/callback".to_string(), }; let invoice = client.create_simple_invoice(&req).await?; println!("Invoice ID: {}", invoice.invoice_id); println!("QR Image: {}", invoice.qr_image); println!("Short URL: {}", invoice.qpay_short_url); for url in &invoice.urls { println!(" {}: {}", url.name, url.link); } Ok(()) }

Create Invoice

use qpay::models::CreateSimpleInvoiceRequest; let req = CreateSimpleInvoiceRequest { invoice_code: "YOUR_INVOICE_CODE".to_string(), sender_invoice_no: "INV-002".to_string(), invoice_receiver_code: "terminal".to_string(), invoice_description: "Order payment".to_string(), sender_branch_code: None, amount: 50000.0, callback_url: "https://example.com/callback".to_string(), }; let invoice = client.create_simple_invoice(&req).await?; println!("Invoice ID: {}", invoice.invoice_id);

Cancel an invoice:

client.cancel_invoice("invoice-id-here").await?;

Check Payment

use qpay::models::{PaymentCheckRequest, Offset}; let req = PaymentCheckRequest { object_type: "INVOICE".to_string(), object_id: "invoice_id_here".to_string(), offset: Some(Offset { page_number: 1, page_limit: 10, }), }; let result = client.check_payment(&req).await?; if result.count > 0 { println!("Payment received!"); if let Some(amount) = result.paid_amount { println!("Total paid: {}", amount); } for row in &result.rows { println!(" {} | {} | {} {} | {}", row.payment_id, row.payment_status, row.payment_amount, row.payment_currency, row.payment_wallet); } } else { println!("No payment yet"); }

List Payments

use qpay::models::{PaymentListRequest, Offset}; let req = PaymentListRequest { object_type: "INVOICE".to_string(), object_id: "invoice_id_here".to_string(), start_date: "2024-01-01".to_string(), end_date: "2024-12-31".to_string(), offset: Offset { page_number: 1, page_limit: 20, }, }; let result = client.list_payments(&req).await?; println!("Total: {}", result.count); for item in &result.rows { println!(" {}: {} {} ({})", item.payment_id, item.payment_amount, item.payment_currency, item.payment_status); }

Get details for a single payment:

let detail = client.get_payment("payment-id-here").await?; println!("Payment {}: {} ({} {})", detail.payment_id, detail.payment_status, detail.payment_amount, detail.payment_currency);

Webhook Handling

QPay sends a POST request to your callback_url when a payment completes. Here is how to handle it with axum:

use axum::{extract::Json, routing::post, Router}; use qpay::{QPayClient, QPayConfig, models::PaymentCheckRequest}; use serde::Deserialize; use std::sync::Arc; #[derive(Deserialize)] struct CallbackBody { payment_id: String, } async fn callback_handler( client: Arc<QPayClient>, Json(body): Json<CallbackBody>, ) -> &'static str { let req = PaymentCheckRequest { object_type: "INVOICE".to_string(), object_id: body.payment_id.clone(), offset: None, }; match client.check_payment(&req).await { Ok(result) if result.count > 0 => { println!("Payment confirmed: {}", body.payment_id); } Ok(_) => { println!("Payment not found for: {}", body.payment_id); } Err(e) => { eprintln!("Error checking payment: {}", e); } } "ok" } #[tokio::main] async fn main() { let config = QPayConfig::from_env().unwrap(); let client = Arc::new(QPayClient::new(config)); let app = Router::new() .route("/api/qpay/callback", post({ let client = client.clone(); move |body| callback_handler(client, body) })); // Start server... }

Error Handling

All methods return Result<T, QPayError>:

use qpay::{is_qpay_error, error}; match client.create_simple_invoice(&req).await { Ok(invoice) => println!("Success: {}", invoice.invoice_id), Err(err) => { if let Some((status_code, code, message)) = is_qpay_error(&err) { println!("QPay API error {}: {} - {}", status_code, code, message); match code { error::ERR_INVOICE_NOT_FOUND => println!("Invoice not found"), error::ERR_INVALID_AMOUNT => println!("Invalid amount"), error::ERR_AUTHENTICATION_FAILED => println!("Auth failed"), error::ERR_PERMISSION_DENIED => println!("Permission denied"), error::ERR_INVOICE_PAID => println!("Invoice already paid"), _ => println!("Other API error: {}", code), } } else { println!("Non-API error: {}", err); } } }

QPayError Variants

VariantDescription
QPayError::Api { status_code, code, message, raw_body }QPay API returned an error response
QPayError::Http(reqwest::Error)Network/HTTP error from reqwest
QPayError::Json(serde_json::Error)JSON serialization/deserialization error
QPayError::Config(String)Configuration error (missing variable, etc.)
QPayError::Token(String)Token acquisition failed

API Reference

MethodDescriptionReturns
QPayClient::new(config)Create client with default HTTP settingsQPayClient
QPayClient::with_http_client(config, http)Create client with custom reqwest::ClientQPayClient
get_token()Authenticate and get token pairResult<TokenResponse>
refresh_token()Refresh the current access tokenResult<TokenResponse>
create_invoice(&req)Create invoice with full optionsResult<InvoiceResponse>
create_simple_invoice(&req)Create invoice with minimal fieldsResult<InvoiceResponse>
create_ebarimt_invoice(&req)Create invoice with tax infoResult<InvoiceResponse>
cancel_invoice(id)Cancel an invoiceResult<()>
get_payment(id)Get payment detailsResult<PaymentDetail>
check_payment(&req)Check payment statusResult<PaymentCheckResponse>
list_payments(&req)List payments with filtersResult<PaymentListResponse>
cancel_payment(id, &req)Cancel a payment (card only)Result<()>
refund_payment(id, &req)Refund a payment (card only)Result<()>
create_ebarimt(&req)Create electronic tax receiptResult<EbarimtResponse>
cancel_ebarimt(payment_id)Cancel electronic tax receiptResult<EbarimtResponse>

All methods are async and return Result<T, QPayError>.

Last updated on