From cc4ad998e16581fa20dc15f50c39ba0040962863 Mon Sep 17 00:00:00 2001
From: Mira Weller
Date: Tue, 2 Dec 2025 12:57:20 +0100
Subject: [PATCH] refactor payment qr codes
---
src/pretix/helpers/payment.py | 129 ++++++++++++++++++
src/pretix/plugins/banktransfer/payment.py | 58 ++------
.../pretixplugins/banktransfer/pending.html | 96 +------------
.../pretixpresale/event/payment_qr_codes.html | 44 ++++++
4 files changed, 186 insertions(+), 141 deletions(-)
create mode 100644 src/pretix/helpers/payment.py
create mode 100644 src/pretix/presale/templates/pretixpresale/event/payment_qr_codes.html
diff --git a/src/pretix/helpers/payment.py b/src/pretix/helpers/payment.py
new file mode 100644
index 0000000000..6163d4f61f
--- /dev/null
+++ b/src/pretix/helpers/payment.py
@@ -0,0 +1,129 @@
+import text_unidecode
+from django.utils.http import urlencode
+from django.utils.safestring import mark_safe
+from django.utils.translation import gettext_lazy as _
+
+
+def dotdecimal(value):
+ return str(value).replace(",", ".")
+
+
+def commadecimal(value):
+ return str(value).replace(".", ",")
+
+
+def generate_payment_qr_codes(
+ event,
+ code,
+ amount,
+ bank_details_sepa_bic,
+ bank_details_sepa_name,
+ bank_details_sepa_iban
+):
+ out = []
+ qr_data = swiss_qrbill(
+ event,
+ code,
+ amount,
+ bank_details_sepa_bic,
+ bank_details_sepa_name,
+ bank_details_sepa_iban
+ )
+ if qr_data:
+ out.append({
+ "id": "qrbill",
+ "label": "QR-bill",
+ "html_prefix": mark_safe(''),
+ "qr_data": qr_data,
+ "css_class": "banktransfer-swiss-cross-overlay",
+ })
+
+ if event.currency == 'EUR' and bank_details_sepa_iban:
+ out.append({
+ "id": "girocode",
+ "label": "EPC-QR",
+ "qr_data": "\n".join(text_unidecode.unidecode(str(d or '')) for d in [
+ "BCD", # Service Tag: ‘BCD’
+ "002", # Version: V2
+ "2", # Character set: ISO 8859-1
+ "SCT", # Identification code: ‘SCT‘
+ bank_details_sepa_bic, # AT-23 BIC of the Beneficiary Bank
+ bank_details_sepa_name, # AT-21 Name of the Beneficiary
+ bank_details_sepa_iban, # AT-20 Account number of the Beneficiary
+ f"{event.currency}{dotdecimal(amount)}", # AT-04 Amount of the Credit Transfer in Euro
+ "", # AT-44 Purpose of the Credit Transfer
+ "", # AT-05 Remittance Information (Structured)
+ code, # AT-05 Remittance Information (Unstructured)
+ "", # Beneficiary to originator information
+ "",
+ ]),
+ })
+
+ qr_data = f"bank://singlepaymentsepa?" + urlencode({
+ "name": bank_details_sepa_name,
+ "iban":bank_details_sepa_iban,
+ "bic": bank_details_sepa_bic,
+ "amount": commadecimal(amount),
+ "reason": code,
+ "currency": event.currency,
+ })
+ out.append({
+ "id": "bezahlcode",
+ "label": "BezahlCode",
+ "qr_data": mark_safe(qr_data),
+ "link": qr_data,
+ "link_aria_label": _("Open BezahlCode in your banking app to start the payment process."),
+ })
+ return out
+
+def swiss_qrbill(
+ event,
+ code,
+ amount,
+ bank_details_sepa_bic,
+ bank_details_sepa_name,
+ bank_details_sepa_iban
+):
+ if not bank_details_sepa_iban or not bank_details_sepa_iban[:2] in ('CH', 'LI'):
+ return
+ if event.currency not in ('EUR', 'CHF'):
+ return
+ if not event.settings.invoice_address_from or not event.settings.invoice_address_from_country:
+ return
+
+ data_fields = [
+ 'SPC',
+ '0200',
+ '1',
+ bank_details_sepa_iban,
+ 'K',
+ bank_details_sepa_name[:70],
+ event.settings.invoice_address_from.replace('\n', ', ')[:70],
+ (event.settings.invoice_address_from_zipcode + ' ' + event.settings.invoice_address_from_city)[:70],
+ '',
+ '',
+ str(event.settings.invoice_address_from_country),
+ '', # rfu
+ '', # rfu
+ '', # rfu
+ '', # rfu
+ '', # rfu
+ '', # rfu
+ '', # rfu
+ str(amount),
+ event.currency,
+ '', # debtor address
+ '', # debtor address
+ '', # debtor address
+ '', # debtor address
+ '', # debtor address
+ '', # debtor address
+ '', # debtor address
+ 'NON',
+ '', # structured reference
+ code,
+ 'EPD',
+ ]
+
+ data_fields = [text_unidecode.unidecode(d or '') for d in data_fields]
+ return '\r\n'.join(data_fields)
diff --git a/src/pretix/plugins/banktransfer/payment.py b/src/pretix/plugins/banktransfer/payment.py
index 380c31a12d..c53aaa339a 100644
--- a/src/pretix/plugins/banktransfer/payment.py
+++ b/src/pretix/plugins/banktransfer/payment.py
@@ -46,12 +46,12 @@ from i18nfield.forms import I18nTextInput
from i18nfield.strings import LazyI18nString
from localflavor.generic.forms import BICFormField, IBANFormField
from localflavor.generic.validators import IBANValidator
-from text_unidecode import unidecode
from pretix.base.forms import I18nMarkdownTextarea
from pretix.base.models import InvoiceAddress, Order, OrderPayment, OrderRefund
from pretix.base.payment import BasePaymentProvider
from pretix.base.templatetags.money import money_filter
+from pretix.helpers.payment import generate_payment_qr_codes
from pretix.plugins.banktransfer.templatetags.ibanformat import ibanformat
from pretix.presale.views.cart import cart_session
@@ -313,51 +313,6 @@ class BankTransfer(BasePaymentProvider):
t += str(self.settings.get('bank_details', as_type=LazyI18nString))
return t
- def swiss_qrbill(self, payment):
- if not self.settings.get('bank_details_sepa_iban') or not self.settings.get('bank_details_sepa_iban')[:2] in ('CH', 'LI'):
- return
- if self.event.currency not in ('EUR', 'CHF'):
- return
- if not self.event.settings.invoice_address_from or not self.event.settings.invoice_address_from_country:
- return
-
- data_fields = [
- 'SPC',
- '0200',
- '1',
- self.settings.get('bank_details_sepa_iban'),
- 'K',
- self.settings.get('bank_details_sepa_name')[:70],
- self.event.settings.invoice_address_from.replace('\n', ', ')[:70],
- (self.event.settings.invoice_address_from_zipcode + ' ' + self.event.settings.invoice_address_from_city)[:70],
- '',
- '',
- str(self.event.settings.invoice_address_from_country),
- '', # rfu
- '', # rfu
- '', # rfu
- '', # rfu
- '', # rfu
- '', # rfu
- '', # rfu
- str(payment.amount),
- self.event.currency,
- '', # debtor address
- '', # debtor address
- '', # debtor address
- '', # debtor address
- '', # debtor address
- '', # debtor address
- '', # debtor address
- 'NON',
- '', # structured reference
- self._code(payment.order),
- 'EPD',
- ]
-
- data_fields = [unidecode(d or '') for d in data_fields]
- return '\r\n'.join(data_fields)
-
def payment_pending_render(self, request: HttpRequest, payment: OrderPayment):
template = get_template('pretixplugins/banktransfer/pending.html')
ctx = {
@@ -367,13 +322,18 @@ class BankTransfer(BasePaymentProvider):
'amount': payment.amount,
'payment_info': payment.info_data,
'settings': self.settings,
- 'swiss_qrbill': self.swiss_qrbill(payment),
- 'eu_barcodes': self.event.currency == 'EUR',
+ 'payment_qr_codes': generate_payment_qr_codes(
+ event=self.event,
+ code=self._code(payment.order),
+ amount=payment.amount,
+ bank_details_sepa_bic=self.settings.get('bank_details_sepa_bic'),
+ bank_details_sepa_name=self.settings.get('bank_details_sepa_name'),
+ bank_details_sepa_iban=self.settings.get('bank_details_sepa_iban'),
+ ) if self.settings.bank_details_type == "sepa" else None,
'pending_description': self.settings.get('pending_description', as_type=LazyI18nString),
'details': self.settings.get('bank_details', as_type=LazyI18nString),
'has_invoices': payment.order.invoices.exists(),
}
- ctx['any_barcodes'] = ctx['swiss_qrbill'] or ctx['eu_barcodes']
return template.render(ctx, request=request)
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:
diff --git a/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html
index 028d90bac2..5d25bad1b1 100644
--- a/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html
+++ b/src/pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/pending.html
@@ -1,7 +1,6 @@
{% load i18n %}
{% load l10n %}
{% load commadecimal %}
-{% load static %}
{% load dotdecimal %}
{% load ibanformat %}
{% load money %}
@@ -17,7 +16,7 @@
{% endblocktrans %}
-
+
- {% trans "Reference code (important):" %}
- {{ code }}
- {% trans "Amount:" %}
- {{ amount|money:event.currency }}
@@ -36,94 +35,7 @@
{% trans "We will send you an email as soon as we received your payment." %}
- {% if settings.bank_details_type == "sepa" and any_barcodes %}
-
-
- {% if swiss_qrbill %}
-
- {% endif %}
- {% if eu_barcodes %}
-
-
- {% endif %}
-
-
- {% if swiss_qrbill %}
-
- {% endif %}
- {% if eu_barcodes %}
-
-
- {% endif %}
-
-
- {% trans "Scan the QR code with your banking app" %}
-
-
+ {% if payment_qr_codes %}
+ {% include "pretixpresale/event/payment_qr_codes.html" %}
{% endif %}
-
-{% if swiss_qrbill %}
-
-{% endif %}
+
\ No newline at end of file
diff --git a/src/pretix/presale/templates/pretixpresale/event/payment_qr_codes.html b/src/pretix/presale/templates/pretixpresale/event/payment_qr_codes.html
new file mode 100644
index 0000000000..9c1500f082
--- /dev/null
+++ b/src/pretix/presale/templates/pretixpresale/event/payment_qr_codes.html
@@ -0,0 +1,44 @@
+{% load i18n %}
+{% load static %}
+
+{% if payment_qr_codes %}
+
+
+ {% for code_info in payment_qr_codes %}
+
+ {% endfor %}
+
+
+ {% for code_info in payment_qr_codes %}
+
+ {% endfor %}
+
+
+ {% trans "Scan the QR code with your banking app" %}
+
+
+ {% for code_info in payment_qr_codes %}
+ {% if code_info.id == "qrbill" %}
+
+ {% endif %}
+ {% endfor %}
+{% endif %}
\ No newline at end of file