From 4141003fcf94e3e2dda89ddaf6a6d5babd9038f1 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Comby Date: Sat, 1 Sep 2018 18:38:12 +0200 Subject: [PATCH] Module de paiement par note --- cotisations/migrations/0035_notepayment.py | 31 +++++++ cotisations/payment_methods/__init__.py | 3 +- cotisations/payment_methods/note/__init__.py | 26 ++++++ cotisations/payment_methods/note/forms.py | 38 ++++++++ cotisations/payment_methods/note/models.py | 65 +++++++++++++ cotisations/payment_methods/note/note.py | 74 +++++++++++++++ cotisations/payment_methods/note/urls.py | 30 ++++++ cotisations/payment_methods/note/views.py | 97 ++++++++++++++++++++ cotisations/payment_methods/urls.py | 3 +- 9 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 cotisations/migrations/0035_notepayment.py create mode 100644 cotisations/payment_methods/note/__init__.py create mode 100644 cotisations/payment_methods/note/forms.py create mode 100644 cotisations/payment_methods/note/models.py create mode 100755 cotisations/payment_methods/note/note.py create mode 100644 cotisations/payment_methods/note/urls.py create mode 100644 cotisations/payment_methods/note/views.py diff --git a/cotisations/migrations/0035_notepayment.py b/cotisations/migrations/0035_notepayment.py new file mode 100644 index 00000000..1d8bcd48 --- /dev/null +++ b/cotisations/migrations/0035_notepayment.py @@ -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), + ), + ] diff --git a/cotisations/payment_methods/__init__.py b/cotisations/payment_methods/__init__.py index b78a86fd..85f2bd53 100644 --- a/cotisations/payment_methods/__init__.py +++ b/cotisations/payment_methods/__init__.py @@ -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 ] diff --git a/cotisations/payment_methods/note/__init__.py b/cotisations/payment_methods/note/__init__.py new file mode 100644 index 00000000..1f133d11 --- /dev/null +++ b/cotisations/payment_methods/note/__init__.py @@ -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 diff --git a/cotisations/payment_methods/note/forms.py b/cotisations/payment_methods/note/forms.py new file mode 100644 index 00000000..e52c275c --- /dev/null +++ b/cotisations/payment_methods/note/forms.py @@ -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 + ) + diff --git a/cotisations/payment_methods/note/models.py b/cotisations/payment_methods/note/models.py new file mode 100644 index 00000000..60ed8ad3 --- /dev/null +++ b/cotisations/payment_methods/note/models.py @@ -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} + )) diff --git a/cotisations/payment_methods/note/note.py b/cotisations/payment_methods/note/note.py new file mode 100755 index 00000000..8b7614b0 --- /dev/null +++ b/cotisations/payment_methods/note/note.py @@ -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") diff --git a/cotisations/payment_methods/note/urls.py b/cotisations/payment_methods/note/urls.py new file mode 100644 index 00000000..7b939136 --- /dev/null +++ b/cotisations/payment_methods/note/urls.py @@ -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[0-9]+)$', + views.note_payment, + name='note_payment' + ), +] diff --git a/cotisations/payment_methods/note/views.py b/cotisations/payment_methods/note/views.py new file mode 100644 index 00000000..d4d0ac21 --- /dev/null +++ b/cotisations/payment_methods/note/views.py @@ -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 + ) diff --git a/cotisations/payment_methods/urls.py b/cotisations/payment_methods/urls.py index 20e50255..dd6d7726 100644 --- a/cotisations/payment_methods/urls.py +++ b/cotisations/payment_methods/urls.py @@ -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')), ]