notbook
This commit is contained in:
205
010qrcodegenerator.py
Normal file
205
010qrcodegenerator.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
============================================
|
||||
QR Platba Generator – Fully Documented Version
|
||||
============================================
|
||||
|
||||
This script generates a valid Czech payment QR code (QR Platba)
|
||||
according to the official ČBA (Czech Banking Association) specification v1.2.
|
||||
|
||||
The script creates a correct SPD (Short Payment Descriptor) string,
|
||||
turns it into a QR code (PNG image), and saves it to disk.
|
||||
|
||||
✅ Works with all Czech banks (Fio, ČSOB, KB, Air Bank…)
|
||||
✅ Supports both IBAN or domestic account format (account/bankcode)
|
||||
✅ Automatically URL-encodes the message (so spaces and diacritics work)
|
||||
✅ Can handle optional VS, SS, KS, due date, and instant-payment flag
|
||||
|
||||
Usage example (in your terminal):
|
||||
---------------------------------
|
||||
python generate_qrplatba.py \
|
||||
--acc CZ7520100000002800046620 \
|
||||
--amount 1543.50 \
|
||||
--vs 7309208104 \
|
||||
--ss 123456 \
|
||||
--ks 0308 \
|
||||
--date 09.12.2025 \
|
||||
--msg "ahoj zlatíčko" \
|
||||
--out qr_ok.png
|
||||
"""
|
||||
|
||||
import argparse # For parsing command-line arguments
|
||||
import urllib.parse # For safe URL-encoding of message text
|
||||
from pathlib import Path # For easy file handling (cross-platform)
|
||||
import qrcode # For generating the QR code image
|
||||
from datetime import datetime # For date parsing and formatting
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 🧩 Helper function: Normalize date into the required YYYYMMDD format
|
||||
# --------------------------------------------------------------------
|
||||
def normalize_date(date_str: str) -> str:
|
||||
"""
|
||||
Converts a user-supplied date string (various formats) into the
|
||||
standard YYYYMMDD format used by the QR Platba specification.
|
||||
|
||||
Acceptable input examples:
|
||||
"20251209"
|
||||
"09.12.2025"
|
||||
"2025-12-09"
|
||||
"09122025"
|
||||
|
||||
Returns:
|
||||
str: normalized date string in "YYYYMMDD" format
|
||||
or empty string "" if input is None or empty
|
||||
"""
|
||||
if not date_str:
|
||||
return ""
|
||||
s = date_str.strip()
|
||||
|
||||
# Already in correct 8-digit form?
|
||||
if len(s) == 8 and s.isdigit():
|
||||
return s
|
||||
|
||||
# Try several common formats
|
||||
for fmt in ("%d.%m.%Y", "%Y-%m-%d", "%d%m%Y"):
|
||||
try:
|
||||
dt = datetime.strptime(s, fmt)
|
||||
return dt.strftime("%Y%m%d")
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# If all parsing attempts fail → raise an informative error
|
||||
raise ValueError(
|
||||
"Date must be in one of formats: YYYYMMDD, DD.MM.YYYY, YYYY-MM-DD, DDMMYYYY"
|
||||
)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 🔧 Helper function: Build the SPD string (core of QR Platba)
|
||||
# --------------------------------------------------------------------
|
||||
def build_spayd(params: dict) -> str:
|
||||
"""
|
||||
Assembles a valid SPD (Short Payment Descriptor) string
|
||||
according to ČBA v1.2 specification.
|
||||
|
||||
Example output:
|
||||
SPD*1.0*ACC:CZ7520100000002800046620*AM:1543.50*CC:CZK*DT:20251209*
|
||||
X-VS:7309208104*X-SS:123456*X-KS:0308*MSG:ahoj%20zlaticko
|
||||
"""
|
||||
|
||||
# Start with fixed header
|
||||
parts = ["SPD*1.0*"]
|
||||
|
||||
# --- Required field: account number / IBAN ---
|
||||
acc = params.get("acc", "").strip()
|
||||
if not acc:
|
||||
raise ValueError("Missing required parameter: --acc")
|
||||
parts.append(f"ACC:{acc}*")
|
||||
|
||||
# --- Optional: amount ---
|
||||
amt = params.get("amount")
|
||||
if amt:
|
||||
parts.append(f"AM:{float(amt):.2f}*") # always two decimals
|
||||
|
||||
# --- Optional: currency (defaults to CZK) ---
|
||||
cc = params.get("cc") or "CZK"
|
||||
parts.append(f"CC:{cc}*")
|
||||
|
||||
# --- Optional: due date ---
|
||||
dt = params.get("dt")
|
||||
if dt:
|
||||
parts.append(f"DT:{dt}*")
|
||||
|
||||
# --- Optional: symbols (VS, SS, KS) ---
|
||||
for key in ("X-VS", "X-SS", "X-KS"):
|
||||
val = params.get(key.replace("-", "_").lower())
|
||||
if val:
|
||||
parts.append(f"{key}:{val}*")
|
||||
|
||||
# --- Optional: payment type (PT:IP = Instant Payment) ---
|
||||
pt = params.get("pt")
|
||||
if pt:
|
||||
parts.append(f"PT:{pt}*")
|
||||
|
||||
# --- Optional: message for recipient ---
|
||||
msg = params.get("msg") or ""
|
||||
if msg:
|
||||
# Encode to keep spaces and Czech letters valid
|
||||
safe_chars = "$%*+-.:/"
|
||||
encoded_msg = urllib.parse.quote(msg, safe=safe_chars)
|
||||
parts.append(f"MSG:{encoded_msg}")
|
||||
|
||||
# Combine everything into one string
|
||||
return "".join(parts)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 🚀 Main program entry point
|
||||
# --------------------------------------------------------------------
|
||||
def main():
|
||||
# -------------------------------
|
||||
# Define all command-line options
|
||||
# -------------------------------
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a Czech QR Platba (payment QR code)."
|
||||
)
|
||||
parser.add_argument("--acc", required=True,
|
||||
help='Account: either "2800046620/2010" or IBAN "CZ..."')
|
||||
parser.add_argument("--amount", help="Payment amount (e.g. 1543.50)")
|
||||
parser.add_argument("--vs", help="Variable symbol (X-VS)")
|
||||
parser.add_argument("--ss", help="Specific symbol (X-SS)")
|
||||
parser.add_argument("--ks", help="Constant symbol (X-KS)")
|
||||
parser.add_argument("--date", help="Due date (YYYYMMDD or DD.MM.YYYY etc.)")
|
||||
parser.add_argument("--msg", help="Message for recipient (will be URL-encoded)")
|
||||
parser.add_argument("--cc", default="CZK", help="Currency (default CZK)")
|
||||
parser.add_argument("--pt", help="Payment type (e.g. IP for instant payment)")
|
||||
parser.add_argument("--out", default="qrplatba.png", help="Output PNG file name")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# -------------------------------
|
||||
# Normalize and prepare arguments
|
||||
# -------------------------------
|
||||
dt_norm = normalize_date(args.date) if args.date else ""
|
||||
|
||||
params = {
|
||||
"acc": args.acc,
|
||||
"amount": args.amount,
|
||||
"cc": args.cc,
|
||||
"dt": dt_norm,
|
||||
"x_vs": args.vs,
|
||||
"x_ss": args.ss,
|
||||
"x_ks": args.ks,
|
||||
"msg": args.msg,
|
||||
"pt": args.pt,
|
||||
}
|
||||
|
||||
# -------------------------------
|
||||
# Build the SPD string
|
||||
# -------------------------------
|
||||
spayd = build_spayd(params)
|
||||
|
||||
# -------------------------------
|
||||
# Generate QR code image
|
||||
# -------------------------------
|
||||
img = qrcode.make(spayd)
|
||||
output_path = Path(args.out)
|
||||
img.save(output_path)
|
||||
|
||||
# -------------------------------
|
||||
# Print results for verification
|
||||
# -------------------------------
|
||||
print("✅ QR Platba successfully generated")
|
||||
print("SPD string (you can verify at https://qr-platba.cz/test/):")
|
||||
print(spayd)
|
||||
print()
|
||||
print(f"📂 QR code image saved to: {output_path.resolve()}")
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 🔘 Run when executed directly
|
||||
# --------------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user