Fix #177 - Allow to change the payment method

This commit is contained in:
Raphael Michel
2016-08-31 13:20:00 +02:00
parent b21ed4d99f
commit 022e02d913
8 changed files with 209 additions and 11 deletions

View File

@@ -33,6 +33,12 @@
{% if order.status == "n" %}
<div class="panel panel-danger">
<div class="panel-heading">
<div class="pull-right">
<a href="{% eventurl event "presale:event.order.pay.change" secret=order.secret order=order.code %}">
<span class="fa fa-edit"></span>
{% trans "Use different payment method" %}
</a>
</div>
<h3 class="panel-title">
{% trans "Payment" %}
</h3>

View File

@@ -0,0 +1,65 @@
{% extends "pretixpresale/event/base.html" %}
{% load i18n %}
{% load eventurl %}
{% block title %}{% trans "Change payment method" %}{% endblock %}
{% block content %}
<h2>
{% blocktrans trimmed with code=order.code %}
Change payment method: {{ code }}
{% endblocktrans %}
</h2>
<p>
{% blocktrans trimmed %}
Please note: If you change your payment method, your order total will change by the
amount displayed to the right of each method.
{% endblocktrans %}
</p>
<form method="post">
{% csrf_token %}
<div class="panel-group" id="payment_accordion">
{% for p in providers %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<label class="radio">
<strong class="pull-right">{% if p.fee_diff >= 0 %}+{% else %}-{% endif %} {{ p.fee_diff_abs|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 collapsed {% if selected == p.provider.identifier %}in{% endif %}">
<div class="panel-body form-horizontal">
{{ p.form }}
</div>
</div>
</div>
{% empty %}
<div class="alert alert-info">
{% trans "There are no alternative payment providers available for this order." %}
</div>
{% endfor %}
</div>
<div class="row checkout-button-row">
<div class="col-md-4">
<a class="btn btn-block btn-default btn-lg"
href="{% eventurl request.event "presale:event.order" secret=order.secret order=order.code %}">
{% trans "Cancel" %}
</a>
</div>
<div class="col-md-4 col-md-offset-4">
{% if providers %}
<button class="btn btn-block btn-primary btn-lg" type="submit">
{% trans "Continue" %}
</button>
{% endif %}
</div>
<div class="clearfix"></div>
</div>
</form>
{% endblock %}

View File

@@ -40,6 +40,9 @@ event_patterns = [
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay/complete$',
pretix.presale.views.order.OrderPayComplete.as_view(),
name='event.order.pay.complete'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay/change',
pretix.presale.views.order.OrderPayChangeMethod.as_view(),
name='event.order.pay.change'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/download/(?P<output>[^/]+)$',
pretix.presale.views.order.OrderDownload.as_view(),
name='event.order.download'),

View File

@@ -2,6 +2,8 @@ from datetime import timedelta
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db import transaction
from django.db.models import Sum
from django.http import Http404
from django.shortcuts import redirect
from django.utils.functional import cached_property
@@ -14,7 +16,7 @@ from pretix.base.models import (
)
from pretix.base.models.orders import InvoiceAddress
from pretix.base.services.invoices import (
generate_invoice, invoice_pdf, invoice_qualified,
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
)
from pretix.base.services.orders import OrderError, cancel_order
from pretix.base.services.tickets import generate
@@ -125,9 +127,9 @@ class OrderPay(EventViewMixin, OrderDetailMixin, TemplateView):
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
resp = self.payment_provider.retry_prepare(
request, self.order
)
resp = self.payment_provider.order_prepare(request, self.order)
if 'payment_change_{}'.format(self.order.pk) in request.session:
del request.session['payment_change_{}'.format(self.order.pk)]
if isinstance(resp, str):
return redirect(resp)
elif resp is True:
@@ -181,7 +183,8 @@ class OrderPayDo(EventViewMixin, OrderDetailMixin, TemplateView):
self.request = request
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
if not self.payment_provider.order_can_retry(self.order) or not self.payment_provider.is_enabled:
can_do = self.payment_provider.order_can_retry(self.order) or 'payment_change_{}'.format(self.order.pk) in request.session
if not can_do 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)
@@ -192,6 +195,8 @@ class OrderPayDo(EventViewMixin, OrderDetailMixin, TemplateView):
def post(self, request, *args, **kwargs):
resp = self.payment_provider.payment_perform(request, self.order)
if 'payment_change_{}'.format(self.order.pk) in request.session:
del request.session['payment_change_{}'.format(self.order.pk)]
return redirect(resp or self.get_order_url())
def get_context_data(self, **kwargs):
@@ -213,8 +218,8 @@ class OrderPayComplete(EventViewMixin, OrderDetailMixin, View):
self.request = request
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
if (not self.payment_provider.payment_is_valid_session(request)
or not self.payment_provider.is_enabled):
if (not self.payment_provider.payment_is_valid_session(request) or
not self.payment_provider.is_enabled):
messages.error(request, _('The payment information you entered was incomplete.'))
return redirect(self.get_payment_url())
return super().dispatch(request, *args, **kwargs)
@@ -230,6 +235,96 @@ class OrderPayComplete(EventViewMixin, OrderDetailMixin, View):
})
class OrderPayChangeMethod(EventViewMixin, OrderDetailMixin, TemplateView):
template_name = 'pretixpresale/event/order_pay_change.html'
def dispatch(self, request, *args, **kwargs):
self.request = request
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
if self.order.status not in (Order.STATUS_PENDING, Order.STATUS_EXPIRED):
messages.error(request, _('The payment method for this order cannot be changed.'))
return redirect(self.get_order_url() + '?paid=yes')
return super().dispatch(request, *args, **kwargs)
def get_payment_url(self):
return eventreverse(self.request.event, 'presale:event.order.pay', kwargs={
'order': self.order.code,
'secret': self.order.secret
})
@cached_property
def _total_order_value(self):
return self.order.positions.aggregate(sum=Sum('price'))['sum']
@cached_property
def provider_forms(self):
providers = []
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:
continue
if not provider.is_enabled or not provider.order_change_allowed(self.order):
continue
fee = provider.calculate_fee(self._total_order_value)
providers.append({
'provider': provider,
'fee': fee,
'fee_diff': fee - self.order.payment_fee,
'fee_diff_abs': abs(fee - self.order.payment_fee),
'form': provider.payment_form_render(self.request)
})
return providers
def post(self, request, *args, **kwargs):
self.request = request
for p in self.provider_forms:
if p['provider'].identifier == request.POST.get('payment', ''):
request.session['payment'] = p['provider'].identifier
request.session['payment_change_{}'.format(self.order.pk)] = '1'
resp = p['provider'].order_prepare(request, self.order)
if resp:
with transaction.atomic():
new_fee = p['provider'].calculate_fee(self._total_order_value)
self.order.log_action('pretix.event.order.payment.changed', {
'old_fee': self.order.payment_fee,
'new_fee': new_fee,
'old_provider': self.order.payment_provider,
'new_provider': p['provider'].identifier
})
self.order.payment_provider = p['provider'].identifier
self.order.payment_fee = new_fee
self.order._calculate_tax()
self.order.save()
i = self.order.invoices.filter(is_cancellation=False).last()
if i:
generate_cancellation(i)
generate_invoice(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)
messages.error(self.request, _("Please select a payment method."))
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['order'] = self.order
ctx['providers'] = self.provider_forms
return ctx
def get_confirm_url(self):
return eventreverse(self.request.event, 'presale:event.order.pay.confirm', kwargs={
'order': self.order.code,
'secret': self.order.secret
})
class OrderModify(EventViewMixin, OrderDetailMixin, QuestionsViewMixin, TemplateView):
template_name = "pretixpresale/event/order_modify.html"