Python Integration Guide
omkarbhosale-upi-qr is the official Python edition of the zero-gateway UPI payment library. It brings automatic transaction splitting, Pydantic v2 schema validation with .safeParse() support, paise-level precision integer arithmetic, and complete framework parity with the JavaScript library.
What's New in Python v3.0.0
Automatically breaks amounts > ₹2,000 into ₹1,999 intervals under NPCI guidelines.
Strict runtime validation with full Zod parity: .safeParse() and .parse() methods.
Operates on integer paise (round(amount * 100)) to prevent IEEE-754 float drift.
Accepts keyword args, dictionary payloads, positional args, or direct Pydantic models.
Named imports, snake_case aliases, and callable upiqr object for JS developers.
QR codes generate completely offline in your Python process. No cloud APIs or webhooks required.
Installation
Install the latest v3.0.0 release using your package manager:
pip install omkarbhosale-upi-qr1. splitTransactionQR(*args, **kwargs)
Splits large transactions exceeding a threshold (default ₹2,000) into ₹1,999 intervals and generates a QR code for each chunk concurrently. Under NPCI guidelines, transactions $\le$ ₹2,000 often bypass merchant interchange fees on PPI wallets and achieve higher checkout completion.
Function Signature
def splitTransactionQR(*args, **kwargs) -> List[SplitQRItem]:
# Also available as snake_case alias:
def split_transaction_qr(*args, **kwargs) -> List[SplitQRItem]:Parameters Table
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| UPI_ID / upi_id | str | Yes | — | Valid UPI ID (e.g. store@upi). |
| AMOUNT / amount | float | int | Yes | — | Total amount to receive (up to ₹10,00,000). |
| splitInterval / split_interval | float | int | No | 1999 | Maximum amount per split chunk. |
| threshold | float | int | No | 2000 | Trigger threshold. If amount ≤ threshold, only 1 QR is generated. |
| name | str | No | None | Payee name (pn parameter in UPI URI). |
| note | str | No | None | Transaction memo. Each chunk appends (Part X/Y). |
| currency | str | No | "INR" | Currency code. Defaults to INR. |
Basic Split Example (₹5,000)
from omkarbhosale_upi_qr import splitTransactionQR
# Automatically splits ₹5,000 into ₹1,999 + ₹1,999 + ₹1,002
splits = splitTransactionQR({
"UPI_ID": "store@upi",
"AMOUNT": 5000,
"name": "Omkar Store",
"note": "Order #9821"
})
for item in splits:
print(f"ID: {item.id} | Amount: ₹{item.amount}")
# Supports both attribute access (.image) and dictionary indexing (['image'])
print(f"Data URL: {item.image[:35]}...\n")Expected JSON Output Structure
[
{
"id": "4a236fad-ebc5-471f-86ab-86a4f9e621e7",
"amount": 1999,
"image": "data:image/png;base64,iVBORw0KGgo..."
},
{
"id": "c19ba6ae-664a-47d7-9948-bf3558fefb6d",
"amount": 1999,
"image": "data:image/png;base64,iVBORw0KGgo..."
},
{
"id": "3c44b4ad-44e7-414f-86c4-60ae9f228ec6",
"amount": 1002,
"image": "data:image/png;base64,iVBORw0KGgo..."
}
]Custom Threshold & Interval Handling
from omkarbhosale_upi_qr import splitTransactionQR
# 1. Custom threshold & interval
splits = splitTransactionQR(
UPI_ID="store@upi",
AMOUNT=1000,
threshold=500, # Trigger splitting on amounts > ₹500
splitInterval=400, # Chunk in ₹400 intervals
name="Omkar Store",
note="Order #12345"
)
# Returns 3 QRs: ₹400 + ₹400 + ₹200
# Notes auto-appended: "(Part 1/3)", "(Part 2/3)", "(Part 3/3)"
# 2. Amounts within threshold (<= ₹2,000) return 1 item without splitting
single_split = splitTransactionQR(
UPI_ID="store@upi",
AMOUNT=1500,
)
# Returns 1 QR: [SplitQRItem(id="...", amount=1500, image="...")]2. generateQR(*args, **kwargs)
Generates a single UPI QR code as a base64 Data URL (data:image/png;base64,...).
Function Signature
def generateQR(*args, **kwargs) -> str:
# Also available as snake_case alias:
def generate_qr(*args, **kwargs) -> str:Usage Examples (Keyword, Dict, and Positional Args)
from omkarbhosale_upi_qr import generateQR
# 1. Keyword arguments with transaction metadata
qr_data_url = generateQR(
UPI_ID="store@okhdfcbank",
AMOUNT=750,
name="Omkar Store",
note="Coffee bill",
)
print(qr_data_url) # data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
# 2. Dictionary payload
qr_data_url = generateQR({
"UPI_ID": "store@okhdfcbank",
"AMOUNT": 750,
})
# 3. Positional arguments (Backwards-compatible)
qr_data_url = generateQR("store@okhdfcbank", 750)3. Pydantic Schemas & Runtime Validation
Every input is validated before generating QR images. The Python package exposes Zod-compatible schema wrappers (.safeParse() and .parse()) alongside full Pydantic v2 models:
from omkarbhosale_upi_qr import (
upiIdSchema,
qrParamsSchema,
splitQRParamsSchema,
splitQRItemSchema,
QRParams,
SplitQRParams
)
# 1. Validate UPI ID directly with Zod-compatible .safeParse()
result = upiIdSchema.safeParse("invalid-upi")
if not result.success:
print(result.error.issues[0].message)
# Output: "Invalid UPI ID format. Expected format: username@bank"
else:
print("Valid UPI ID:", result.data)
# 2. Pre-validate split parameters
result = splitQRParamsSchema.safeParse({
"UPI_ID": "store@upi",
"AMOUNT": 5000,
"splitInterval": 1999,
})
if result.success:
validated_params = result.data # SplitQRParams instance
print("Valid parameters for total amount:", validated_params.AMOUNT)
else:
print("Validation error:", result.error.message)
# 3. Direct Pydantic model usage
typed_params = SplitQRParams(
UPI_ID="merchant@okhdfcbank",
AMOUNT=7500.0,
splitInterval=1999.0,
threshold=2000.0,
name="Merchant Store",
note="Invoice #88"
)Validation Rules Reference:
- UPI ID: Regex
^[\w.-]+@[\w.-]+$, 3 to 50 characters, auto-trimmed. - Single Amount: Positive, finite number ≤ ₹1,00,000.
- Split Amount: Positive, finite number ≤ ₹10,00,000.
- Error Format: Throws
UPIValidationError(inherits fromValueError) formatted asValidation error: field: message.
4. Import Modes & Pythonic Aliases
To make integrating effortless for both Python developers and JavaScript teams porting code, multiple import styles are supported:
# 1. Named imports (Recommended)
from omkarbhosale_upi_qr import generateQR, splitTransactionQR
# 2. Pythonic snake_case imports
from omkarbhosale_upi_qr import generate_qr, split_transaction_qr
# 3. Callable default object (JS parity)
from omkarbhosale_upi_qr import upiqr
single = upiqr(UPI_ID="user@upi", AMOUNT=500)
splits = upiqr.splitTransactionQR(UPI_ID="user@upi", AMOUNT=5000)
# 4. Import Pydantic models directly
from omkarbhosale_upi_qr import QRParams, SplitQRParams, SplitQRItem5. Saving QR Code as a PNG File
To write the generated QR code directly to disk for PDF invoices, receipt printers, or email attachments, decode the base64 string using standard base64:
import base64
from omkarbhosale_upi_qr import generateQR
# 1. Generate QR Data URL
qr_data = generateQR(
UPI_ID="merchant@okhdfcbank",
AMOUNT=1200,
name="Omkar Store",
note="Invoice #1092"
)
# 2. Strip the Data URL prefix and decode base64
if "," in qr_data:
raw_base64 = qr_data.split(",")[1]
else:
raw_base64 = qr_data
# 3. Save to disk as PNG image
with open("payment_qr.png", "wb") as f:
f.write(base64.b64decode(raw_base64))
print("QR Code successfully saved to payment_qr.png")6. FastAPI Backend Integration
Production-ready async FastAPI application featuring both single QR HTML checkout and split-transaction JSON API:
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse, JSONResponse
from omkarbhosale_upi_qr import generateQR, splitTransactionQR, UPIValidationError
app = FastAPI(title="UPI Payment Gateway API")
UPI_ID = "store@okhdfcbank"
# 1. Single QR HTML Checkout page
@app.get("/checkout/single", response_class=HTMLResponse)
def get_checkout_page(order_id: str, amount: float):
try:
qr_data_url = generateQR(
UPI_ID=UPI_ID,
AMOUNT=amount,
name="Omkar Store",
note=f"Order #{order_id}"
)
return f"""
<html>
<head><title>Pay Order #{order_id}</title></head>
<body style="font-family: system-ui; text-align: center; padding: 40px;">
<h2>Scan & Pay for Order #{order_id}</h2>
<h3>Total Amount: ₹{amount:.2f}</h3>
<img src="{qr_data_url}" alt="UPI QR" style="width: 250px; height: 250px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1);" />
<p>Scan with GPay, PhonePe, Paytm, or BHIM</p>
</body>
</html>
"""
except UPIValidationError as err:
raise HTTPException(status_code=400, detail=str(err))
# 2. Split Transaction JSON API (for orders > ₹2,000)
@app.post("/api/checkout/split")
def create_split_checkout(order_id: str, amount: float):
try:
splits = splitTransactionQR(
UPI_ID=UPI_ID,
AMOUNT=amount,
name="Omkar Store",
note=f"Order #{order_id}"
)
return {
"success": True,
"order_id": order_id,
"total_amount": amount,
"parts_count": len(splits),
"parts": [item.to_dict() for item in splits]
}
except UPIValidationError as err:
raise HTTPException(status_code=400, detail=str(err))7. Flask Backend Integration
Flask endpoint handling dynamic payment QR generation with automated splitting for orders > ₹2,000:
from flask import Flask, request, jsonify
from omkarbhosale_upi_qr import generateQR, splitTransactionQR, UPIValidationError
app = Flask(__name__)
MERCHANT_UPI = "store@okhdfcbank"
@app.route("/api/qr/generate", methods=["POST"])
def generate_payment_qr():
data = request.get_json() or {}
amount = float(data.get("amount", 0))
mode = data.get("mode", "single") # 'single' or 'split'
note = data.get("note", "Online Checkout")
try:
if mode == "split" and amount > 2000:
splits = splitTransactionQR(
UPI_ID=MERCHANT_UPI,
AMOUNT=amount,
note=note
)
return jsonify({
"success": True,
"mode": "split",
"total_amount": amount,
"parts": [item.to_dict() for item in splits]
})
else:
qr_image = generateQR(
UPI_ID=MERCHANT_UPI,
AMOUNT=amount,
note=note
)
return jsonify({
"success": True,
"mode": "single",
"amount": amount,
"qr_image": qr_image
})
except UPIValidationError as err:
return jsonify({"success": False, "error": str(err)}), 400
except Exception as err:
return jsonify({"success": False, "error": "Internal server error"}), 500
if __name__ == "__main__":
app.run(port=5000, debug=True)