Skip to Content
Getting Started

Getting Started

This guide walks you through integrating QPay payments into your application — from installing an SDK to receiving your first payment.


Prerequisites

Before you begin, make sure you have:

  1. A QPay Merchant Account — Contact QPay  to register as a merchant and obtain your credentials.
  2. API Credentials — You will receive a username, password, and invoice code from QPay.
  3. A Callback URL — A publicly accessible HTTPS endpoint where QPay will send payment notifications when a customer completes a payment.

[!TIP] For development and testing, use the sandbox environment at https://merchant-sandbox.qpay.mn. Switch to https://merchant.qpay.mn when you are ready to go live.


Step 1: Choose Your SDK

Pick the SDK that matches your tech stack. All SDKs share the same design patterns, so the concepts you learn with one apply to all.

LanguagePackageInstall
Goqpay-gogo get github.com/qpay-sdk/qpay-go
JavaScript / TypeScriptqpay-jsnpm install qpay-js
Pythonqpay-pypip install qpay-py
PHPqpay-phpcomposer require usukhbayar/qpay-php
Rubyqpay-sdkgem install qpay-sdk
Dart / Flutterqpaydart pub add qpay
Rustqpaycargo add qpay
Javaqpay-javaMaven / Gradle
.NET (C#)QPaydotnet add package QPay
Swiftqpay-swiftSwift Package Manager
cURLqpay-curlgit clone

If you are using a web framework, also check the Framework Packages (Laravel, Django, Spring Boot, Express, NestJS, Rails, ASP.NET Core, Flutter, FastAPI) for deeper integration with built-in webhook routing, DI, and configuration.


Step 2: Installation

Install the SDK for your language:

Go

go get github.com/qpay-sdk/qpay-go

JavaScript / TypeScript

npm install qpay-js

Python

pip install qpay-py

PHP

composer require usukhbayar/qpay-php

Java (Maven)

<dependency> <groupId>io.github.qpay-sdk</groupId> <artifactId>qpay-java</artifactId> <version>1.0.0</version> </dependency>

Java (Gradle)

implementation 'io.github.qpay-sdk:qpay-java:1.0.0'

Ruby

gem install qpay-sdk

.NET

dotnet add package QPay

Dart / Flutter

dart pub add qpay

Swift (Package.swift)

.package(url: "https://github.com/qpay-sdk/qpay-swift.git", from: "1.0.0")

Rust

cargo add qpay

Step 3: Configuration

All QPay SDKs support the same set of environment variables. This is the recommended way to configure your credentials — it keeps secrets out of your source code.

Environment Variables

VariableRequiredDescriptionExample
QPAY_BASE_URLYesQPay API base URLhttps://merchant.qpay.mn
QPAY_USERNAMEYesYour merchant usernameMY_MERCHANT
QPAY_PASSWORDYesYour merchant passwords3cret_p@ss
QPAY_INVOICE_CODEYesYour default invoice codeMY_INVOICE_CODE
QPAY_CALLBACK_URLYesPayment notification URLhttps://example.com/api/qpay/callback

Create a .env file in your project root:

.env
QPAY_BASE_URL=https://merchant-sandbox.qpay.mn QPAY_USERNAME=your_username QPAY_PASSWORD=your_password QPAY_INVOICE_CODE=YOUR_INVOICE_CODE QPAY_CALLBACK_URL=https://yoursite.com/api/qpay/callback

[!WARNING] Never commit your .env file to version control. Add it to your .gitignore.

Loading Configuration

Every SDK provides a function to load config from environment variables:

cfg, err := qpay.LoadConfigFromEnv() client := qpay.NewClient(cfg)
import { QPayClient, loadConfigFromEnv } from 'qpay-js'; const client = new QPayClient(loadConfigFromEnv());
from qpay import QPayClient, QPayConfig config = QPayConfig.from_env() client = QPayClient(config)
use QPay\Config; use QPay\QPayClient; $config = Config::fromEnv(); $client = new QPayClient($config);

Manual Configuration

You can also configure the client directly in code:

// JavaScript / TypeScript const client = new QPayClient({ baseUrl: 'https://merchant.qpay.mn', username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD', invoiceCode: 'YOUR_INVOICE_CODE', callbackUrl: 'https://yoursite.com/api/qpay/callback', });

Step 4: Create Your First Invoice

An invoice is how you request payment from a customer. When you create an invoice, QPay returns a QR code image (base64-encoded) and deep links for mobile banking apps.

Go

package main import ( "context" "fmt" "log" qpay "github.com/qpay-sdk/qpay-go" ) func main() { cfg, err := qpay.LoadConfigFromEnv() if err != nil { log.Fatal(err) } client := qpay.NewClient(cfg) invoice, err := client.CreateSimpleInvoice(context.Background(), &qpay.CreateSimpleInvoiceRequest{ InvoiceCode: cfg.InvoiceCode, SenderInvoiceNo: "ORDER-001", InvoiceReceiverCode: "terminal", InvoiceDescription: "Payment for Order #001", Amount: 50000, CallbackURL: cfg.CallbackURL, }) if err != nil { log.Fatal(err) } fmt.Printf("Invoice ID: %s\n", invoice.InvoiceID) fmt.Printf("QR Image: %s\n", invoice.QRImage) // base64 PNG fmt.Printf("Short URL: %s\n", invoice.QPay_ShortURL) }

JavaScript / TypeScript

import { QPayClient, loadConfigFromEnv } from 'qpay-js'; const config = loadConfigFromEnv(); const client = new QPayClient(config); const invoice = await client.createSimpleInvoice({ invoiceCode: config.invoiceCode, senderInvoiceNo: 'ORDER-001', invoiceReceiverCode: 'terminal', invoiceDescription: 'Payment for Order #001', amount: 50000, callbackUrl: config.callbackUrl, }); console.log('Invoice ID:', invoice.invoiceId); console.log('QR Image:', invoice.qrImage); // base64 PNG console.log('Short URL:', invoice.qPayShortUrl);

Python

from qpay import QPayClient, QPayConfig, CreateSimpleInvoiceRequest config = QPayConfig.from_env() with QPayClient(config) as client: invoice = client.create_simple_invoice( CreateSimpleInvoiceRequest( invoice_code=config.invoice_code, sender_invoice_no="ORDER-001", invoice_receiver_code="terminal", invoice_description="Payment for Order #001", amount=50000.0, callback_url=config.callback_url, ) ) print(f"Invoice ID: {invoice.invoice_id}") print(f"QR Image: {invoice.qr_image}") # base64 PNG print(f"Short URL: {invoice.qpay_short_url}")

PHP

use QPay\Config; use QPay\QPayClient; use QPay\Models\CreateSimpleInvoiceRequest; $config = Config::fromEnv(); $client = new QPayClient($config); $invoice = $client->createSimpleInvoice(new CreateSimpleInvoiceRequest( invoiceCode: $config->invoiceCode, senderInvoiceNo: 'ORDER-001', invoiceReceiverCode: 'terminal', invoiceDescription: 'Payment for Order #001', amount: 50000.0, callbackUrl: $config->callbackUrl, )); echo "Invoice ID: " . $invoice->invoiceId . "\n"; echo "QR Image: " . $invoice->qrImage . "\n"; // base64 PNG echo "Short URL: " . $invoice->qPayShortUrl . "\n";

Invoice Response

The invoice response contains everything you need to present payment options to the customer:

FieldDescription
invoiceIdUnique invoice identifier (use this for payment checks)
qrImageBase64-encoded PNG of the QR code — display this to the customer
qPayShortUrlShort URL that opens the QPay payment page
urlsArray of deep links for specific mobile banking apps

[!TIP] Display the QR code for desktop users and use the deep links (urls) to redirect mobile users directly to their banking app.


Step 5: Check Payment Status

After the customer scans the QR code or opens a banking app link, you need to verify whether the payment was completed. There are two approaches: polling and webhooks.

Polling (Check Payment)

Call the checkPayment method with the invoice ID to see if any payments have been made:

Go

result, err := client.CheckPayment(ctx, &qpay.PaymentCheckRequest{ ObjectType: "INVOICE", ObjectID: invoice.InvoiceID, Offset: &qpay.Offset{ PageNumber: 1, PageLimit: 10, }, }) if result.Count > 0 { fmt.Printf("Payment received! Paid amount: %.0f MNT\n", result.PaidAmount) } else { fmt.Println("Waiting for payment...") }

JavaScript / TypeScript

const result = await client.checkPayment({ objectType: 'INVOICE', objectId: invoice.invoiceId, offset: { pageNumber: 1, pageLimit: 10 }, }); if (result.count > 0) { console.log('Payment received! Paid amount:', result.paidAmount); } else { console.log('Waiting for payment...'); }

Python

from qpay import PaymentCheckRequest, Offset result = client.check_payment( PaymentCheckRequest( object_type="INVOICE", object_id=invoice.invoice_id, offset=Offset(page_number=1, page_limit=10), ) ) if result.count > 0: print(f"Payment received! Paid amount: {result.paid_amount} MNT") else: print("Waiting for payment...")

PHP

use QPay\Models\PaymentCheckRequest; $result = $client->checkPayment(new PaymentCheckRequest( objectType: 'INVOICE', objectId: $invoice->invoiceId, pageNumber: 1, pageLimit: 10, )); if ($result->count > 0) { echo "Payment received! Paid amount: {$result->paidAmount} MNT\n"; } else { echo "Waiting for payment...\n"; }

[!NOTE] In a real application, poll at reasonable intervals (e.g., every 3-5 seconds) and stop after a timeout. Do not poll indefinitely.


Step 6: Handle Webhooks (Callbacks)

QPay sends an HTTP POST request to your QPAY_CALLBACK_URL when a payment is completed. This is more efficient than polling and is recommended for production use.

How Callbacks Work

  1. You set callbackUrl when creating the invoice.
  2. When the customer completes the payment, QPay sends a POST request to that URL.
  3. Your server receives the notification and verifies the payment using checkPayment.
  4. You update the order status in your database.

Example: Express.js Webhook Handler

import express from 'express'; import { QPayClient, loadConfigFromEnv } from 'qpay-js'; const app = express(); app.use(express.json()); const client = new QPayClient(loadConfigFromEnv()); app.post('/api/qpay/callback', async (req, res) => { const { invoice_id } = req.body; // Always verify the payment with QPay -- never trust the callback alone const result = await client.checkPayment({ objectType: 'INVOICE', objectId: invoice_id, offset: { pageNumber: 1, pageLimit: 10 }, }); if (result.count > 0) { // Payment confirmed -- update your order status console.log(`Payment confirmed for invoice ${invoice_id}`); console.log(`Paid amount: ${result.paidAmount}`); // await updateOrderStatus(invoice_id, 'paid'); } res.status(200).json({ status: 'ok' }); });

Example: Python (Flask) Webhook Handler

from flask import Flask, request, jsonify from qpay import QPayClient, QPayConfig, PaymentCheckRequest, Offset app = Flask(__name__) config = QPayConfig.from_env() client = QPayClient(config) @app.post("/api/qpay/callback") def qpay_callback(): data = request.get_json() invoice_id = data.get("invoice_id") # Always verify the payment with QPay result = client.check_payment( PaymentCheckRequest( object_type="INVOICE", object_id=invoice_id, offset=Offset(page_number=1, page_limit=10), ) ) if result.count > 0: print(f"Payment confirmed for invoice {invoice_id}") print(f"Paid amount: {result.paid_amount}") # update_order_status(invoice_id, "paid") return jsonify({"status": "ok"}), 200

Example: Go Webhook Handler

http.HandleFunc("/api/qpay/callback", func(w http.ResponseWriter, r *http.Request) { var body struct { InvoiceID string `json:"invoice_id"` } json.NewDecoder(r.Body).Decode(&body) // Always verify the payment with QPay result, err := client.CheckPayment(r.Context(), &qpay.PaymentCheckRequest{ ObjectType: "INVOICE", ObjectID: body.InvoiceID, Offset: &qpay.Offset{PageNumber: 1, PageLimit: 10}, }) if err != nil { http.Error(w, "check failed", http.StatusInternalServerError) return } if result.Count > 0 { log.Printf("Payment confirmed for invoice %s, amount: %.0f", body.InvoiceID, result.PaidAmount) // updateOrderStatus(body.InvoiceID, "paid") } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) })

[!WARNING] Always verify the payment by calling checkPayment after receiving a callback. Never rely solely on the callback data — it could be spoofed by a third party.


Integration Flow Summary

Here is the complete payment integration flow:

1. Customer places an order on your site 2. Your server calls createSimpleInvoice() with the order amount 3. QPay returns a QR code + deep links 4. You display the QR code / redirect to banking app 5. Customer pays via their bank 6. QPay sends a callback to your server (POST) 7. Your server calls checkPayment() to verify 8. You mark the order as paid

What’s Next?

Now that you have the basics working, explore these resources:

  • SDK Documentation — Detailed guides for each language with full API reference
  • API Reference — Complete endpoint documentation for authentication, invoices, payments, and ebarimt
  • Framework Packages — Pre-built integrations for Laravel, Django, Spring Boot, Express, NestJS, Rails, ASP.NET Core, Flutter, and FastAPI
  • CMS Plugins — Drop-in payment plugins for WooCommerce, Shopify, OpenCart, Magento, PrestaShop, WordPress, and Odoo
  • Error Codes — Comprehensive error reference for debugging and troubleshooting
Last updated on