This commit is contained in:
2025-11-05 19:54:51 +01:00
parent 27e311c433
commit 0375982137
4 changed files with 317 additions and 1 deletions

4
.idea/vcs.xml generated
View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

205
010qrcodegenerator.py Normal file
View 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()

109
011 test.py Normal file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import urllib.parse
import qrcode
from pathlib import Path
from datetime import datetime
from PIL import Image, ImageTk
import customtkinter as ctk
from tkinter import messagebox
IBAN = "CZ7520100000002800046620"
CURRENCY = "CZK"
OUTPUT_DIR = Path("QRPlatby")
OUTPUT_DIR.mkdir(exist_ok=True)
PRIJMENI = "Buzalka"
JMENO = "Vladimír"
RODCIS = "730928104"
ITEMS = {
"Očkování chřipka Vaxigrip": 600.00,
"Očkování chřipka Efluelda": 1300.00,
}
def create_spayd(iban, amount, vs, msg, currency="CZK"):
msg_encoded = urllib.parse.quote(msg, safe="$%*+-.:/")
return f"SPD*1.0*ACC:{iban}*AM:{amount:.2f}*CC:{currency}*X-VS:{vs}*MSG:{msg_encoded}"
class QRPlatbaApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title("QR Platba Ordinace MUDr. Buzalková")
self.geometry("520x520")
self.minsize(480, 480)
self.resizable(True, True)
ctk.set_appearance_mode("light")
ctk.set_default_color_theme("blue")
frame = ctk.CTkFrame(self, corner_radius=10)
frame.pack(expand=True, fill="both", padx=20, pady=20)
ctk.CTkLabel(frame, text="Generátor QR Platby",
font=("Arial", 20, "bold")).pack(pady=(10, 20))
patient = ctk.CTkFrame(frame, corner_radius=8)
patient.pack(fill="x", pady=(0, 20), padx=10)
for text in [f"Příjmení: {PRIJMENI}",
f"Jméno: {JMENO}",
f"Rodné číslo: {RODCIS}"]:
ctk.CTkLabel(patient, text=text, font=("Arial", 12)).pack(anchor="w", padx=10, pady=2)
pay = ctk.CTkFrame(frame, corner_radius=8)
pay.pack(fill="x", pady=(0, 20), padx=10)
ctk.CTkLabel(pay, text="Vyberte položku k úhradě:",
font=("Arial", 12, "bold")).pack(anchor="w", padx=10, pady=(10, 5))
self.selected_item = ctk.StringVar(value=list(ITEMS.keys())[0])
self.combo = ctk.CTkOptionMenu(pay,
variable=self.selected_item,
values=list(ITEMS.keys()),
font=("Arial", 12),
command=self.update_amount)
self.combo.pack(fill="x", padx=10)
self.amount_label = ctk.CTkLabel(pay, text="", font=("Arial", 12, "italic"))
self.amount_label.pack(anchor="e", padx=10, pady=(5, 10))
self.update_amount()
ctk.CTkButton(frame, text="Vytvořit QR Platbu",
font=("Arial", 13, "bold"),
height=40,
command=self.generate_qr).pack(pady=10)
self.qr_label = ctk.CTkLabel(frame, text="")
self.qr_label.pack(pady=15)
ctk.CTkLabel(frame,
text="© Ordinace MUDr. Buzalková | QR Platba dle ČBA v1.2",
font=("Arial", 10),
text_color="#666").pack(side="bottom", pady=(10, 0))
def update_amount(self, _=None):
item = self.selected_item.get()
self.amount_label.configure(text=f"Částka: {ITEMS[item]:.2f}")
def generate_qr(self):
item = self.selected_item.get()
spayd = create_spayd(IBAN, ITEMS[item], RODCIS, f"{PRIJMENI} {JMENO} {item}", CURRENCY)
img = qrcode.make(spayd)
filename = f"{PRIJMENI}_{JMENO}_{datetime.now():%Y%m%d_%H%M%S}.png"
out_path = OUTPUT_DIR / filename
img.save(out_path)
img_resized = img.resize((300, 300))
qr_tk = ImageTk.PhotoImage(img_resized)
self.qr_label.configure(image=qr_tk)
self.qr_label.image = qr_tk
# 🔄 Adjust window height dynamically
self.update_idletasks()
self.geometry(f"{self.winfo_reqwidth()}x{self.winfo_reqheight()}")
messagebox.showinfo("QR Platba vytvořena",
f"Soubor uložen jako:\n{out_path}\n\nSPD řetězec:\n{spayd}")
if __name__ == "__main__":
app = QRPlatbaApp()
app.mainloop()