Commit f7657a22 authored by Hugo LEVY-FALK's avatar Hugo LEVY-FALK Committed by root

Rechargement via comnpay du solde.

parent d9ebb266
......@@ -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 = []
)
......@@ -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\
......
"""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
}
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 += '<input type="hidden" name="'+key+'" value="'+array_tpe[key]+'"/>'
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
{% 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 %}
<h3>Recharger de {{ amount }} €</h3>
<form class="form" method="{{ method }}" action="{{action}}">
{{ content | safe }}
{% bootstrap_button "Payer" button_type="submit" icon="piggy-bank" %}
</form>
{% endblock %}
{% 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 %}
<h3>Rechargement du solde</h3>
<p>Solde de l'utilisateur : {{ request.user.solde }} €</p>
<form class="form" method="post">
{% csrf_token %}
{% bootstrap_form rechargeform %}
{% bootstrap_button "Valider" button_type="submit" icon="piggy-bank" %}
</form>
{% endblock %}
......@@ -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<factureid>[0-9]+)$',
payment.accept_payment,
name='accept_payment'
),
url(r'^payment/refuse/$',
payment.refuse_payment,
name='refuse_payment'
),
url(r'^payment/ipn/$',
payment.ipn,
name='ipn'
),
url(r'^$', views.index, name='index'),
]
......@@ -37,6 +37,8 @@ from django.db import transaction
from django.db.models import Q
from django.forms import modelformset_factory, formset_factory
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.debug import sensitive_variables
from reversion import revisions as reversion
from reversion.models import Version
# Import des models, forms et fonctions re2o
......@@ -72,6 +74,7 @@ from .forms import (
NewFactureSoldeForm,
RechargeForm
)
from . import payment
from .tex import render_invoice
......@@ -682,3 +685,23 @@ def new_facture_solde(request, userid):
}, 'cotisations/new_facture_solde.html', request)
@login_required
def recharge(request):
f = RechargeForm(request.POST or None)
if f.is_valid():
facture = Facture(user=request.user)
paiement, _created = Paiement.objects.get_or_create(moyen='Rechargement en ligne')
facture.paiement = paiement
facture.valid = False
facture.save()
v = Vente.objects.create(
facture=facture,
name='solde',
prix=f.cleaned_data['value'],
number=1,
)
v.save()
options, _created = AssoOption.objects.get_or_create()
content = payment.PAYMENT_SYSTEM[options.payment](facture, request.get_host())
return render(request, 'cotisations/payment.html', content)
return form({'rechargeform':f}, 'cotisations/recharge.html', request)
......@@ -2153,3 +2153,4 @@ def srv_post_save(sender, **kwargs):
def text_post_delete(sender, **kwargs):
"""Regeneration dns après modification d'un SRV"""
regen('dns')
......@@ -48,7 +48,7 @@ class EditOptionalUserForm(ModelForm):
téléphone'
self.fields['user_solde'].label = 'Activation du solde pour\
les utilisateurs'
self.fields['max_recharge'].label = 'Rechargement max'
self.fields['max_solde'].label = 'Solde maximum'
class EditOptionalMachineForm(ModelForm):
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-01-11 10:34
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0028_auto_20180111_1129'),
]
operations = [
migrations.AddField(
model_name='assooption',
name='payment',
field=models.CharField(choices=[('NONE', 'NONE'), ('COMNPAY', 'COMNPAY')], default='NONE', max_length=255),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-01-11 22:46
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0029_auto_20180111_1134'),
]
operations = [
migrations.RemoveField(
model_name='optionaluser',
name='max_recharge',
),
migrations.AddField(
model_name='optionaluser',
name='max_solde',
field=models.DecimalField(decimal_places=2, default=50, max_digits=5),
),
]
......@@ -41,10 +41,10 @@ class OptionalUser(models.Model):
decimal_places=2,
default=0
)
max_recharge = models.DecimalField(
max_solde = models.DecimalField(
max_digits=5,
decimal_places=2,
default=100
default=50
)
gpg_fingerprint = models.BooleanField(default=True)
all_can_create = models.BooleanField(
......
......@@ -55,8 +55,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>Creations d'users par tous</th>
<td>{{ useroptions.all_can_create }}</td>
{% if useroptions.user_solde %}
<th>Rechargement max</th>
<td>{{ useroptions.max_recharge }}</td>
<th>Solde maximum</th>
<td>{{ useroptions.max_solde }}</td>
{% endif %}
</tr>
</table>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment