mirror of
https://github.com/pretix/pretix.git
synced 2026-05-08 15:44:02 +00:00
Refs #33 -- Added UI and Stripe support for retrying failed payments
This commit is contained in:
@@ -17,10 +17,15 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if can_retry %}
|
||||
<a href="{% url "presale:event.order.pay" organizer=request.event.organizer.slug event=request.event.slug order=order.code %}"
|
||||
class="btn btn-primary pull-right"><i class="fa fa-money"></i> {% trans "Complete payment" %}</a>
|
||||
{% endif %}
|
||||
{{ payment }}
|
||||
<strong>{% blocktrans trimmed with date=order.expires|date:"SHORT_DATE_FORMAT" %}
|
||||
Please complete your payment before {{ date }}
|
||||
{% endblocktrans %}</strong>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "pretixpresale/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Cancel order" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% blocktrans trimmed with code=order.code %}
|
||||
Pay order: {{ code }}
|
||||
{% endblocktrans %}
|
||||
</h2>
|
||||
|
||||
<form method="post" class="form-horizontal" href="">
|
||||
{% csrf_token %}
|
||||
<div class="form-horizontal">
|
||||
{{ form }}
|
||||
</div>
|
||||
<div class="row checkout-button-row">
|
||||
<div class="col-md-4">
|
||||
<a class="btn btn-block btn-default btn-lg"
|
||||
href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug order=order.code %}">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<button class="btn btn-block btn-primary btn-lg" type="submit">
|
||||
{% trans "Continue" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,43 @@
|
||||
{% extends "pretixpresale/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Cancel order" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% blocktrans trimmed with code=order.code %}
|
||||
Pay order: {{ code }}
|
||||
{% endblocktrans %}
|
||||
</h2>
|
||||
|
||||
<form method="post" class="form-horizontal" href="">
|
||||
{% csrf_token %}
|
||||
|
||||
<p>{% trans "Please confirm the following payment details." %}</p>
|
||||
<div class="row-fluid">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{{ payment_provider.verbose_name }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ payment }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row checkout-button-row">
|
||||
<div class="col-md-4">
|
||||
<a class="btn btn-block btn-default btn-lg"
|
||||
href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug order=order.code %}">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<button class="btn btn-block btn-primary btn-lg" type="submit">
|
||||
{% trans "Pay now" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@@ -23,6 +23,10 @@ urlpatterns = [
|
||||
name='event.order.cancel'),
|
||||
url(r'^order/(?P<order>[^/]+)/modify$', pretix.presale.views.order.OrderModify.as_view(),
|
||||
name='event.order.modify'),
|
||||
url(r'^order/(?P<order>[^/]+)/pay$', pretix.presale.views.order.OrderPay.as_view(),
|
||||
name='event.order.pay'),
|
||||
url(r'^order/(?P<order>[^/]+)/pay/confirm$', pretix.presale.views.order.OrderPayDo.as_view(),
|
||||
name='event.order.pay.confirm'),
|
||||
url(r'^order/(?P<order>[^/]+)/download/(?P<output>[^/]+)$', pretix.presale.views.order.OrderDownload.as_view(),
|
||||
name='event.order.download'),
|
||||
url(r'^login$', pretix.presale.views.event.EventLogin.as_view(), name='event.checkout.login'),
|
||||
|
||||
@@ -156,7 +156,7 @@ class PaymentDetails(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin,
|
||||
providers.append({
|
||||
'provider': provider,
|
||||
'fee': fee,
|
||||
'form': provider.checkout_form_render(self.request),
|
||||
'form': provider.payment_form_render(self.request),
|
||||
})
|
||||
return providers
|
||||
|
||||
@@ -220,7 +220,7 @@ class OrderConfirm(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, Ch
|
||||
if 'payment' not in request.session or not self.payment_provider:
|
||||
messages.error(request, _('The payment information you entered was incomplete.'))
|
||||
return redirect(self.get_payment_url())
|
||||
if not self.payment_provider.checkout_is_valid_session(request) or \
|
||||
if not self.payment_provider.payment_is_valid_session(request) or \
|
||||
not self.payment_provider.is_enabled or \
|
||||
not self.payment_provider.is_allowed(request):
|
||||
messages.error(request, _('The payment information you entered was incomplete.'))
|
||||
@@ -259,7 +259,7 @@ class OrderConfirm(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, Ch
|
||||
return redirect(self.get_confirm_url())
|
||||
else:
|
||||
messages.success(request, _('Your order has been placed.'))
|
||||
resp = self.payment_provider.checkout_perform(request, order)
|
||||
resp = self.payment_provider.payment_perform(request, order)
|
||||
return redirect(resp or self.get_order_url(order))
|
||||
|
||||
def get_previous_url(self):
|
||||
|
||||
@@ -13,6 +13,7 @@ from pretix.presale.views.checkout import QuestionsViewMixin
|
||||
|
||||
|
||||
class OrderDetailMixin:
|
||||
|
||||
@cached_property
|
||||
def order(self):
|
||||
try:
|
||||
@@ -24,6 +25,21 @@ class OrderDetailMixin:
|
||||
except Order.DoesNotExist:
|
||||
return None
|
||||
|
||||
@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.order.payment_provider:
|
||||
return provider
|
||||
|
||||
def get_order_url(self):
|
||||
return reverse('presale:event.order', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'order': self.order.code,
|
||||
})
|
||||
|
||||
|
||||
class OrderDetails(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
|
||||
CartDisplayMixin, TemplateView):
|
||||
@@ -35,14 +51,6 @@ class OrderDetails(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
|
||||
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
@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.order.payment_provider:
|
||||
return provider
|
||||
|
||||
@cached_property
|
||||
def download_buttons(self):
|
||||
buttons = []
|
||||
@@ -75,11 +83,94 @@ class OrderDetails(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
|
||||
)
|
||||
if self.order.status == Order.STATUS_PENDING:
|
||||
ctx['payment'] = self.payment_provider.order_pending_render(self.request, self.order)
|
||||
ctx['can_retry'] = self.payment_provider.order_can_retry(self.order) and self.payment_provider.is_enabled
|
||||
elif self.order.status == Order.STATUS_PAID:
|
||||
ctx['payment'] = self.payment_provider.order_paid_render(self.request, self.order)
|
||||
ctx['can_retry'] = False
|
||||
return ctx
|
||||
|
||||
|
||||
class OrderPay(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin, TemplateView):
|
||||
template_name = "pretixpresale/event/order_pay.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.request = request
|
||||
if not self.order:
|
||||
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
|
||||
if not self.payment_provider.order_can_retry(self.order) or not self.payment_provider.is_enabled:
|
||||
messages.error(request, _('The payment for this order cannot be continued.'))
|
||||
return redirect(self.get_order_url())
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
resp = self.payment_provider.retry_prepare(
|
||||
request, self.order
|
||||
)
|
||||
if isinstance(resp, str):
|
||||
return redirect(resp)
|
||||
elif resp is True:
|
||||
return redirect(self.get_confirm_url())
|
||||
else:
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['order'] = self.order
|
||||
ctx['form'] = self.form
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
def form(self):
|
||||
return self.payment_provider.payment_form_render(self.request)
|
||||
|
||||
def get_confirm_url(self):
|
||||
return reverse('presale:event.order.pay.confirm', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'order': self.order.code,
|
||||
})
|
||||
|
||||
|
||||
class OrderPayDo(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin, TemplateView):
|
||||
template_name = "pretixpresale/event/order_pay_confirm.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.request = request
|
||||
if not self.order:
|
||||
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
|
||||
if not self.payment_provider.order_can_retry(self.order) or not self.payment_provider.is_enabled:
|
||||
messages.error(request, _('The payment for this order cannot be continued.'))
|
||||
return redirect(self.get_order_url())
|
||||
if not self.payment_provider.payment_is_valid_session(request) or \
|
||||
not self.payment_provider.is_enabled or \
|
||||
not self.payment_provider.is_allowed(request):
|
||||
messages.error(request, _('The payment information you entered was incomplete.'))
|
||||
return redirect(self.get_payment_url())
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
resp = self.payment_provider.payment_perform(request, self.order)
|
||||
return redirect(resp or self.get_order_url())
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['order'] = self.order
|
||||
ctx['payment'] = self.payment_provider.checkout_confirm_render(self.request)
|
||||
ctx['payment_provider'] = self.payment_provider
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
def form(self):
|
||||
return self.payment_provider.payment_form_render(self.request)
|
||||
|
||||
def get_payment_url(self):
|
||||
return reverse('presale:event.order.pay', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'order': self.order.code,
|
||||
})
|
||||
|
||||
|
||||
class OrderModify(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
|
||||
QuestionsViewMixin, TemplateView):
|
||||
template_name = "pretixpresale/event/order_modify.html"
|
||||
@@ -96,12 +187,6 @@ class OrderModify(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
|
||||
))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.request = request
|
||||
self.kwargs = kwargs
|
||||
if not self.order:
|
||||
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
|
||||
if not self.order.can_modify_answers:
|
||||
return HttpResponseForbidden(_('You cannot modify this order'))
|
||||
failed = not self.save()
|
||||
if failed:
|
||||
messages.error(self.request,
|
||||
@@ -113,13 +198,16 @@ class OrderModify(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
|
||||
order=self.order.code)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.request = request
|
||||
self.kwargs = kwargs
|
||||
if not self.order:
|
||||
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
|
||||
if not self.order.can_modify_answers:
|
||||
return HttpResponseForbidden(_('You cannot modify this order'))
|
||||
return super().get(request, *args, **kwargs)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
|
||||
Reference in New Issue
Block a user