mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Stripe connect: Allow to set application fee
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.base.forms import SettingsForm
|
||||
|
||||
|
||||
class StripeKeyValidator:
|
||||
def __init__(self, prefix):
|
||||
@@ -21,3 +23,18 @@ class StripeKeyValidator:
|
||||
'prefix': self._prefixes[0],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class OrganizerStripeSettingsForm(SettingsForm):
|
||||
payment_stripe_connect_app_fee_percent = forms.DecimalField(
|
||||
label=_('Stripe Connect: App fee (percent)'),
|
||||
required=False,
|
||||
)
|
||||
payment_stripe_connect_app_fee_max = forms.DecimalField(
|
||||
label=_('Stripe Connect: App fee (max)'),
|
||||
required=False,
|
||||
)
|
||||
payment_stripe_connect_app_fee_min = forms.DecimalField(
|
||||
label=_('Stripe Connect: App fee (min)'),
|
||||
required=False,
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ import json
|
||||
import logging
|
||||
import urllib.parse
|
||||
from collections import OrderedDict
|
||||
from decimal import Decimal
|
||||
|
||||
import stripe
|
||||
from django import forms
|
||||
@@ -256,6 +257,20 @@ class StripeMethod(BasePaymentProvider):
|
||||
def _get_amount(self, payment):
|
||||
return self._decimal_to_int(payment.amount)
|
||||
|
||||
def _connect_kwargs(self, payment):
|
||||
d = {}
|
||||
if self.settings.connect_client_id and self.settings.connect_user_id:
|
||||
fee = Decimal('0.00')
|
||||
if self.settings.get('connect_app_fee_percent', as_type=Decimal):
|
||||
fee = round_decimal(self.settings.get('connect_app_fee_percent', as_type=Decimal) * payment.amount / Decimal('100.00'), self.event.currency)
|
||||
if self.settings.connect_app_fee_max:
|
||||
fee = min(fee, self.settings.get('connect_app_fee_max', as_type=Decimal))
|
||||
if self.settings.get('connect_app_fee_min', as_type=Decimal):
|
||||
fee = max(fee, self.settings.get('connect_app_fee_min', as_type=Decimal))
|
||||
if fee:
|
||||
d['application_fee_amount'] = self._decimal_to_int(fee)
|
||||
return d
|
||||
|
||||
@property
|
||||
def api_kwargs(self):
|
||||
if self.settings.connect_client_id and self.settings.connect_user_id:
|
||||
@@ -301,6 +316,7 @@ class StripeMethod(BasePaymentProvider):
|
||||
code=payment.order.code
|
||||
)[:22]
|
||||
params.update(self.api_kwargs)
|
||||
params.update(self._connect_kwargs(payment))
|
||||
charge = stripe.Charge.create(
|
||||
amount=self._get_amount(payment),
|
||||
currency=self.event.currency.lower(),
|
||||
@@ -612,6 +628,9 @@ class StripeCC(StripeMethod):
|
||||
|
||||
try:
|
||||
if self.payment_is_valid_session(request):
|
||||
params = {}
|
||||
params.update(self._connect_kwargs(payment))
|
||||
params.update(self.api_kwargs)
|
||||
intent = stripe.PaymentIntent.create(
|
||||
amount=self._get_amount(payment),
|
||||
currency=self.event.currency.lower(),
|
||||
@@ -638,7 +657,7 @@ class StripeCC(StripeMethod):
|
||||
'payment': payment.pk,
|
||||
'hash': hashlib.sha1(payment.order.secret.lower().encode()).hexdigest(),
|
||||
}),
|
||||
**self.api_kwargs
|
||||
**params
|
||||
)
|
||||
else:
|
||||
payment_info = json.loads(payment.info)
|
||||
|
||||
@@ -4,7 +4,7 @@ from collections import OrderedDict
|
||||
from django import forms
|
||||
from django.dispatch import receiver
|
||||
from django.template.loader import get_template
|
||||
from django.urls import resolve
|
||||
from django.urls import resolve, reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.base.settings import settings_hierarkey
|
||||
@@ -12,6 +12,7 @@ from pretix.base.signals import (
|
||||
logentry_display, register_global_settings, register_payment_providers,
|
||||
requiredaction_display,
|
||||
)
|
||||
from pretix.control.signals import nav_organizer
|
||||
from pretix.plugins.stripe.forms import StripeKeyValidator
|
||||
from pretix.presale.signals import html_head
|
||||
|
||||
@@ -121,6 +122,18 @@ def register_global_settings(sender, **kwargs):
|
||||
StripeKeyValidator('pk_test_'),
|
||||
),
|
||||
)),
|
||||
('payment_stripe_connect_app_fee_percent', forms.DecimalField(
|
||||
label=_('Stripe Connect: App fee (percent)'),
|
||||
required=False,
|
||||
)),
|
||||
('payment_stripe_connect_app_fee_max', forms.DecimalField(
|
||||
label=_('Stripe Connect: App fee (max)'),
|
||||
required=False,
|
||||
)),
|
||||
('payment_stripe_connect_app_fee_min', forms.DecimalField(
|
||||
label=_('Stripe Connect: App fee (min)'),
|
||||
required=False,
|
||||
)),
|
||||
])
|
||||
|
||||
|
||||
@@ -141,3 +154,20 @@ def pretixcontrol_action_display(sender, action, request, **kwargs):
|
||||
|
||||
ctx = {'data': data, 'event': sender, 'action': action}
|
||||
return template.render(ctx, request)
|
||||
|
||||
|
||||
@receiver(nav_organizer, dispatch_uid="stripe_nav_organizer")
|
||||
def nav_o(sender, request, organizer, **kwargs):
|
||||
if request.user.has_active_staff_session(request.session.session_key):
|
||||
url = resolve(request.path_info)
|
||||
return [{
|
||||
'label': _('Stripe Connect'),
|
||||
'url': reverse('plugins:stripe:settings.connect', kwargs={
|
||||
'organizer': request.organizer.slug
|
||||
}),
|
||||
'parent': reverse('control:organizer.edit', kwargs={
|
||||
'organizer': request.organizer.slug
|
||||
}),
|
||||
'active': 'settings.connect' in url.url_name,
|
||||
}]
|
||||
return []
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends "pretixcontrol/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load hierarkey_form %}
|
||||
{% load formset_tags %}
|
||||
{% block title %}{% trans "Stripe Connect" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% trans "Stripe Connect" %}
|
||||
</h1>
|
||||
|
||||
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{% url "control:global.settings" as g_url %}
|
||||
{% propagated request.organizer g_url "payment_stripe_connect_app_fee_percent" "payment_stripe_connect_app_fee_min" "payment_stripe_connect_app_fee_max" %}
|
||||
{% bootstrap_form form layout="control" %}
|
||||
{% endpropagated %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -3,8 +3,9 @@ from django.conf.urls import include, url
|
||||
from pretix.multidomain import event_url
|
||||
|
||||
from .views import (
|
||||
ReturnView, ScaReturnView, ScaView, applepay_association, oauth_disconnect,
|
||||
oauth_return, redirect_view, webhook,
|
||||
OrganizerSettingsFormView, ReturnView, ScaReturnView, ScaView,
|
||||
applepay_association, oauth_disconnect, oauth_return, redirect_view,
|
||||
webhook,
|
||||
)
|
||||
|
||||
event_patterns = [
|
||||
@@ -24,6 +25,8 @@ organizer_patterns = [
|
||||
urlpatterns = [
|
||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/stripe/disconnect/',
|
||||
oauth_disconnect, name='oauth.disconnect'),
|
||||
url(r'^control/organizer/(?P<organizer>[^/]+)/stripeconnect/',
|
||||
OrganizerSettingsFormView.as_view(), name='settings.connect'),
|
||||
url(r'^_stripe/webhook/$', webhook, name='webhook'),
|
||||
url(r'^_stripe/oauth_return/$', oauth_return, name='oauth.return'),
|
||||
url(r'^.well-known/apple-developer-merchantid-domain-association$', applepay_association, name='applepay.association'),
|
||||
|
||||
@@ -17,14 +17,20 @@ from django.views import View
|
||||
from django.views.decorators.clickjacking import xframe_options_exempt
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.generic import FormView
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.base.models import Event, Order, OrderPayment, Quota
|
||||
from pretix.base.models import Event, Order, OrderPayment, Organizer, Quota
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
from pretix.control.permissions import event_permission_required
|
||||
from pretix.control.permissions import (
|
||||
AdministratorPermissionRequiredMixin, event_permission_required,
|
||||
)
|
||||
from pretix.control.views.event import DecoupleMixin
|
||||
from pretix.control.views.organizer import OrganizerDetailViewMixin
|
||||
from pretix.multidomain.urlreverse import eventreverse
|
||||
from pretix.plugins.stripe.forms import OrganizerStripeSettingsForm
|
||||
from pretix.plugins.stripe.models import ReferencedStripeObject
|
||||
from pretix.plugins.stripe.payment import StripeCC, StripeSettingsHolder
|
||||
from pretix.plugins.stripe.tasks import (
|
||||
@@ -580,3 +586,37 @@ class ScaReturnView(StripeOrderView, View):
|
||||
self.order.refresh_from_db()
|
||||
|
||||
return render(request, 'pretixplugins/stripe/sca_return.html', {'order': self.order})
|
||||
|
||||
|
||||
class OrganizerSettingsFormView(DecoupleMixin, OrganizerDetailViewMixin, AdministratorPermissionRequiredMixin, FormView):
|
||||
model = Organizer
|
||||
permission = 'can_change_organizer_settings'
|
||||
form_class = OrganizerStripeSettingsForm
|
||||
template_name = 'pretixplugins/stripe/organizer_stripe.html'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('plugins:stripe:settings.connect', kwargs={
|
||||
'organizer': self.request.organizer.slug,
|
||||
})
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['obj'] = self.request.organizer
|
||||
return kwargs
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
if form.has_changed():
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.settings', user=self.request.user, data={
|
||||
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||
}
|
||||
)
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return redirect(self.get_success_url())
|
||||
else:
|
||||
messages.error(self.request, _('We could not save your changes. See below for details.'))
|
||||
return self.get(request)
|
||||
|
||||
Reference in New Issue
Block a user