Skip to content

qpay-sdk/qpay-rust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

qpay

CI crates.io docs.rs License: MIT

QPay V2 API SDK for Rust. Async client with automatic token management, invoice creation, payment operations, and ebarimt (electronic tax receipt) support.

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

Quick Start

use qpay::{QPayClient, QPayConfig, models::CreateSimpleInvoiceRequest};

#[tokio::main]
async fn main() -> Result<(), qpay::QPayError> {
    // Configure from environment variables
    let config = QPayConfig::from_env()?;
    let client = QPayClient::new(config);

    // Create an invoice
    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);

    Ok(())
}

Configuration

From environment variables

let config = QPayConfig::from_env()?;

Required 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

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);

Usage

Authentication

Token management is fully automatic. The client obtains and refreshes tokens as needed before each request. You can also manage tokens manually:

// Get a new token
let token = client.get_token().await?;
println!("Access token: {}", token.access_token);

// Refresh the current token
let new_token = client.refresh_token().await?;

Create an invoice (simple)

use qpay::models::CreateSimpleInvoiceRequest;

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: "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);
println!("QR text: {}", invoice.qr_text);

// Show deeplinks for bank apps
for url in &invoice.urls {
    println!("{}: {}", url.name, url.link);
}

Create an invoice (full options)

use qpay::models::*;

let req = CreateInvoiceRequest {
    invoice_code: "YOUR_INVOICE_CODE".to_string(),
    sender_invoice_no: "INV-002".to_string(),
    sender_branch_code: Some("BRANCH_01".to_string()),
    sender_branch_data: Some(SenderBranchData {
        name: Some("Main Branch".to_string()),
        email: Some("branch@example.com".to_string()),
        ..Default::default()
    }),
    sender_staff_data: None,
    sender_staff_code: None,
    invoice_receiver_code: "terminal".to_string(),
    invoice_receiver_data: Some(InvoiceReceiverData {
        register: Some("AA12345678".to_string()),
        name: Some("Customer Name".to_string()),
        email: Some("customer@example.com".to_string()),
        phone: Some("99001122".to_string()),
        address: None,
    }),
    invoice_description: "Detailed invoice".to_string(),
    enable_expiry: Some("true".to_string()),
    allow_partial: Some(false),
    minimum_amount: None,
    allow_exceed: Some(false),
    maximum_amount: None,
    amount: 100000.0,
    callback_url: "https://example.com/callback".to_string(),
    sender_terminal_code: None,
    sender_terminal_data: None,
    allow_subscribe: None,
    subscription_interval: None,
    subscription_webhook: None,
    note: Some("Special instructions".to_string()),
    transactions: None,
    lines: Some(vec![
        InvoiceLine {
            tax_product_code: Some("TAX001".to_string()),
            line_description: "Product A".to_string(),
            line_quantity: "2".to_string(),
            line_unit_price: "50000".to_string(),
            note: None,
            discounts: None,
            surcharges: None,
            taxes: None,
        },
    ]),
};

let invoice = client.create_invoice(&req).await?;

Create an invoice with ebarimt (tax)

use qpay::models::*;

let req = CreateEbarimtInvoiceRequest {
    invoice_code: "YOUR_INVOICE_CODE".to_string(),
    sender_invoice_no: "INV-TAX-001".to_string(),
    sender_branch_code: None,
    sender_staff_data: None,
    sender_staff_code: None,
    invoice_receiver_code: "terminal".to_string(),
    invoice_receiver_data: None,
    invoice_description: "Tax invoice".to_string(),
    tax_type: "1".to_string(),
    district_code: "23".to_string(),
    callback_url: "https://example.com/callback".to_string(),
    lines: vec![
        EbarimtInvoiceLine {
            tax_product_code: Some("TAX001".to_string()),
            line_description: "Taxable product".to_string(),
            barcode: None,
            line_quantity: "1".to_string(),
            line_unit_price: "10000".to_string(),
            note: None,
            classification_code: None,
            taxes: None,
        },
    ],
};

let invoice = client.create_ebarimt_invoice(&req).await?;

Cancel an invoice

client.cancel_invoice("invoice_id_here").await?;

Check payment status

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?;
println!("Payment count: {}", result.count);

if let Some(amount) = result.paid_amount {
    println!("Total paid: {}", amount);
}

for row in &result.rows {
    println!("Payment {} - Status: {} - Amount: {}",
        row.payment_id, row.payment_status, row.payment_amount);
}

Get payment details

let payment = client.get_payment("payment_id_here").await?;
println!("Status: {}", payment.payment_status);
println!("Amount: {} {}", payment.payment_amount, payment.payment_currency);
println!("Date: {}", payment.payment_date);
println!("Wallet: {}", payment.payment_wallet);

List payments

use qpay::models::{PaymentListRequest, Offset};

let req = PaymentListRequest {
    object_type: "INVOICE".to_string(),
    object_id: "invoice_id_here".to_string(),
    start_date: "2026-01-01".to_string(),
    end_date: "2026-01-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);
}

Cancel a payment

use qpay::models::PaymentCancelRequest;

let req = PaymentCancelRequest {
    callback_url: Some("https://example.com/cancel-callback".to_string()),
    note: Some("Cancelled by customer request".to_string()),
};

client.cancel_payment("payment_id_here", &req).await?;

Refund a payment

use qpay::models::PaymentRefundRequest;

let req = PaymentRefundRequest {
    callback_url: Some("https://example.com/refund-callback".to_string()),
    note: Some("Refund for order #001".to_string()),
};

client.refund_payment("payment_id_here", &req).await?;

Create ebarimt (electronic tax receipt)

use qpay::models::CreateEbarimtRequest;

let req = CreateEbarimtRequest {
    payment_id: "payment_id_here".to_string(),
    ebarimt_receiver_type: "83".to_string(),  // "83" = individual, "80" = organization
    ebarimt_receiver: None,  // Set to register number for organizations
    district_code: Some("23".to_string()),
    classification_code: None,
};

let ebarimt = client.create_ebarimt(&req).await?;
println!("Ebarimt ID: {}", ebarimt.id);
println!("Lottery: {}", ebarimt.ebarimt_lottery);
println!("QR: {}", ebarimt.ebarimt_qr_data);
println!("Status: {}", ebarimt.barimt_status);

Cancel ebarimt

let ebarimt = client.cancel_ebarimt("payment_id_here").await?;
println!("Cancelled: {}", ebarimt.barimt_status);

Error Handling

All methods return Result<T, QPayError>. Error variants:

Variant Description
QPayError::Api QPay API returned an error response (status code, error code, message)
QPayError::Http Network/HTTP error from reqwest
QPayError::Json JSON serialization/deserialization error
QPayError::Config Configuration error (missing environment variable, etc.)
QPayError::Token Token acquisition failed

Checking for API errors

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 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"),
                _ => println!("Other API error: {}", code),
            }
        } else {
            println!("Non-API error: {}", err);
        }
    }
}

Error code constants

The qpay::error module exports all QPay error code constants for pattern matching:

use qpay::error::*;

// Invoice errors
ERR_INVOICE_NOT_FOUND       // "INVOICE_NOTFOUND"
ERR_INVOICE_PAID            // "INVOICE_PAID"
ERR_INVOICE_ALREADY_CANCELED // "INVOICE_ALREADY_CANCELED"
ERR_INVOICE_CODE_INVALID    // "INVOICE_CODE_INVALID"
ERR_INVOICE_LINE_REQUIRED   // "INVOICE_LINE_REQUIRED"

// Payment errors
ERR_PAYMENT_NOT_FOUND        // "PAYMENT_NOTFOUND"
ERR_PAYMENT_ALREADY_CANCELED // "PAYMENT_ALREADY_CANCELED"
ERR_PAYMENT_NOT_PAID         // "PAYMENT_NOT_PAID"

// Auth errors
ERR_AUTHENTICATION_FAILED   // "AUTHENTICATION_FAILED"
ERR_PERMISSION_DENIED       // "PERMISSION_DENIED"
ERR_NO_CREDENTIALS          // "NO_CREDENDIALS"

// Merchant errors
ERR_MERCHANT_NOT_FOUND      // "MERCHANT_NOTFOUND"
ERR_MERCHANT_INACTIVE       // "MERCHANT_INACTIVE"

// Ebarimt errors
ERR_EBARIMT_NOT_REGISTERED       // "EBARIMT_NOT_REGISTERED"
ERR_EBARIMT_CANCEL_NOT_SUPPORTED // "EBARIMT_CANCEL_NOTSUPPERDED"
ERR_EBARIMT_QR_CODE_INVALID      // "EBARIMT_QR_CODE_INVALID"

// Amount errors
ERR_INVALID_AMOUNT  // "INVALID_AMOUNT"
ERR_MIN_AMOUNT_ERR  // "MIN_AMOUNT_ERR"
ERR_MAX_AMOUNT_ERR  // "MAX_AMOUNT_ERR"

API Reference

QPayConfig

Method Description
QPayConfig::new(base_url, username, password, invoice_code, callback_url) Create config with explicit values
QPayConfig::from_env() Load config from environment variables

QPayClient

Method Description
QPayClient::new(config) Create client with default HTTP settings
QPayClient::with_http_client(config, http) Create client with custom reqwest::Client

Auth

Method Description
client.get_token() Authenticate and get token pair
client.refresh_token() Refresh the current access token

Invoice

Method Description
client.create_invoice(&req) Create invoice with full options
client.create_simple_invoice(&req) Create invoice with minimal fields
client.create_ebarimt_invoice(&req) Create invoice with tax information
client.cancel_invoice(id) Cancel an invoice

Payment

Method Description
client.get_payment(id) Get payment details
client.check_payment(&req) Check payment status for an invoice
client.list_payments(&req) List payments with filters
client.cancel_payment(id, &req) Cancel a payment (card only)
client.refund_payment(id, &req) Refund a payment (card only)

Ebarimt

Method Description
client.create_ebarimt(&req) Create electronic tax receipt
client.cancel_ebarimt(payment_id) Cancel electronic tax receipt

License

MIT

Packages

 
 
 

Contributors

Languages