From: Gael Le Mignot Date: Mon, 18 Nov 2019 15:13:28 +0000 (+0100) Subject: Initial working version of online payment X-Git-Tag: 1.4.1~5^2~12^2~7 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=8bf2fa8d344ad7d67faefc0ebc973bcdfc2074ea;p=teleforma.git Initial working version of online payment --- diff --git a/teleforma/forms.py b/teleforma/forms.py index 8a6203c2..4130f9a4 100644 --- a/teleforma/forms.py +++ b/teleforma/forms.py @@ -96,7 +96,8 @@ class UserForm(ModelForm): help_text="Matière d’oral de langue (en option)", queryset=Course.objects.filter(oral_1=True)) promo_code = CharField(label=_('Code promo'), max_length=100, required=False) - payment_schedule = ChoiceField(label=_(u'échéancier de paiement'), + + payment_schedule = ChoiceField(label=_(u'Échéancier de paiement'), choices=payment_schedule_choices, required=True) diff --git a/teleforma/models/core.py b/teleforma/models/core.py index b863b465..c9d63e2e 100755 --- a/teleforma/models/core.py +++ b/teleforma/models/core.py @@ -75,10 +75,10 @@ streaming_choices = [('mp3', 'mp3'), ('ogg', 'ogg'), ('webm', 'webm'), ('mp4', ' mimetypes.add_type('video/webm','.webm') payment_choices = [ ('online', u'en ligne'), - ('check', _('check')), - ('tranfer', _('transfer')), - ('credit card', _('credit card')), - ('money', _('money')), + ('check', u'par chèque'), + ('tranfer', u'par virement'), + ('credit card', u'par carte'), + ('money', u'en liquide'), ('other', u"autre"), ] diff --git a/teleforma/models/crfpa.py b/teleforma/models/crfpa.py index eb79b72b..f107ba0b 100755 --- a/teleforma/models/crfpa.py +++ b/teleforma/models/crfpa.py @@ -307,7 +307,7 @@ class Payment(models.Model): db_table = app_label + '_' + 'payments' verbose_name = _("Payment") verbose_name_plural = _("Payments") - ordering = ['month'] + ordering = ['scheduled', 'month'] class Discount(models.Model): diff --git a/teleforma/templates/payment/payment_fail.html b/teleforma/templates/payment/payment_fail.html new file mode 100644 index 00000000..df2a4706 --- /dev/null +++ b/teleforma/templates/payment/payment_fail.html @@ -0,0 +1,7 @@ +{% extends "registration/registration_base.html" %} +{% block title %}Paiement en ligne{% endblock %} +{% block content %} +

+ Votre transaction a été annulée. Il peut s'agir d'une erreur technique, ou d'informations incorrectes entrées lors du paiement. Veuillez nous contacter. +

+{% endblock %} diff --git a/teleforma/templates/payment/payment_start.html b/teleforma/templates/payment/payment_start.html index 4859c91d..a3dfd1b4 100644 --- a/teleforma/templates/payment/payment_start.html +++ b/teleforma/templates/payment/payment_start.html @@ -1,12 +1,15 @@ {% extends "registration/registration_base.html" %} +{% load payment %} {% block title %}Paiement en ligne{% endblock %} {% block content %}

Vous êtes sur le point de payer une échéance de {{ payment.value }} €.

-
- {% csrf_token %} - -
+{% payment_summary payment %} + + {% autoescape off %} + {{ sherlock_info }} + {% endautoescape %} + {% endblock %} diff --git a/teleforma/templates/payment/payment_summary.html b/teleforma/templates/payment/payment_summary.html new file mode 100644 index 00000000..c5caed12 --- /dev/null +++ b/teleforma/templates/payment/payment_summary.html @@ -0,0 +1,24 @@ +
+

Votre échéancier de paiement

+ + + + + + + + + + + {% for payment in payments %} + + + + + + {% endfor %} + +
ÉchéanceMontantStatut
{{ payment.scheduled }}{{ payment.value }}{{ payment.status }}
+
+ + diff --git a/teleforma/templates/payment/payment_validate.html b/teleforma/templates/payment/payment_validate.html index 5d326920..5ba9fc94 100644 --- a/teleforma/templates/payment/payment_validate.html +++ b/teleforma/templates/payment/payment_validate.html @@ -1,7 +1,11 @@ {% extends "registration/registration_base.html" %} +{% load payment %} {% block title %}Paiement en ligne{% endblock %} {% block content %}

Vous venez de payer avec succès une échéance de {{ payment.value }} €.

+ +{% payment_summary payment %} + {% endblock %} diff --git a/teleforma/templates/registration/registration_form.html b/teleforma/templates/registration/registration_form.html index f1231619..7c0ef465 100644 --- a/teleforma/templates/registration/registration_form.html +++ b/teleforma/templates/registration/registration_form.html @@ -89,6 +89,13 @@ * Champs obligatoires
+
+ + {{ form.payment_schedule.errors }} + + + {{ form.payment_schedule }} +
{{ form.accept.errors }} @@ -154,4 +161,4 @@ width: 222px; } -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/teleforma/templatetags/payment.py b/teleforma/templatetags/payment.py new file mode 100644 index 00000000..93852a68 --- /dev/null +++ b/teleforma/templatetags/payment.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +from django import template +from datetime import date +from teleforma.models import Payment +register = template.Library() + +@register.inclusion_tag('payment/payment_summary.html', + takes_context=True) +def payment_summary(context, payment): + objs = Payment.objects.filter(student = payment.student) + payments = [] + today = date.today() + for obj in objs: + if obj.type == 'online': + if obj.id == payment.id: + status = 'en cours' + sclass = "pending" + elif obj.online_paid: + status = 'payé' + sclass = "paid" + elif obj.scheduled >= today: + status = 'à payer ultérieurement' + sclass = "topay_later" + else: + status = 'à payer' + sclass = "topay" + else: + status = obj.get_type_display() + sclass = "offline" + payments.append({ 'scheduled': obj.scheduled or obj.date_created.date(), + 'value': obj.value, + 'status': status }) + payments.sort(key = lambda p: p['scheduled']) + + return { "payments": payments } diff --git a/teleforma/urls.py b/teleforma/urls.py index 15b19dc7..2cdcdcd0 100644 --- a/teleforma/urls.py +++ b/teleforma/urls.py @@ -153,10 +153,15 @@ urlpatterns = patterns('', # Payment url(r'^payment/(?P.*)/start/$', PaymentStartView.as_view(), name="teleforma-payment-start"), - url(r'^payment/(?P.*)/validate/$', PaymentValidateView.as_view(), - name="teleforma-payment-validate"), - + url(r'^payment/bank_auto/(?P.*)', + bank_auto, name='teleforma-bank-auto'), + url(r'^payment/bank_success/(?P.*)', + bank_success, name='teleforma-bank-success'), + url(r'^payment/bank_cancel/(?P.*)', + bank_cancel, name='teleforma-bank-cancel'), + url(r'^echec-de-paiement', + bank_fail, name='teleforma-bank-fail'), ) diff --git a/teleforma/views/payment.py b/teleforma/views/payment.py index 700b3075..38e86f45 100644 --- a/teleforma/views/payment.py +++ b/teleforma/views/payment.py @@ -8,6 +8,72 @@ from django.views.decorators.csrf import csrf_exempt from django.db.models import Q from django.http import HttpResponseForbidden from django.core.exceptions import ValidationError, PermissionDenied +from django.conf import settings +from django.contrib.sites.models import get_current_site + +import commands + +import logging +log = logging.getLogger('payment') + +def call_scherlocks(what, data, merchant_id): + """ + Perform a Scherlock's call, with parameters in data + what is either 'request' or 'response', the program to call + """ + log.debug('call_scherlocks %r %r' % (what, data)) + requestbin = os.path.join(settings.PAYMENT_SHERLOCKS_PATH, 'bin/static', what) + params = dict(data) + params['pathfile'] = os.path.join(settings.PAYMENT_SHERLOCKS_PATH, + 'param/pathfile.' + merchant_id) + params = ' '.join([ '%s=%s' % (k,v) for k,v in params.items() ]) + cmdline = requestbin + ' ' + params + + print cmdline + + status, out = commands.getstatusoutput(cmdline) + if status: + raise OSError, "error calling %s" % cmdline + res = out.split('!')[1:-1] + if int(res[0]): + raise ValueError, "Scherlock's returned %s" % res[1] + return res[2:] + +def check_payment_info(data): + """ + Check that the payment info are valid + """ + response_code = data[8]; + cvv_response_code = data[14]; + + log.debug('check_payment_info %s %s' % (response_code, cvv_response_code)) + + return response_code == '00' + +def process_payment(request, payment): + """ + Process a payment to Sherlocks + """ + params = dict(settings.PAYMENT_PARAMETERS) + period = payment.student.period + period_short_name = period.name.split()[0] + params['merchant_id'] = params['merchant_id'][period_short_name] + merchant_id = params['merchant_id'] + params['amount'] = str(int(payment.value*100)) + params['order_id'] = str(payment.pk) + current_site = get_current_site(request) + root = 'https://%s' % (current_site.domain) + + kwargs = { 'merchant_id': merchant_id } + params['normal_return_url'] = root + reverse('teleforma-bank-success', + kwargs = kwargs) + params['cancel_return_url'] = root + reverse('teleforma-bank-cancel', + kwargs = kwargs) + params['automatic_response_url'] = root + reverse('teleforma-bank-auto', + kwargs = kwargs) + res = call_scherlocks('request', params, merchant_id = merchant_id) + return res[0] + class PaymentStartView(DetailView): @@ -22,31 +88,55 @@ class PaymentStartView(DetailView): if payment.student.user_id != self.request.user.pk and not self.request.user.is_superuser: raise PermissionDenied context['payment'] = payment + context['sherlock_info'] = process_payment(self.request, payment) return context @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(PaymentStartView, self).dispatch(*args, **kwargs) -class PaymentValidateView(DetailView): - - template_name = 'payment/payment_validate.html' - model = Payment - - def get_context_data(self, **kwargs): - context = super(PaymentValidateView, self).get_context_data(**kwargs) - payment = self.get_object() - if payment.type != 'online' or payment.online_paid: - raise PermissionDenied - if payment.student.user_id != self.request.user.pk and not self.request.user.is_superuser: - raise PermissionDenied - +@csrf_exempt +def bank_auto(request, merchant_id): + """ + Bank automatic callback + """ + res = call_scherlocks('response', { 'message': request.POST['DATA'] }, + merchant_id = merchant_id) + order_id = res[24]; + payment = Payment.objects.get(pk = order_id) + if check_payment_info(res) and payment.type == 'online' and not payment.online_paid: payment.online_paid = True payment.save() - context['payment'] = payment - return context + return HttpResponse('OK - Validated') + else: + return HttpResponse('OK - Cancelled') - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super(PaymentValidateView, self).dispatch(*args, **kwargs) +@csrf_exempt +def bank_success(request, merchant_id): + """ + Bank success callback + """ + log.info("bank_success %r" % request.POST) + res = call_scherlocks('response', { 'message': request.POST['DATA'] }, + merchant_id = merchant_id) + if check_payment_info(res): + order_id = res[24]; + payment = Payment.objects.get(pk = order_id) + if payment.type == 'online' and payment.online_paid and (payment.student.user_id == request.user.pk or request.user.is_superuser): + return render_to_response('payment/payment_validate.html', + {'payment': payment, }, + context_instance=RequestContext(request)) + return HttpResponseRedirect('/echec-de-paiement') + +@csrf_exempt +def bank_cancel(request, merchant_id): + """ + Bank cancel operation callback + """ + return HttpResponseRedirect('/echec-de-paiement') +def bank_fail(request): + """ + Display message when a payment failed + """ + return render_to_response('payment/payment_fail.html')