Python SDK
Python client library for the QPay V2 API. Provides both synchronous (QPayClient) and asynchronous (AsyncQPayClient) clients with automatic token management, typed request/response dataclasses, and comprehensive error handling. Built on httpx.
Installation
pip install qpay-pyRequires Python 3.9+.
Configuration
From Environment Variables
from qpay import QPayConfig
config = QPayConfig.from_env()| 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
from qpay import QPayConfig
config = QPayConfig(
base_url="https://merchant.qpay.mn",
username="your_username",
password="your_password",
invoice_code="YOUR_INVOICE_CODE",
callback_url="https://yoursite.com/callback",
)Custom HTTP Client
import httpx
from qpay import QPayClient, QPayConfig
http_client = httpx.Client(timeout=60.0)
client = QPayClient(config, http_client=http_client)Quick Start
Synchronous
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}")
print(f"Short URL: {invoice.qpay_short_url}")Asynchronous
import asyncio
from qpay import AsyncQPayClient, QPayConfig, CreateSimpleInvoiceRequest
config = QPayConfig.from_env()
async def main():
async with AsyncQPayClient(config) as client:
invoice = await 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}")
asyncio.run(main())Create Invoice
from qpay import CreateSimpleInvoiceRequest
invoice = client.create_simple_invoice(
CreateSimpleInvoiceRequest(
invoice_code="YOUR_CODE",
sender_invoice_no="INV-101",
invoice_receiver_code="terminal",
invoice_description="Simple invoice",
amount=10000.0,
callback_url="https://yoursite.com/callback",
)
)
print(f"Invoice ID: {invoice.invoice_id}")
print(f"QR Text: {invoice.qr_text}")
print(f"Short URL: {invoice.qpay_short_url}")
for url in invoice.urls:
print(f" {url['name']}: {url['link']}")Cancel an invoice:
client.cancel_invoice("invoice-id-here")Check Payment
from qpay import PaymentCheckRequest, Offset
result = client.check_payment(
PaymentCheckRequest(
object_type="INVOICE",
object_id="invoice-id-here",
offset=Offset(page_number=1, page_limit=10),
)
)
if result.count > 0:
print(f"Payment received! Paid: {result.paid_amount}")
for row in result.rows:
print(f" {row.payment_id}: {row.payment_status} ({row.payment_amount} {row.payment_currency})")
else:
print("No payment yet")List Payments
from qpay import PaymentListRequest, Offset
result = client.list_payments(
PaymentListRequest(
object_type="INVOICE",
object_id="invoice-id-here",
start_date="2024-01-01",
end_date="2024-12-31",
offset=Offset(page_number=1, page_limit=20),
)
)
print(f"Total: {result.count}")
for item in result.rows:
print(f" {item.payment_id} | {item.payment_amount} {item.payment_currency} | {item.payment_status}")Get details for a single payment:
detail = client.get_payment("payment-id-here")
print(f"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.
FastAPI
from fastapi import FastAPI, HTTPException, Request
from qpay import AsyncQPayClient, QPayConfig, QPayError, PaymentCheckRequest
app = FastAPI()
config = QPayConfig.from_env()
qpay = AsyncQPayClient(config)
@app.on_event("shutdown")
async def shutdown():
await qpay.close()
@app.post("/api/qpay/callback")
async def qpay_callback(request: Request):
body = await request.json()
payment_id = body.get("payment_id")
try:
result = await qpay.check_payment(
PaymentCheckRequest(
object_type="INVOICE",
object_id=payment_id,
)
)
if result.count > 0:
# Payment verified -- update your order status
print(f"Payment confirmed: {payment_id} ({result.paid_amount} MNT)")
except QPayError as e:
raise HTTPException(status_code=e.status_code, detail=e.message)
return {"status": "ok"}Django
import json
from django.http import JsonResponse, HttpResponseBadRequest
from django.views.decorators.csrf import csrf_exempt
from qpay import QPayClient, QPayConfig, PaymentCheckRequest
config = QPayConfig.from_env()
@csrf_exempt
def qpay_callback(request):
if request.method != "POST":
return HttpResponseBadRequest("Method not allowed")
body = json.loads(request.body)
payment_id = body.get("payment_id")
with QPayClient(config) as client:
result = client.check_payment(
PaymentCheckRequest(
object_type="INVOICE",
object_id=payment_id,
)
)
if result.count > 0:
# Payment verified -- update your order status
pass
return JsonResponse({"status": "ok"})Error Handling
from qpay import QPayError
try:
client.cancel_invoice("nonexistent-id")
except QPayError as e:
print(f"Status: {e.status_code}") # e.g., 404
print(f"Code: {e.code}") # e.g., "INVOICE_NOTFOUND"
print(f"Msg: {e.message}") # e.g., "Invoice not found"
print(f"Raw: {e.raw_body}") # full response bodyError Code Constants
from qpay import (
ERR_AUTHENTICATION_FAILED,
ERR_INVOICE_NOT_FOUND,
ERR_INVOICE_PAID,
ERR_INVOICE_ALREADY_CANCELED,
ERR_INVALID_AMOUNT,
ERR_PAYMENT_NOT_FOUND,
ERR_PAYMENT_ALREADY_CANCELED,
ERR_PERMISSION_DENIED,
QPayError,
)
try:
client.cancel_invoice("some-id")
except QPayError as e:
if e.code == ERR_INVOICE_NOT_FOUND:
print("Invoice does not exist")
elif e.code == ERR_INVOICE_PAID:
print("Invoice has already been paid")
elif e.code == ERR_INVOICE_ALREADY_CANCELED:
print("Already canceled")
else:
raiseAPI Reference
| Method | Returns | Description |
|---|---|---|
get_token() | TokenResponse | Authenticate and get a new token pair |
refresh_token() | TokenResponse | Refresh the current access token |
create_invoice(req) | InvoiceResponse | Create a detailed invoice |
create_simple_invoice(req) | InvoiceResponse | Create a simple invoice |
create_ebarimt_invoice(req) | InvoiceResponse | Create an invoice with ebarimt |
cancel_invoice(id) | None | Cancel an invoice |
get_payment(id) | PaymentDetail | Get payment details |
check_payment(req) | PaymentCheckResponse | Check payment status |
list_payments(req) | PaymentListResponse | List payments |
cancel_payment(id, req) | None | Cancel a payment |
refund_payment(id, req) | None | Refund a payment |
create_ebarimt(req) | EbarimtResponse | Create an ebarimt receipt |
cancel_ebarimt(id) | EbarimtResponse | Cancel an ebarimt receipt |
close() | None | Close the underlying HTTP client |
The AsyncQPayClient has the same methods, all returning coroutines (async/await).
Links
- PyPI: pypi.org/project/qpay-pyÂ
- GitHub: github.com/qpay-sdk/qpay-pyÂ
Last updated on