]> git.parisson.com Git - teleforma.git/commitdiff
Initial working version of online payment
authorGael Le Mignot <gael@pilotsystems.net>
Mon, 18 Nov 2019 15:13:28 +0000 (16:13 +0100)
committerGael Le Mignot <gael@pilotsystems.net>
Mon, 18 Nov 2019 15:13:28 +0000 (16:13 +0100)
teleforma/forms.py
teleforma/models/core.py
teleforma/models/crfpa.py
teleforma/templates/payment/payment_fail.html [new file with mode: 0644]
teleforma/templates/payment/payment_start.html
teleforma/templates/payment/payment_summary.html [new file with mode: 0644]
teleforma/templates/payment/payment_validate.html
teleforma/templates/registration/registration_form.html
teleforma/templatetags/payment.py [new file with mode: 0644]
teleforma/urls.py
teleforma/views/payment.py

index 8a6203c2ae7acb037017cad6111874899e6d7358..4130f9a466991d3b6c101c0e10d9ecbbee669c07 100644 (file)
@@ -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)
     
index b863b465e0e0bdbaf29d342774bb85c70db87f0c..c9d63e2e64f2f4b074a98add8e21ca756dd5f86d 100755 (executable)
@@ -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"),
 ]
 
index eb79b72b5f5ce0ed79a7f75f60880b2ae06e09e0..f107ba0b7a7082b8b1db015e8630033d79e55f0c 100755 (executable)
@@ -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 (file)
index 0000000..df2a470
--- /dev/null
@@ -0,0 +1,7 @@
+{% extends "registration/registration_base.html" %}
+{% block title %}Paiement en ligne{% endblock %}
+{% block content %}
+<p>
+  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.
+</p>
+{% endblock %}
index 4859c91d61e1c76c79829bb974d377aaa3dc1d13..a3dfd1b4516b9e5246bf2a66c2e5e3cce6269e70 100644 (file)
@@ -1,12 +1,15 @@
 {% extends "registration/registration_base.html" %}
+{% load payment %}
 {% block title %}Paiement en ligne{% endblock %}
 {% block content %}
 <p>
   Vous êtes sur le point de payer une échéance de {{ payment.value }} €.
 </p>
 
-<form method="get" action="{% url teleforma-payment-validate payment.pk %}">
-  {% csrf_token %}
-  <input type="submit" value="Payer" />
-</form>
+{% 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 (file)
index 0000000..c5caed1
--- /dev/null
@@ -0,0 +1,24 @@
+<div id="payment_summary">
+  <h2>Votre échéancier de paiement</h2>
+  
+  <table>
+    <thead>
+      <tr>
+        <th>Échéance</th>
+        <th>Montant</th>
+        <th>Statut</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for payment in payments %}
+      <tr>
+        <td>{{ payment.scheduled }}</td>
+        <td>{{ payment.value }}</td>
+        <td class="{{ payment.sclass }}">{{ payment.status }}</td>
+      </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+</div>
+
+
index 5d326920774c26cc545d9f4ce01d8fdb102ca6bf..5ba9fc94574042c490f341ca7a205ece538cfabd 100644 (file)
@@ -1,7 +1,11 @@
 {% extends "registration/registration_base.html" %}
+{% load payment %}
 {% block title %}Paiement en ligne{% endblock %}
 {% block content %}
 <p>
   Vous venez de payer avec succès une échéance de {{ payment.value }} €.
 </p>
+
+{% payment_summary payment %}
+
 {% endblock %}
index f1231619e3417cacd96c4afcaacf4b8cccef8bf0..7c0ef465ef7ab775565ce6d650df5df5d8099c01 100644 (file)
     <span class="error">* Champs obligatoires</span>
 
     <div class="submit-panel">
+        <div> 
+            <span class="error">
+                {{ form.payment_schedule.errors }}
+            </span>
+            <label for="id_payment_schedule">{{ form.payment_schedule.label }}</label>
+            {{ form.payment_schedule }}
+        </div>
         <div>
             <span class="error">
                 {{ form.accept.errors }}
         width: 222px;
     }
 </style>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/teleforma/templatetags/payment.py b/teleforma/templatetags/payment.py
new file mode 100644 (file)
index 0000000..93852a6
--- /dev/null
@@ -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 }
index 15b19dc77c2299943a127ab0e97451b7d2a4e793..2cdcdcd09741c32a8bf7f62ccb7d17be8cd6e4bb 100644 (file)
@@ -153,10 +153,15 @@ urlpatterns = patterns('',
     # Payment
     url(r'^payment/(?P<pk>.*)/start/$', PaymentStartView.as_view(),
         name="teleforma-payment-start"),
-    url(r'^payment/(?P<pk>.*)/validate/$', PaymentValidateView.as_view(),
-        name="teleforma-payment-validate"),
-                       
 
+    url(r'^payment/bank_auto/(?P<merchant_id>.*)',
+        bank_auto, name='teleforma-bank-auto'),
+    url(r'^payment/bank_success/(?P<merchant_id>.*)',
+        bank_success, name='teleforma-bank-success'),
+    url(r'^payment/bank_cancel/(?P<merchant_id>.*)',
+        bank_cancel, name='teleforma-bank-cancel'),
 
+    url(r'^echec-de-paiement',
+        bank_fail, name='teleforma-bank-fail'),
 
 )
index 700b3075b43a7212ed3930751cc439fd681f244a..38e86f4501d95dd265590d4c514097eb8c258a33 100644 (file)
@@ -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')