diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index a3423be429..090e8d1c3b 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -78,7 +78,12 @@ class BasePaymentProvider: """ form = Form( data=(request.POST if request.method == 'POST' else None), - prefix='payment_%s' % self.identifier + prefix='payment_%s' % self.identifier, + initial={ + k.replace('payment_%s_' % self.identifier, ''): v + for k, v in request.session.items() + if k.startswith('payment_%s_' % self.identifier) + } ) form.fields = self.checkout_form_fields return form @@ -115,3 +120,11 @@ class BasePaymentProvider: return True else: return False + + def checkout_is_valid_session(self, request): + """ + This is called at the time the user tries to place the order. It should return + True, if the user's session is valid and all data your payment provider requires + in future steps is present. + """ + raise NotImplementedError() diff --git a/src/pretix/plugins/banktransfer/payment.py b/src/pretix/plugins/banktransfer/payment.py index facb865839..b7fc76b07e 100644 --- a/src/pretix/plugins/banktransfer/payment.py +++ b/src/pretix/plugins/banktransfer/payment.py @@ -26,3 +26,6 @@ class BankTransfer(BasePaymentProvider): def checkout_prepare(self, request, total): return True + + def checkout_is_valid_session(self, request): + return True diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index a8ca31c05a..245f79d358 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -17,3 +17,6 @@ class Stripe(BasePaymentProvider): required=False )) ]) + + def checkout_is_valid_session(self, request): + return False diff --git a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html index 0e78521d65..7ac95dc2c7 100644 --- a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html +++ b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -33,7 +33,21 @@ {# TODO: Question answers #} - {# TODO: Payment method #} +
+
+
+ +

+ {% trans "Payment" %} +

+
+
+ {{ payment }} +
+
+
+ class="panel-collapse collapse {% if selected == p.provider.identifier %}in{% endif %}">
{{ p.form }}
diff --git a/src/pretix/presale/views/__init__.py b/src/pretix/presale/views/__init__.py index 25157302d8..6beaa45dfe 100644 --- a/src/pretix/presale/views/__init__.py +++ b/src/pretix/presale/views/__init__.py @@ -5,6 +5,7 @@ from django.contrib.auth.views import redirect_to_login from django.core.urlresolvers import reverse from django.db.models import Q +from django.utils.functional import cached_property from django.utils.timezone import now from pretix.base.models import CartPosition @@ -35,6 +36,22 @@ class EventLoginRequiredMixin: class CartDisplayMixin: + @cached_property + def cartpos(self): + """ + A list of this users cart position + """ + return list(CartPosition.objects.current.filter( + Q(user=self.request.user) & Q(event=self.request.event) + ).order_by( + 'item', 'variation' + ).select_related( + 'item', 'variation' + ).prefetch_related( + 'variation__values', 'variation__values__prop', + 'item__questions', 'answers' + )) + def get_cart(self): cartpos = CartPosition.objects.current.filter( Q(user=self.request.user) & Q(event=self.request.event) diff --git a/src/pretix/presale/views/checkout.py b/src/pretix/presale/views/checkout.py index c18d3e2dc5..d238e958fb 100644 --- a/src/pretix/presale/views/checkout.py +++ b/src/pretix/presale/views/checkout.py @@ -76,7 +76,7 @@ class QuestionsForm(forms.Form): self.fields['question_%s' % q.identity] = field -class CheckoutStart(EventViewMixin, EventLoginRequiredMixin, TemplateView): +class CheckoutStart(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, TemplateView): template_name = "pretixpresale/event/checkout_questions.html" def get_success_url(self): @@ -115,22 +115,6 @@ class CheckoutStart(EventViewMixin, EventLoginRequiredMixin, TemplateView): formlist.append(form) return formlist - @cached_property - def cartpos(self): - """ - A list of this users cart position - """ - return list(CartPosition.objects.current.filter( - Q(user=self.request.user) & Q(event=self.request.event) - ).order_by( - 'item', 'variation' - ).select_related( - 'item', 'variation' - ).prefetch_related( - 'variation__values', 'variation__values__prop', - 'item__questions', 'answers' - )) - def post(self, *args, **kwargs): failed = False for form in self.forms: @@ -242,13 +226,79 @@ class PaymentDetails(EventViewMixin, EventLoginRequiredMixin, TemplateView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['providers'] = self.provider_forms + ctx['selected'] = self.request.POST.get('payment', self.request.session.get('payment', '')) return ctx class OrderConfirm(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, TemplateView): template_name = "pretixpresale/event/checkout_confirm.html" + def get_success_url(self): + return reverse('presale:event.checkout.success', kwargs={ + 'event': self.request.event.slug, + 'organizer': self.request.event.organizer.slug, + }) + + def get_url(self): + return reverse('presale:event.checkout.confirm', kwargs={ + 'event': self.request.event.slug, + 'organizer': self.request.event.organizer.slug, + }) + + def get_previous_url(self): + return reverse('presale:event.checkout.payment', kwargs={ + 'event': self.request.event.slug, + 'organizer': self.request.event.organizer.slug, + }) + def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['cart'] = self.get_cart() return ctx + + @cached_property + def payment_provider(self): + responses = register_payment_providers.send(self.request.event) + for receiver, response in responses: + provider = response(self.request.event) + if provider.identifier == self.request.session['payment']: + return provider + + def check_process(self, request): + if not self.payment_provider: + messages.error(request, _('The payment information you entered was incomplete.')) + return redirect(self.get_previous_url()) + if not self.payment_provider.checkout_is_valid_session(request): + messages.error(request, _('The payment information you entered was incomplete.')) + return redirect(self.get_previous_url()) + if len(self.cart_items) == 0: + messages.warning(request, _('Your cart is empty.')) + return redirect(reverse('presale:event.index', kwargs={ + 'event': self.request.event.slug, + 'organizer': self.request.event.organizer.slug, + })) + for cp in self.cart_items: + answ = { + aw.question_id: aw.answer for aw in cp.answers.all() + } + for q in cp.item.questions.all(): + if q.required and q.identity not in answ: + messages.warning(request, _('Please fill in answers to all required questions.')) + return redirect(reverse('presale:event.checkout.start', kwargs={ + 'event': self.request.event.slug, + 'organizer': self.request.event.organizer.slug, + })) + + def get(self, request, *args, **kwargs): + self.request = request + check = self.check_process(request) + if check: + return check + return super().get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + self.request = request + check = self.check_process(request) + if check: + return check + return super().post(request, *args, **kwargs)