forked from CGM_Public/pretix_original
Restrict Payment Providers to Sales Channels (#1481)
* Allow to restrict payment providers to specific sales channels * Fix test * Add `payment_restrictions_supported`-property to SalesChannels
This commit is contained in:
committed by
Raphael Michel
parent
384e7f8fc1
commit
6896682dd1
@@ -42,6 +42,17 @@ class SalesChannel:
|
||||
"""
|
||||
return True
|
||||
|
||||
@property
|
||||
def payment_restrictions_supported(self) -> bool:
|
||||
"""
|
||||
If this property is ``True``, organizers can restrict the usage of payment providers to this sales channel.
|
||||
|
||||
Example: pretixPOS provides its own sales channel, ignores the configured payment providers completely and
|
||||
handles payments locally. Therefor, this property should be set to ``False`` for the pretixPOS sales channel as
|
||||
the event organizer cannot restrict the usage of any payment provider through the backend.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def get_all_sales_channels():
|
||||
global _ALL_CHANNELS
|
||||
|
||||
@@ -20,6 +20,7 @@ from django_countries import Countries
|
||||
from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.forms import PlaceholderValidator
|
||||
from pretix.base.models import (
|
||||
CartPosition, Event, GiftCard, InvoiceAddress, Order, OrderPayment,
|
||||
@@ -62,12 +63,11 @@ class BasePaymentProvider:
|
||||
def __str__(self):
|
||||
return self.identifier
|
||||
|
||||
@property
|
||||
def is_implicit(self) -> bool:
|
||||
def is_implicit(self, request: HttpRequest) -> bool:
|
||||
"""
|
||||
Returns whether or whether not this payment provider is an "implicit" payment provider that will
|
||||
*always* and unconditionally be used if is_allowed() returns True and does not require any input.
|
||||
This is intended to be used by the FreePaymentProvider, which skips the payment choice page.
|
||||
This is intended to be used by the FreeOrderProvider, which skips the payment choice page.
|
||||
By default, this returns ``False``. Please do not set this if you don't know exactly what you are doing.
|
||||
"""
|
||||
return False
|
||||
@@ -278,8 +278,21 @@ class BasePaymentProvider:
|
||||
required=False,
|
||||
disabled=not self.event.settings.invoice_address_required
|
||||
)),
|
||||
('_restrict_to_sales_channels',
|
||||
forms.MultipleChoiceField(
|
||||
label=_('Restrict to specific sales channels'),
|
||||
choices=(
|
||||
(c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
|
||||
if c.payment_restrictions_supported
|
||||
),
|
||||
initial=['web'],
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
help_text=_(
|
||||
'Only allow the usage of this payment provider in the following sales channels'),
|
||||
))
|
||||
])
|
||||
d['_restricted_countries']._as_type = list
|
||||
d['_restrict_to_sales_channels']._as_type = list
|
||||
return d
|
||||
|
||||
def settings_form_clean(self, cleaned_data):
|
||||
@@ -391,7 +404,7 @@ class BasePaymentProvider:
|
||||
|
||||
The default implementation checks for the _availability_date setting to be either unset or in the future
|
||||
and for the _total_max and _total_min requirements to be met. It also checks the ``_restrict_countries``
|
||||
setting.
|
||||
and ``_restrict_to_sales_channels`` setting.
|
||||
|
||||
:param total: The total value without the payment method fee, after taxes.
|
||||
|
||||
@@ -432,6 +445,10 @@ class BasePaymentProvider:
|
||||
if str(ia.country) not in restricted_countries:
|
||||
return False
|
||||
|
||||
if hasattr(request, 'sales_channel') and request.sales_channel.identifier not in \
|
||||
self.settings.get('_restrict_to_sales_channels', as_type=list, default=['web']):
|
||||
return False
|
||||
|
||||
return timing and pricing
|
||||
|
||||
def payment_form_render(self, request: HttpRequest, total: Decimal) -> str:
|
||||
@@ -765,8 +782,7 @@ class ManualPayment(BasePaymentProvider):
|
||||
return _('In test mode, you can just manually mark this order as paid in the backend after it has been '
|
||||
'created.')
|
||||
|
||||
@property
|
||||
def is_implicit(self):
|
||||
def is_implicit(self, request: HttpRequest):
|
||||
return 'pretix.plugins.manualpayment' not in self.event.plugins
|
||||
|
||||
def is_allowed(self, request: HttpRequest, total: Decimal=None):
|
||||
|
||||
@@ -137,6 +137,10 @@ DEFAULTS = {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
},
|
||||
'payment_resellers__restrict_to_sales_channels': {
|
||||
'default': ['resellers'],
|
||||
'type': list
|
||||
},
|
||||
'payment_term_accept_late': {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
|
||||
@@ -719,7 +719,7 @@ class ProviderForm(SettingsForm):
|
||||
v.set_event(self.obj)
|
||||
|
||||
if hasattr(v, '_as_type'):
|
||||
self.initial[k] = self.obj.settings.get(k, as_type=v._as_type)
|
||||
self.initial[k] = self.obj.settings.get(k, as_type=v._as_type, default=v.initial)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="iconcol">
|
||||
{% for channel in provider.sales_channels %}
|
||||
<span class="fa fa-{{ channel.icon }} text-muted"
|
||||
data-toggle="tooltip" title="{% trans channel.verbose_name %}"></span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url 'control:event.settings.payment.provider' event=request.event.slug organizer=request.organizer.slug provider=provider.identifier %}"
|
||||
class="btn btn-default">
|
||||
|
||||
@@ -392,11 +392,15 @@ class PaymentSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['providers'] = sorted(
|
||||
[p for p in self.request.event.get_payment_providers().values()
|
||||
if not p.is_implicit and (p.settings_form_fields or p.settings_content_render(self.request))],
|
||||
if not (p.is_implicit(self.request) if callable(p.is_implicit) else p.is_implicit) and
|
||||
(p.settings_form_fields or p.settings_content_render(self.request))],
|
||||
key=lambda s: s.verbose_name
|
||||
)
|
||||
|
||||
sales_channels = get_all_sales_channels()
|
||||
for p in context['providers']:
|
||||
p.show_enabled = p.is_enabled
|
||||
p.sales_channels = [sales_channels[channel] for channel in p.settings.get('_restrict_to_sales_channels', as_type=list, default=['web'])]
|
||||
if p.is_meta:
|
||||
p.show_enabled = p.settings._enabled in (True, 'True')
|
||||
return context
|
||||
|
||||
@@ -589,7 +589,7 @@ class PaymentStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
return False
|
||||
|
||||
for p in self.request.event.get_payment_providers().values():
|
||||
if p.is_implicit:
|
||||
if p.is_implicit(request) if callable(p.is_implicit) else p.is_implicit:
|
||||
if self._is_allowed(p, request):
|
||||
self.cart_session['payment'] = p.identifier
|
||||
return False
|
||||
|
||||
@@ -359,6 +359,7 @@ class EventsTest(SoupTest):
|
||||
'payment_banktransfer__fee_abs': '12.23',
|
||||
'payment_banktransfer_bank_details_type': 'other',
|
||||
'payment_banktransfer_bank_details_0': 'Test',
|
||||
'payment_banktransfer__restrict_to_sales_channels': ['web'],
|
||||
})
|
||||
self.event1.settings.flush()
|
||||
assert self.event1.settings.get('payment_banktransfer__enabled', as_type=bool)
|
||||
|
||||
Reference in New Issue
Block a user