Enhance payment method validation

This commit is contained in:
Raphael Michel
2015-03-07 13:32:09 +01:00
parent 2acc653807
commit 3bacfdcb9f
7 changed files with 121 additions and 20 deletions

View File

@@ -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()

View File

@@ -26,3 +26,6 @@ class BankTransfer(BasePaymentProvider):
def checkout_prepare(self, request, total):
return True
def checkout_is_valid_session(self, request):
return True

View File

@@ -17,3 +17,6 @@ class Stripe(BasePaymentProvider):
required=False
))
])
def checkout_is_valid_session(self, request):
return False

View File

@@ -33,7 +33,21 @@
</div>
</div>
{# TODO: Question answers #}
{# TODO: Payment method #}
<div class="row-fluid">
<div class="panel panel-primary">
<div class="panel-heading">
<div class="pull-right">
<a href="{% url "presale:event.checkout.payment" organizer=request.event.organizer.slug event=request.event.slug %}">{% trans "Modify" %}</a>
</div>
<h3 class="panel-title">
{% trans "Payment" %}
</h3>
</div>
<div class="panel-body">
{{ payment }}
</div>
</div>
</div>
<div class="row checkout-button-row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-block btn-primary btn-lg" type="submit">

View File

@@ -16,13 +16,14 @@
<strong class="pull-right">+ {{ p.fee|floatformat:2 }} {{ event.currency }}</strong>
<input type="radio" name="payment" value="{{ p.provider.identifier }}"
data-parent="#payment_accordion"
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}" />
<strong>{{ p.provider.verbose_name }}</strong>
</label>
</h4>
</div>
<div id="payment_{{ p.provider.identifier }}"
class="panel-collapse collapse {% if request.POST.payment == p.provider.identifier %}in{% endif %}">
class="panel-collapse collapse {% if selected == p.provider.identifier %}in{% endif %}">
<div class="panel-body form-horizontal">
{{ p.form }}
</div>

View File

@@ -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)

View File

@@ -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)