Hi,
first of all: thank you for Hi.Events. I volunteer for a small German non-profit
association and chose it over alf.io and pretix,
mainly because of the arm64 images, the complete German translation and how
actively the project is maintained. It runs on a Raspberry Pi and does exactly
what we need.
While preparing for our first paid ticket sale, we ran into a legal issue that
I think affects every EU deployment, so I wanted to report it properly rather
than just patch around it.
The problem
Article 8(2) of the EU Consumer Rights Directive 2011/83/EU requires that
the button which concludes a paid order must be labelled only with the words
"order with obligation to pay" — or a corresponding unambiguous formulation
making clear that placing the order entails an obligation to pay.
The consequence of non-compliance is unusually severe. The same article states:
"If the trader has not complied with this subparagraph, the consumer shall
not be bound by the contract or order."
In German law this is implemented in § 312j (3) and (4) BGB: with an incorrect
label, the contract simply does not come into existence. The buyer would
not have to pay, but would also have no claim to the ticket.
Where it occurs
frontend/src/components/routes/product-widget/Payment/index.tsx (~line 151):
{order?.is_payment_required ? (
<Group gap={8} wrap="nowrap">
<IconLock size={16}/>
<Text fw={600}>{t`Pay`} {formatCurrency(order.total_gross, order.currency)}</Text>
</Group>
) : t`Complete Payment`}
For offline/bank-transfer payments this button is the final step — it triggers
transitionOrderToOfflinePayment, and the offline payment method component
itself renders only the payment instructions with no further button. So this is
the button that legally concludes the order.
Why this is not just a German problem
The message id Pay (4fL/V7) is translated the same way in every EU locale
shipped with the project:
| Locale |
Current |
Required (example) |
| de |
"Bezahlen" |
"Zahlungspflichtig bestellen" |
| fr |
"Payer" |
"Commande avec obligation de paiement" |
| it |
"Paga" |
"Ordine con obbligo di pagamento" |
| es |
"Pagar" |
"Pedido con obligación de pago" |
| nl |
"Betalen" |
"Bestelling met betalingsverplichting" |
| pt |
"Pagar" |
"Encomenda com obrigação de pagamento" |
"Pay" alone is generally not considered sufficient, because it indicates the
act of paying rather than the placing of a binding order. Some jurisdictions
are stricter than others, but the directive applies EU-wide.
Suggested fixes
Two options, not mutually exclusive:
-
Short term — adjust the translations. Changing the Pay message string
per locale would resolve it for most deployments. Note that the amount is
appended, so the result reads e.g. "Zahlungspflichtig bestellen 12,00 €",
which is acceptable but slightly awkward. Splitting the label and the amount
into separate elements would read better.
-
Long term — make the label configurable. There is already
continue_button_text in the event settings, but it only applies to the
homepage button, not to the checkout. An equivalent setting for the final
checkout button would let organisers meet their local requirements without
patching the image. This would also help outside the EU, where other
wording rules exist.
I would be happy to open a pull request for either approach if that is useful
to you — just let me know which direction you would prefer.
What we are doing meanwhile
We build a thin image on top of the official one that rewrites the string in
the compiled catalogues, with a build-time assertion so that the build fails
if the message id changes in a future release. It works, but it is obviously a
workaround, and every self-hosting EU organiser would need to do the same.
Thanks again for the project — and for considering this.
Best regards,
Daniel Ertl
Hi,
first of all: thank you for Hi.Events. I volunteer for a small German non-profit
association and chose it over alf.io and pretix,
mainly because of the arm64 images, the complete German translation and how
actively the project is maintained. It runs on a Raspberry Pi and does exactly
what we need.
While preparing for our first paid ticket sale, we ran into a legal issue that
I think affects every EU deployment, so I wanted to report it properly rather
than just patch around it.
The problem
Article 8(2) of the EU Consumer Rights Directive 2011/83/EU requires that
the button which concludes a paid order must be labelled only with the words
"order with obligation to pay" — or a corresponding unambiguous formulation
making clear that placing the order entails an obligation to pay.
The consequence of non-compliance is unusually severe. The same article states:
In German law this is implemented in § 312j (3) and (4) BGB: with an incorrect
label, the contract simply does not come into existence. The buyer would
not have to pay, but would also have no claim to the ticket.
Where it occurs
frontend/src/components/routes/product-widget/Payment/index.tsx(~line 151):For offline/bank-transfer payments this button is the final step — it triggers
transitionOrderToOfflinePayment, and the offline payment method componentitself renders only the payment instructions with no further button. So this is
the button that legally concludes the order.
Why this is not just a German problem
The message id
Pay(4fL/V7) is translated the same way in every EU localeshipped with the project:
"Pay" alone is generally not considered sufficient, because it indicates the
act of paying rather than the placing of a binding order. Some jurisdictions
are stricter than others, but the directive applies EU-wide.
Suggested fixes
Two options, not mutually exclusive:
Short term — adjust the translations. Changing the
Paymessage stringper locale would resolve it for most deployments. Note that the amount is
appended, so the result reads e.g. "Zahlungspflichtig bestellen 12,00 €",
which is acceptable but slightly awkward. Splitting the label and the amount
into separate elements would read better.
Long term — make the label configurable. There is already
continue_button_textin the event settings, but it only applies to thehomepage button, not to the checkout. An equivalent setting for the final
checkout button would let organisers meet their local requirements without
patching the image. This would also help outside the EU, where other
wording rules exist.
I would be happy to open a pull request for either approach if that is useful
to you — just let me know which direction you would prefer.
What we are doing meanwhile
We build a thin image on top of the official one that rewrites the string in
the compiled catalogues, with a build-time assertion so that the build fails
if the message id changes in a future release. It works, but it is obviously a
workaround, and every self-hosting EU organiser would need to do the same.
Thanks again for the project — and for considering this.
Best regards,
Daniel Ertl