]> git.parisson.com Git - teleforma.git/commitdiff
Switching to new payment system
authorGael Le Mignot <gael@pilotsystems.net>
Thu, 16 Jun 2022 11:42:56 +0000 (13:42 +0200)
committerGael Le Mignot <gael@pilotsystems.net>
Thu, 16 Jun 2022 11:42:56 +0000 (13:42 +0200)
app/settings.py
teleforma/templates/payment/payment_start.html
teleforma/views/core.py
teleforma/views/payment.py

index 2e741da355538b4962b0d08bcf533a0831406782..98877f50c8df1c6cad0c30f2653791bd89ab08c6 100644 (file)
@@ -279,6 +279,9 @@ SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
 #SESSION_ENGINE = "unique_session.backends.session_backend"
 UNIQUE_SESSION_WHITELIST = (1, 2042)
 SESSION_EXPIRE_AT_BROWSER_CLOSE = False
+SESSION_COOKIE_SAMESITE="None"
+SESSION_COOKIE_SECURE=True
+
 
 RECAPTCHA_PUBLIC_KEY = '6Ldq5DgbAAAAADkKg19JXlhx6F1XUQDsrXfXqSP6'
 RECAPTCHA_PRIVATE_KEY = '6Ldq5DgbAAAAAOVDOeF2kH8i2e2VSNHpqlinbpAJ'
@@ -477,16 +480,27 @@ JAZZMIN_UI_TWEAKS = {
     },
     "actions_sticky_top": True
 }
-# Sherlock's online payment
-PAYMENT_SHERLOCKS_PATH='/srv/sherlocks'
-
-PAYMENT_PARAMETERS = { 'merchant_id' : { 'Semestrielle': "040109417200053",
-                                  'Annuelle': "040109417200053",
-                                  'Estivale': "040109417200054", },                                         
-                       'merchant_country': 'fr',
-                       'currency_code': '978',
-                       'language': 'fr'
-}
+
+SHERLOKS_URL = "https://sherlocks-payment-webinit-simu.secure.lcl.fr/paymentInit"
+
+PAYMENT_PARAMETERS = { 'Semestrielle': { 'merchantId' : "002001000000003",
+                                         '_key': "002001000000003_KEY1",
+                                         'keyVersion': '1',
+                                         'currencyCode': '978',
+                                        },
+                       'Annuelle': { 'merchantId' : "002001000000003",
+                                     '_key': "002001000000003_KEY1",
+                                     'keyVersion': '1',
+                                     'currencyCode': '978',
+                                    },
+                       'Estivale': { 'merchantId' : "002001000000003",
+                                     '_key': "002001000000003_KEY1",
+                                     'keyVersion': '1',
+                                     'currencyCode': '978',
+                                    },
+                      }
+SHERLOKS_USE_TRANSACTION_ID = True
+
 
 
 ORAL_OPTION_PRICE = 250
index a3dfd1b4516b9e5246bf2a66c2e5e3cce6269e70..d87bb8baa9950e0d74e6ad73665ddf41b104e013 100644 (file)
@@ -8,8 +8,22 @@
 
 {% payment_summary payment %}
 
-    {% autoescape off %}
-    {{ sherlock_info }}
-    {% endautoescape %}
+<div class="container">
+  <div align="center">
+    Vous utilisez le formulaire sécurisé standard SSL, choisissez une carte ci-dessous <img src="/static/payment/logo/CLEF.gif" border="0" /> :
+  </div>
+  <form method="post" action="{{ sherlock_url }}">
+    <input type="hidden" name="Data" value="{{ sherlock_data }}" />
+    <input type="hidden" name="InterfaceVersion" value="HP_2.41" />
+    <input type="hidden" name="Seal" value="{{ sherlock_seal }}" />
+    <div align="center">
+      <input type="image" name="CB" src="/static/payment/logo/CB.gif" border="0" />
+      &nbsp;
+      <input type="image" name="VISA" src="/static/payment/logo/VISA.gif" border="0" />
+      &nbsp;
+      <input type="image" name="MASTERCARD" src="/static/payment/logo/MASTERCARD.gif" border="0" />
+      <br /
+    </div>
+  </form>
 
 {% endblock %}
index 65421a0d75ba7914fb1ad7a491037b0ba24fc0ab..f84c84924c62b0a1a779343a870799855d949124 100644 (file)
@@ -300,6 +300,7 @@ class PeriodAccessMixin(View):
 
     def render_to_response(self, context):
         period = context['period']
+        print(period, context['periods'])
         if not period in context['periods']:
             messages.warning(self.request, _(
                 "You do NOT have access to this resource and then have been redirected to your desk."))
index 081c93913733c0eaead9c0114e9320c5721c68b4..8afcb85b34cec12ad4137b0c5ea309d480b25bf9 100644 (file)
@@ -4,7 +4,7 @@ import datetime
 import logging
 import os
 import pprint
-import subprocess
+import hashlib
 
 from django.conf import settings
 from django.contrib.auth.decorators import login_required
@@ -26,65 +26,92 @@ from ..models.crfpa import Payment
 log = logging.getLogger('payment')
 
 
-def call_scherlocks(what, data, merchant_id):
+def compute_sherlocks_seal(data, key):
     """
-    Perform a Scherlock's call, with parameters in data
-    what is either 'request' or 'response', the program to call
+    Compute the seal for Sherlock's
     """
-    log.info('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
+    data = data.encode('utf-8')
+    seal = hashlib.sha256(data + key.encode('utf-8')).hexdigest()
+    return seal
 
-    status, out = subprocess.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):
+def encode_sherlocks_data(params, key):
+    """
+    Encode data for Sherlock's
+    """
+    data = []
+    for k, v in params.items():
+        v = v.replace('|', '_').replace(',', '_').replace('=', '_')
+        data.append('%s=%s' % (k, v))
+    data = '|'.join(data)
+    seal = compute_sherlocks_seal(data, key)
+    return data, seal
+    
+def check_payment_info(post_data):
     """
     Check that the payment info are valid
     """
-    response_code = data[8]
-    cvv_response_code = data[14]
+    seal = post_data.get('Seal', None)
+    data = post_data.get('Data', None)
+    keys = [ p['_key'] for p in settings.PAYMENT_PARAMETERS.values() ]
+
+    if not seal or not data:
+        log.warning('Missing seal or data')
+        raise SuspiciousOperation('Missing data')
+
+    for key in keys:
+        wanted_seal = compute_sherlocks_seal(data, key)
+        if seal == wanted_seal:
+            break
+    else:
+        log.warning('Invalid seal')
+        raise SuspiciousOperation('Invalid seal')
+
+    words = data.split('|')
+    values = {}
+    for word in words:
+        if "=" in word:
+            key, value = word.split('=', 1)
+            values[key] = value
 
-    log.info('check_payment_info %s %s' % (response_code, cvv_response_code))
+    order_id = values.get('orderId', None)
+    code = values.get('responseCode', None)
+    if not order_id or not code:
+        log.warning('Missing order_id or code')
+        raise SuspiciousOperation('Missing value in data')
+    
+    res = { 'order_id': order_id,
+            'valid': code == '00' }
 
-    return response_code == '00'
+    log.debug('check_payment_info %s %s' % (order_id, code))
+
+    return res
 
 
 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 = dict(settings.PAYMENT_PARAMETERS[period_short_name])
+    key = params.pop('_key')    
+    merchant_id = params['merchantId']
     params['amount'] = str(int(payment.value*100))
-    params['order_id'] = str(payment.pk)
+    params['orderId'] = str(payment.pk)
+    if settings.SHERLOKS_USE_TRANSACTION_ID:
+        params['s10TransactionReference.s10TransactionId'] = '%06d' % payment.pk
+    else:
+        params['transactionReference'] = 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)
-    pprint.pprint(params)
-    res = call_scherlocks('request', params, merchant_id=merchant_id)
-    return res[0]
+    params['normalReturnUrl'] = root + reverse('teleforma-bank-success',
+                                               kwargs=kwargs)
+    params['automaticResponseURL'] = root + reverse('teleforma-bank-auto',
+                                                    kwargs=kwargs)
+    data, seal = encode_sherlocks_data(params, key)
+    return data, settings.SHERLOKS_URL, seal
 
 
 class PaymentStartView(DetailView):
@@ -100,7 +127,10 @@ 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)
+        data, url, seal = process_payment(self.request, payment)
+        context['sherlock_url'] = url
+        context['sherlock_data'] = data
+        context['sherlock_seal'] = seal
         return context
 
     @method_decorator(login_required)
@@ -113,11 +143,11 @@ 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]
+    log.info("bank_auto %r" % request.POST)
+    info = check_payment_info(request.POST)
+    order_id = info['order_id']
     payment = Payment.objects.get(pk=order_id)
-    if check_payment_info(res) and payment.type == 'online' and not payment.online_paid:
+    if info['valid'] and payment.type == 'online' and not payment.online_paid:
         payment.online_paid = True
         payment.date_paid = datetime.datetime.now()
         if payment.student.restricted:
@@ -167,11 +197,10 @@ 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]
+    log.info("bank_auto %r" % request.POST)
+    info = check_payment_info(request.POST)
+    order_id = info['order_id']
+    if info['valid']:
         payment = Payment.objects.get(pk=order_id)
         if payment.type == 'online' and payment.online_paid:
             return render(request, 'payment/payment_validate.html',