mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-26 14:42:25 +00:00
Module de paiement par note
This commit is contained in:
parent
afcf36b659
commit
4141003fcf
9 changed files with 365 additions and 2 deletions
31
cotisations/migrations/0035_notepayment.py
Normal file
31
cotisations/migrations/0035_notepayment.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-09-01 11:27
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import cotisations.payment_methods.mixins
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0034_auto_20180831_1532'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='NotePayment',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('server', models.CharField(max_length=255, verbose_name='server')),
|
||||
('port', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('id_note', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'NoteKfet',
|
||||
},
|
||||
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
|
||||
),
|
||||
]
|
|
@ -127,10 +127,11 @@ method to your model, where `form` is an instance of
|
|||
"""
|
||||
|
||||
|
||||
from . import comnpay, cheque, balance, urls
|
||||
from . import comnpay, cheque, balance, note, urls
|
||||
|
||||
PAYMENT_METHODS = [
|
||||
comnpay,
|
||||
cheque,
|
||||
balance,
|
||||
note
|
||||
]
|
||||
|
|
26
cotisations/payment_methods/note/__init__.py
Normal file
26
cotisations/payment_methods/note/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# 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 © 2018 Gabriel Detraz, Pierre-Antoine Comby
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
This module contains a method to pay online using comnpay.
|
||||
"""
|
||||
from . import models, urls
|
||||
NAME = "NOTE"
|
||||
PaymentMethod = models.NotePayment
|
38
cotisations/payment_methods/note/forms.py
Normal file
38
cotisations/payment_methods/note/forms.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# 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 © 2018 Pierre-Antoine Comby
|
||||
# Copyright © 2018 Gabriel Detraz
|
||||
#
|
||||
# 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.
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from cotisations.utils import find_payment_method
|
||||
|
||||
class NoteCredentialForm(forms.Form):
|
||||
"""A special form to get credential to connect to a NoteKfet2015 server throught his API
|
||||
object.
|
||||
"""
|
||||
login = forms.CharField(
|
||||
label=_("pseudo note")
|
||||
)
|
||||
password = forms.CharField(
|
||||
label=_("Password"),
|
||||
widget=forms.PasswordInput
|
||||
)
|
||||
|
65
cotisations/payment_methods/note/models.py
Normal file
65
cotisations/payment_methods/note/models.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# 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 © 2018 Pierre-Antoine Comby
|
||||
# Copyright © 2018 Gabriel Detraz
|
||||
#
|
||||
# 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.
|
||||
from django.db import models
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib import messages
|
||||
|
||||
from cotisations.models import Paiement
|
||||
from cotisations.payment_methods.mixins import PaymentMethodMixin
|
||||
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
|
||||
class NotePayment(PaymentMethodMixin, models.Model):
|
||||
"""
|
||||
The model allowing you to pay with NoteKfet2015.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("NoteKfet")
|
||||
|
||||
payment = models.OneToOneField(
|
||||
Paiement,
|
||||
on_delete = models.CASCADE,
|
||||
related_name = 'payment_method',
|
||||
editable = False
|
||||
)
|
||||
server = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("server")
|
||||
)
|
||||
port = models.PositiveIntegerField(
|
||||
blank = True,
|
||||
null = True
|
||||
)
|
||||
id_note = models.PositiveIntegerField(
|
||||
blank = True,
|
||||
null = True
|
||||
)
|
||||
|
||||
def end_payment(self, invoice, request):
|
||||
return redirect(reverse(
|
||||
'cotisations:note:note_payment',
|
||||
kwargs={'factureid': invoice.id}
|
||||
))
|
74
cotisations/payment_methods/note/note.py
Executable file
74
cotisations/payment_methods/note/note.py
Executable file
|
@ -0,0 +1,74 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
# Codé par PAC , forké de 20-100
|
||||
|
||||
""" Module pour dialoguer avec la NoteKfet2015 """
|
||||
|
||||
import socket
|
||||
import json
|
||||
import ssl
|
||||
import traceback
|
||||
|
||||
|
||||
def get_response(socket):
|
||||
length_str = b''
|
||||
char = socket.recv(1)
|
||||
while char != b'\n':
|
||||
length_str += char
|
||||
char = socket.recv(1)
|
||||
total = int(length_str)
|
||||
return json.loads(socket.recv(total).decode('utf-8'))
|
||||
|
||||
def connect(server, port):
|
||||
sock = socket.socket()
|
||||
try:
|
||||
# On établit la connexion sur port 4242
|
||||
sock.connect((server, port))
|
||||
# On passe en SSL
|
||||
sock = ssl.wrap_socket(sock)
|
||||
# On fait un hello
|
||||
sock.send(b'["hello", "manual"]')
|
||||
retcode = get_response(sock)
|
||||
except:
|
||||
# Si on a foiré quelque part, c'est que le serveur est down
|
||||
return (False, sock, "Serveur indisponible")
|
||||
return (True, sock, "")
|
||||
|
||||
def login(server, port, username, password, masque = [[], [], True]):
|
||||
result, sock, err = connect(server, port)
|
||||
if not result:
|
||||
return (False, None, err)
|
||||
try:
|
||||
commande = ["login", [username, password, "bdd", masque]]
|
||||
sock.send(json.dumps(commande).encode("utf-8"))
|
||||
response = get_response(sock)
|
||||
retcode = response['retcode']
|
||||
if retcode == 0:
|
||||
return (True, sock, "")
|
||||
elif retcode == 5:
|
||||
return (False, sock, "Login incorrect")
|
||||
else:
|
||||
return (False, sock, "Erreur inconnue " + str(retcode))
|
||||
except:
|
||||
# Si on a foiré quelque part, c'est que le serveur est down
|
||||
return (False, sock, "Erreur de communication avec le serveur")
|
||||
|
||||
|
||||
def don(sock, montant, id_note, facture):
|
||||
"""
|
||||
Faire faire un don à l'id_note
|
||||
"""
|
||||
try:
|
||||
sock.send(json.dumps(["dons", [[id_note], round(montant*100), "Facture : id=%s, designation=%s" % (facture.id, facture.name())]]).encode("utf-8"))
|
||||
response = get_response(sock)
|
||||
retcode = response['retcode']
|
||||
transaction_retcode = response["msg"][0][0]
|
||||
if 0 < retcode < 100 or 200 <= retcode or 0 < transaction_retcode < 100 or 200 <= transaction_retcode:
|
||||
return (False, "Transaction échouée. (Solde trop négatif ?)")
|
||||
elif retcode == 0:
|
||||
return (True, "")
|
||||
else:
|
||||
return (False, "Erreur inconnue " + str(retcode))
|
||||
except:
|
||||
return (False, "Erreur de communication avec le serveur")
|
30
cotisations/payment_methods/note/urls.py
Normal file
30
cotisations/payment_methods/note/urls.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# 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 © 2018 Gabriel Detraz
|
||||
#
|
||||
# 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.
|
||||
from django.conf.urls import url
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^note_payment/(?P<factureid>[0-9]+)$',
|
||||
views.note_payment,
|
||||
name='note_payment'
|
||||
),
|
||||
]
|
97
cotisations/payment_methods/note/views.py
Normal file
97
cotisations/payment_methods/note/views.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# 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 © 2018 Gabriel Detraz
|
||||
# Copyright © 2018 Pierre-Antoine Comby
|
||||
#
|
||||
# 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.
|
||||
"""Payment
|
||||
|
||||
Here are the views needed by comnpay
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
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.utils.translation import ugettext as _
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
|
||||
from cotisations.models import Facture
|
||||
from cotisations.utils import find_payment_method
|
||||
from .models import NotePayment
|
||||
from re2o.views import form
|
||||
from re2o.acl import (
|
||||
can_create,
|
||||
can_edit
|
||||
)
|
||||
from .note import login, don
|
||||
from .forms import NoteCredentialForm
|
||||
|
||||
@login_required
|
||||
@can_edit(Facture)
|
||||
def note_payment(request, facture, factureid):
|
||||
"""
|
||||
Build a request to start the negociation with NoteKfet by using
|
||||
a facture id, the price and the login/password data stored in
|
||||
the preferences.
|
||||
"""
|
||||
user = facture.user
|
||||
payment_method = find_payment_method(facture.paiement)
|
||||
if not payment_method or not isinstance(payment_method, NotePayment):
|
||||
messages.error(request, "Erreur inconnue")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': user.id}
|
||||
))
|
||||
noteform = NoteCredentialForm(request.POST or None)
|
||||
if noteform.is_valid():
|
||||
pseudo = noteform.cleaned_data['login']
|
||||
password = noteform.cleaned_data['password']
|
||||
result, sock, err = login(payment_method.server, payment_method.port, pseudo, password)
|
||||
if not result:
|
||||
messages.error(request, err)
|
||||
return form(
|
||||
{'form': noteform, 'amount': facture.prix_total()},
|
||||
"cotisations/payment.html",
|
||||
request
|
||||
)
|
||||
else:
|
||||
result, err = don(sock, facture.prix_total(), payment_method.id_note, facture)
|
||||
if not result:
|
||||
messages.error(request, err)
|
||||
return form(
|
||||
{'form': noteform, 'amount': facture.prix_total()},
|
||||
"cotisations/payment.html",
|
||||
request
|
||||
)
|
||||
facture.valid = True
|
||||
facture.save()
|
||||
messages.success(request, "Le paiement par note a bien été effectué")
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': user.id}
|
||||
))
|
||||
return form(
|
||||
{'form': noteform, 'amount': facture.prix_total()},
|
||||
"cotisations/payment.html",
|
||||
request
|
||||
)
|
|
@ -19,9 +19,10 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
from django.conf.urls import include, url
|
||||
from . import comnpay, cheque
|
||||
from . import comnpay, cheque, note
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^comnpay/', include(comnpay.urls, namespace='comnpay')),
|
||||
url(r'^cheque/', include(cheque.urls, namespace='cheque')),
|
||||
url(r'^note/', include(note.urls, namespace='note')),
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue