diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py
index 57bf2150e..f1bc72c4b 100644
--- a/src/pretix/base/payment.py
+++ b/src/pretix/base/payment.py
@@ -25,6 +25,9 @@ class BasePaymentProvider:
def __init__(self, event: Event):
self.event = event
self.settings = SettingsSandbox('payment', self.identifier, event)
+ # Default values
+ if self.settings.get('_fee_reverse_calc') is None:
+ self.settings.set('_fee_reverse_calc', True)
def __str__(self):
return self.identifier
@@ -48,7 +51,11 @@ class BasePaymentProvider:
"""
fee_abs = self.settings.get('_fee_abs', as_type=Decimal, default=0)
fee_percent = self.settings.get('_fee_percent', as_type=Decimal, default=0)
- return round_decimal(price * fee_percent / 100) + fee_abs
+ fee_reverse_calc = self.settings.get('_fee_reverse_calc', as_type=bool, default=True)
+ if fee_reverse_calc:
+ return round_decimal((price + fee_abs) * (1 / (1 - fee_percent / 100)) - price)
+ else:
+ return round_decimal(price * fee_percent / 100) + fee_abs
@property
def verbose_name(self) -> str:
@@ -118,6 +125,14 @@ class BasePaymentProvider:
help_text=_('Percentage'),
required=False
)),
+ ('_fee_reverse_calc',
+ forms.BooleanField(
+ label=_('Calculate the fee from the total value including the fee.'),
+ help_text=_('We recommend you to enable this if you want your users to pay the payment fees of your '
+ 'payment provider. Click here '
+ 'for detailled information on what this does.'),
+ required=False
+ )),
])
def settings_content_render(self, request: HttpRequest) -> str:
diff --git a/src/pretix/control/templates/pretixcontrol/help/base.html b/src/pretix/control/templates/pretixcontrol/help/base.html
new file mode 100644
index 000000000..d328414da
--- /dev/null
+++ b/src/pretix/control/templates/pretixcontrol/help/base.html
@@ -0,0 +1,7 @@
+{% extends "pretixcontrol/base.html" %}
+{% load i18n %}
+{% block content %}
+
{% trans "Help center" %}
+ {% block inner %}
+ {% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/src/pretix/control/templates/pretixcontrol/help/payment/fee_reverse.html b/src/pretix/control/templates/pretixcontrol/help/payment/fee_reverse.html
new file mode 100644
index 000000000..89937d6ea
--- /dev/null
+++ b/src/pretix/control/templates/pretixcontrol/help/payment/fee_reverse.html
@@ -0,0 +1,68 @@
+{% extends "pretixcontrol/help/base.html" %}
+{% block title %}Payment fee calculation{% endblock %}
+{% block inner %}
+ Payment fee calculation
+
+ If you configure a fee for a payment method, there are two possible ways for us to calculate this. Let's
+ assume that your payment provider, e.g. PayPal, charges you 5 % fees and you want to charge your users the
+ same 5 %, such that for a ticket with a list price of 100 € you will get your full 100 €.
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index 5f422729f..460cb21d3 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -1,8 +1,8 @@
from django.conf.urls import include, url
from pretix.control.views import (
- attendees, auth, dashboards, event, item, main, orders, organizer, user,
- vouchers,
+ attendees, auth, dashboards, event, help, item, main, orders, organizer,
+ user, vouchers,
)
urlpatterns = [
@@ -78,4 +78,5 @@ urlpatterns = [
url(r'^orders/$', orders.OrderList.as_view(), name='event.orders'),
url(r'^attendees/$', attendees.AttendeeList.as_view(), name='event.attendees'),
])),
+ url(r'^help/(?P[^.]+)$', help.HelpView.as_view(), name='help'),
]
diff --git a/src/pretix/control/views/help.py b/src/pretix/control/views/help.py
new file mode 100644
index 000000000..da69f6203
--- /dev/null
+++ b/src/pretix/control/views/help.py
@@ -0,0 +1,23 @@
+from django import template
+from django.http import Http404
+from django.shortcuts import render
+from django.views.generic import View
+
+from pretix.base.models import Organizer
+
+
+class HelpView(View):
+ model = Organizer
+ context_object_name = 'organizers'
+ template_name = 'pretixcontrol/organizers/index.html'
+ paginate_by = 30
+
+ def get(self, request, *args, **kwargs):
+ try:
+ locale = request.LANGUAGE_CODE
+ return render(request, 'pretixcontrol/help/%s.%s.html' % (kwargs.get('topic'), locale), {})
+ except template.TemplateDoesNotExist:
+ try:
+ return render(request, 'pretixcontrol/help/%s.html' % kwargs.get('topic'), {})
+ except template.TemplateDoesNotExist:
+ raise Http404('')
diff --git a/src/tests/base/test_payment.py b/src/tests/base/test_payment.py
new file mode 100644
index 000000000..106a695eb
--- /dev/null
+++ b/src/tests/base/test_payment.py
@@ -0,0 +1,54 @@
+import time
+from decimal import Decimal
+
+import pytest
+from django.utils.timezone import now
+from tests.testdummy.payment import DummyPaymentProvider
+
+from pretix.base.models import Event, EventLock, Organizer
+from pretix.base.services import locking
+
+
+@pytest.fixture
+def event():
+ o = Organizer.objects.create(name='Dummy', slug='dummy')
+ event = Event.objects.create(
+ organizer=o, name='Dummy', slug='dummy',
+ date_from=now()
+ )
+ return event
+
+
+@pytest.mark.django_db
+def test_payment_fee_forward(event):
+ prov = DummyPaymentProvider(event)
+ prov.settings.set('_fee_abs', Decimal('0.30'))
+ prov.settings.set('_fee_percent', Decimal('5.00'))
+ prov.settings.set('_fee_reverse_calc', False)
+ assert prov.calculate_fee(Decimal('100.00')) == Decimal('5.30')
+
+
+@pytest.mark.django_db
+def test_payment_fee_reverse_percent(event):
+ prov = DummyPaymentProvider(event)
+ prov.settings.set('_fee_abs', Decimal('0.00'))
+ prov.settings.set('_fee_percent', Decimal('5.00'))
+ prov.settings.set('_fee_reverse_calc', True)
+ assert prov.calculate_fee(Decimal('100.00')) == Decimal('5.26')
+
+
+@pytest.mark.django_db
+def test_payment_fee_reverse_percent_and_abs(event):
+ prov = DummyPaymentProvider(event)
+ prov.settings.set('_fee_abs', Decimal('0.30'))
+ prov.settings.set('_fee_percent', Decimal('2.90'))
+ prov.settings.set('_fee_reverse_calc', True)
+ assert prov.calculate_fee(Decimal('100.00')) == Decimal('3.30')
+
+
+@pytest.mark.django_db
+def test_payment_fee_reverse_percent_and_abs_default(event):
+ prov = DummyPaymentProvider(event)
+ prov.settings.set('_fee_abs', Decimal('0.30'))
+ prov.settings.set('_fee_percent', Decimal('2.90'))
+ assert prov.calculate_fee(Decimal('100.00')) == Decimal('3.30')
diff --git a/src/tests/testdummy/payment.py b/src/tests/testdummy/payment.py
new file mode 100644
index 000000000..8be66dfbb
--- /dev/null
+++ b/src/tests/testdummy/payment.py
@@ -0,0 +1,19 @@
+import logging
+
+from pretix.base.payment import BasePaymentProvider
+
+logger = logging.getLogger('tests.testdummy.ticketoutput')
+
+
+class DummyPaymentProvider(BasePaymentProvider):
+ identifier = 'testdummy'
+ verbose_name = 'Test dummy'
+
+ def order_pending_render(self, request, order) -> str:
+ pass
+
+ def payment_is_valid_session(self, request) -> bool:
+ pass
+
+ def checkout_confirm_render(self, request) -> str:
+ pass
diff --git a/src/tests/testdummy/signals.py b/src/tests/testdummy/signals.py
index 56955b6ea..500070f3d 100644
--- a/src/tests/testdummy/signals.py
+++ b/src/tests/testdummy/signals.py
@@ -1,9 +1,17 @@
from django.dispatch import receiver
-from pretix.base.signals import register_ticket_outputs
+from pretix.base.signals import (
+ register_payment_providers, register_ticket_outputs,
+)
@receiver(register_ticket_outputs, dispatch_uid="output_dummy")
def register_ticket_outputs(sender, **kwargs):
from .ticketoutput import DummyTicketOutput
return DummyTicketOutput
+
+
+@receiver(register_payment_providers, dispatch_uid="payment_dummy")
+def register_ticket_outputs(sender, **kwargs):
+ from .payment import DummyPaymentProvider
+ return DummyPaymentProvider