diff --git a/cotisations/forms.py b/cotisations/forms.py index bbe50b69..303ee32e 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -279,3 +279,11 @@ class NewFactureSoldeForm(NewFactureForm): raise forms.ValidationError("Le numéro de chèque et\ la banque sont obligatoires.") return cleaned_data + + +class RechargeForm(Form): + value = forms.FloatField( + label='Valeur', + min_value=0.01, + validators = [] + ) diff --git a/cotisations/models.py b/cotisations/models.py index a775237c..07c3b8fa 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -289,7 +289,7 @@ class Vente(models.Model): if not user_request.has_perm('cotisations.change_vente'): return False, u"Vous n'avez pas le droit d'éditer les ventes" elif not user_request.has_perm('cotisations.change_all_facture') and not self.facture.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer les factures de cet user protégé" + return False, u"Vous ne pouvez pas éditer les factures de cet user protégé" elif not user_request.has_perm('cotisations.change_all_vente') and\ (self.facture.control or not self.facture.valid): return False, u"Vous n'avez pas le droit d'éditer une vente\ diff --git a/cotisations/payment.py b/cotisations/payment.py new file mode 100644 index 00000000..ab7260b2 --- /dev/null +++ b/cotisations/payment.py @@ -0,0 +1,101 @@ +"""Payment + +Here are defined some views dedicated to online payement. +""" +from django.urls import reverse +from django.shortcuts import redirect, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from django.views.decorators.csrf import csrf_exempt +from django.utils.datastructures import MultiValueDictKeyError +from django.http import HttpResponse, HttpResponseBadRequest + +from collections import OrderedDict +from .models import Facture +from .payment_utils.comnpay import Payment as ComnpayPayment + +@csrf_exempt +@login_required +def accept_payment(request, factureid): + facture = get_object_or_404(Facture, id=factureid) + messages.success( + request, + "Le paiement de {} € a été accepté.".format(facture.prix()) + ) + return redirect(reverse('users:profil', kwargs={'userid':request.user.id})) + + +@csrf_exempt +@login_required +def refuse_payment(request): + messages.error( + request, + "Le paiement a été refusé." + ) + return redirect(reverse('users:profil', kwargs={'userid':request.user.id})) + +@csrf_exempt +def ipn(request): + p = ComnpayPayment() + order = ('idTpe', 'idTransaction', 'montant', 'result', 'sec', ) + try: + data = OrderedDict([(f, request.POST[f]) for f in order]) + except MultiValueDictKeyError: + return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") + + if not p.validSec(data, "DEMO"): + return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") + + result = True if (request.POST['result'] == 'OK') else False + idTpe = request.POST['idTpe'] + idTransaction = request.POST['idTransaction'] + + # On vérifie que le paiement nous est destiné + if not idTpe == "DEMO": + return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") + + try: + factureid = int(idTransaction) + except ValueError: + return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") + + facture = get_object_or_404(Facture, id=factureid) + + # On vérifie que le paiement est valide + if not result: + # Le paiement a échoué : on effectue les actions nécessaires (On indique qu'elle a échoué) + facture.delete() + + # On notifie au serveur ComNPay qu'on a reçu les données pour traitement + return HttpResponse("HTTP/1.1 200 OK") + + facture.valid = True + facture.save() + + # A nouveau, on notifie au serveur qu'on a bien traité les données + return HttpResponse("HTTP/1.0 200 OK") + + +def comnpay(facture, host): + p = ComnpayPayment( + "DEMO", + "DEMO", + 'https://' + host + reverse('cotisations:accept_payment', kwargs={'factureid':facture.id}), + 'https://' + host + reverse('cotisations:refuse_payment'), + 'https://' + host + reverse('cotisations:ipn'), + "", + "D" + ) + r = { + 'action' : 'https://secure.homologation.comnpay.com', + 'method' : 'POST', + 'content' : p.buildSecretHTML("Rechargement du solde", facture.prix(), idTransaction=str(facture.id)), + 'amount' : facture.prix, + } + return r + + +PAYMENT_SYSTEM = { + 'COMNPAY' : comnpay, + 'NONE' : None +} diff --git a/cotisations/payment_utils/__init__.py b/cotisations/payment_utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cotisations/payment_utils/comnpay.py b/cotisations/payment_utils/comnpay.py new file mode 100644 index 00000000..6c1701d3 --- /dev/null +++ b/cotisations/payment_utils/comnpay.py @@ -0,0 +1,68 @@ +import time +from random import randrange +import base64 +import hashlib +from collections import OrderedDict +from itertools import chain + +class Payment(): + + vad_number = "" + secret_key = "" + urlRetourOK = "" + urlRetourNOK = "" + urlIPN = "" + source = "" + typeTr = "D" + + def __init__(self, vad_number = "", secret_key = "", urlRetourOK = "", urlRetourNOK = "", urlIPN = "", source="", typeTr="D"): + self.vad_number = vad_number + self.secret_key = secret_key + self.urlRetourOK = urlRetourOK + self.urlRetourNOK = urlRetourNOK + self.urlIPN = urlIPN + self.source = source + self.typeTr = typeTr + + def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""): + if idTransaction == "": + self.idTransaction = str(time.time())+self.vad_number+str(randrange(999)) + else: + self.idTransaction = idTransaction + + array_tpe = OrderedDict( + montant= str(montant), + idTPE= self.vad_number, + idTransaction= self.idTransaction, + devise= "EUR", + lang= 'fr', + nom_produit= produit, + source= self.source, + urlRetourOK= self.urlRetourOK, + urlRetourNOK= self.urlRetourNOK, + typeTr= str(self.typeTr) + ) + + if self.urlIPN!="": + array_tpe['urlIPN'] = self.urlIPN + + array_tpe['key'] = self.secret_key; + strWithKey = base64.b64encode(bytes('|'.join(array_tpe.values()), 'utf-8')) + del array_tpe["key"] + array_tpe['sec'] = hashlib.sha512(strWithKey).hexdigest() + + ret = "" + for key in array_tpe: + ret += '' + + return ret + + def validSec(self, values, secret_key): + if "sec" in values: + sec = values['sec'] + del values["sec"] + strWithKey = hashlib.sha512(base64.b64encode(bytes('|'.join(values.values()) +"|"+secret_key, 'utf-8'))).hexdigest() + return strWithKey.upper() == sec.upper() + else: + return False + diff --git a/cotisations/templates/cotisations/payment.html b/cotisations/templates/cotisations/payment.html new file mode 100644 index 00000000..46f26784 --- /dev/null +++ b/cotisations/templates/cotisations/payment.html @@ -0,0 +1,37 @@ +{% extends "cotisations/sidebar.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load staticfiles%} + +{% block title %}Rechargement du solde{% endblock %} + +{% block content %} +
Solde de l'utilisateur : {{ request.user.solde }} €
+ +{% endblock %} diff --git a/cotisations/urls.py b/cotisations/urls.py index cbeeb8eb..0040e48c 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -115,5 +115,21 @@ urlpatterns = [ views.new_facture_solde, name='new_facture_solde' ), + url(r'^recharge/$', + views.recharge, + name='recharge' + ), + url(r'^payment/accept/(?P