From 32642a29f05a9d69cd570aaf9e62b908aaa9b973 Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <ynerant@crans.org>
Date: Sat, 19 Jun 2021 11:42:51 +0200
Subject: [PATCH] Authorize user rather than Crans to use Note Kfet

---
 billing/admin.py                  |   7 --
 billing/forms.py                  |  23 +------
 billing/models/payment_methods.py |  26 +++-----
 billing/urls.py                   |   1 -
 billing/views/__init__.py         |   5 +-
 billing/views/note_kfet.py        | 107 ++++++++++--------------------
 billing/views/purchase.py         |   2 +-
 constellation/settings.py         |   2 +-
 8 files changed, 52 insertions(+), 121 deletions(-)

diff --git a/billing/admin.py b/billing/admin.py
index 3701a95..bc7b253 100644
--- a/billing/admin.py
+++ b/billing/admin.py
@@ -1,9 +1,7 @@
 from django import forms
-from django.conf import settings
 from django.contrib import admin
 
 from django.db import models
-from django.shortcuts import redirect
 from django.urls import reverse_lazy
 from django.utils.safestring import mark_safe
 from django.utils.text import capfirst
@@ -228,11 +226,6 @@ class NoteKfetPaymentMethodAdmin(PolymorphicChildModelAdmin):
     list_display = ('__str__', 'visible',)
     list_filter = ('visible',)
 
-    def add_view(self, request, form_url='', extra_context=None):
-        return redirect(settings.NOTE_KFET_URL + "o/authorize/?client_id=" + settings.NOTE_KFET_CLIENT_ID
-                        + "&response_type=code&scope=" + settings.NOTE_KFET_SCOPES + "&state=toto"
-                        + f"&redirect_uri={request.scheme}://{request.get_host()}{reverse_lazy('billing:note_auth')}")
-
 
 @admin.register(Invoice)
 class InvoiceAdmin(admin.ModelAdmin):
diff --git a/billing/forms.py b/billing/forms.py
index 889cff1..8286b8b 100644
--- a/billing/forms.py
+++ b/billing/forms.py
@@ -1,9 +1,6 @@
-import requests
 from django import forms
-from django.conf import settings
-from django.utils.translation import gettext_lazy as _
 
-from .models import Entry, Invoice, NoteKfetPaymentMethod, Product
+from .models import Entry, Invoice, Product
 
 
 class EntryForm(forms.ModelForm):
@@ -22,21 +19,3 @@ class ProductForm(forms.ModelForm):
     class Meta:
         model = Product
         fields = ('name',)
-
-
-class NoteKfetForm(forms.Form):
-    alias = forms.CharField(max_length=255)
-
-    def clean(self):
-        cleaned_data = super().clean()
-
-        alias = cleaned_data['alias']
-        access_token = NoteKfetPaymentMethod.objects.get().access_token
-        resp = requests.get(settings.NOTE_KFET_URL + "api/note/alias/?name=" + alias,
-                            headers={"Authorization": f"Bearer {access_token}"}).json()
-        if resp['count'] != 1:
-            self.add_error('alias', _("No user was found with this alias."))
-        else:
-            cleaned_data['note_id'] = resp['results'][0]['id']
-
-        return cleaned_data
diff --git a/billing/models/payment_methods.py b/billing/models/payment_methods.py
index 217ed97..7394ce6 100644
--- a/billing/models/payment_methods.py
+++ b/billing/models/payment_methods.py
@@ -1,3 +1,5 @@
+from django.conf import settings
+from django.contrib.sites.models import Site
 from django.db import models
 from django.urls import reverse_lazy
 from django.utils.translation import gettext_lazy as _
@@ -17,7 +19,7 @@ class PaymentMethod(PolymorphicModel):
         help_text=_("if enabled, all members will see this payment method."),
     )
 
-    def get_payment_url(self, invoice):
+    def get_payment_url(self, invoice, request=None):
         """
         URL where the user should be redirected after purchase.
         By default, the invoice stays invalid and we are redirected to the invoice list.
@@ -34,7 +36,7 @@ class PaymentMethod(PolymorphicModel):
 
 
 class ComnpayPaymentMethod(PaymentMethod):
-    def get_payment_url(self, invoice):
+    def get_payment_url(self, invoice, request=None):
         return reverse_lazy('billing:comnpay_redirect', args=(invoice.pk,))
 
     class Meta:
@@ -43,7 +45,7 @@ class ComnpayPaymentMethod(PaymentMethod):
 
 
 class StripePaymentMethod(PaymentMethod):
-    def get_payment_url(self, invoice):
+    def get_payment_url(self, invoice, request=None):
         return reverse_lazy('billing:stripe_redirect', args=(invoice.pk,))
 
     class Meta:
@@ -52,18 +54,12 @@ class StripePaymentMethod(PaymentMethod):
 
 
 class NoteKfetPaymentMethod(PaymentMethod):
-    access_token = models.CharField(
-        max_length=64,
-        verbose_name=_("access token"),
-    )
-
-    refresh_token = models.CharField(
-        max_length=64,
-        verbose_name=_("refresh token"),
-    )
-
-    def get_payment_url(self, invoice):
-        return reverse_lazy('billing:note_pay', args=(invoice.pk,))
+    def get_payment_url(self, invoice, request=None):
+        scheme = request.scheme if request else "https"
+        host = request.get_host() if request else Site.objects.first().domain
+        return settings.NOTE_KFET_URL + "o/authorize/?client_id=" + settings.NOTE_KFET_CLIENT_ID \
+            + "&response_type=code&scope=" + settings.NOTE_KFET_SCOPES + "&state=" + str(invoice.pk) \
+            + f"&redirect_uri={scheme}://{host}{reverse_lazy('billing:note_auth')}"
 
     class Meta:
         verbose_name = _("Note Kfet payment method")
diff --git a/billing/urls.py b/billing/urls.py
index a69fcda..3315ef1 100644
--- a/billing/urls.py
+++ b/billing/urls.py
@@ -19,5 +19,4 @@ urlpatterns = [
     path('stripe/fail/', views.StripeFailView.as_view(), name='stripe_fail'),
 
     path('note/authorize/', views.NoteKfetAuthorizeView.as_view(), name='note_auth'),
-    path('note/pay/<int:pk>/', views.NoteKfetPayView.as_view(), name='note_pay'),
 ]
diff --git a/billing/views/__init__.py b/billing/views/__init__.py
index 28983f8..9d6ee45 100644
--- a/billing/views/__init__.py
+++ b/billing/views/__init__.py
@@ -1,11 +1,10 @@
 from .comnpay import ComnpayFailView, ComnpaySuccessView, IPNComnpayView, RedirectComnpayView
-from .note_kfet import NoteKfetAuthorizeView, NoteKfetPayView
+from .note_kfet import NoteKfetAuthorizeView
 from .purchase import InvoiceListView, PurchaseView, RenderInvoiceView
 from .stripe import StripeFailView, StripeSuccessView, RedirectStripeView
 
 __all__ = [
-    "ComnpayFailView", "ComnpaySuccessView", "InvoiceListView", "IPNComnpayView",
-    "NoteKfetAuthorizeView", "NoteKfetPayView",
+    "ComnpayFailView", "ComnpaySuccessView", "InvoiceListView", "IPNComnpayView", "NoteKfetAuthorizeView",
     "PurchaseView", "RedirectComnpayView", "RedirectStripeView", "RenderInvoiceView",
     "StripeFailView", "StripeSuccessView",
 ]
diff --git a/billing/views/note_kfet.py b/billing/views/note_kfet.py
index 854fa7b..0026976 100644
--- a/billing/views/note_kfet.py
+++ b/billing/views/note_kfet.py
@@ -1,25 +1,28 @@
 import requests
 from django.conf import settings
 from django.contrib import messages
-from django.contrib.auth.mixins import LoginRequiredMixin
 from django.urls import reverse_lazy
 from django.utils.translation import gettext_lazy as _
-from django.views.generic import DetailView, FormView, RedirectView
+from django.views.generic import RedirectView
 
-from ..forms import NoteKfetForm
-from ..models import NoteKfetPaymentMethod, Invoice
+from ..models import Invoice
 
 
 class NoteKfetAuthorizeView(RedirectView):
+    url = reverse_lazy('index')
+
     def get(self, request, *args, **kwargs):
         if 'code' not in request.GET or 'state' not in request.GET:
             messages.error(request, _("Bad request"))
             return super().get(request, *args, **kwargs)
 
-        if request.GET['state'] != 'toto':  # FIXME use better state code
-            messages.error(request, _("Invalid state code"))
+        invoice_id = request.GET['state']
+        if not Invoice.objects.filter(pk=invoice_id).exists():
+            messages.error(request, _("Bad request"))
             return super().get(request, *args, **kwargs)
 
+        invoice = Invoice.objects.get(pk=invoice_id)
+
         code = request.GET['code']
 
         response = requests.post(settings.NOTE_KFET_URL + "o/token/", data={
@@ -33,73 +36,35 @@ class NoteKfetAuthorizeView(RedirectView):
         if 'error' not in resp_json:
             resp_json = response.json()
             access_token = resp_json['access_token']
-            refresh_token = resp_json['refresh_token']
-            payment_method, created = NoteKfetPaymentMethod.objects.get_or_create()
-            if created:
-                payment_method.name = "Note Kfet"
-            payment_method.access_token = access_token
-            payment_method.refresh_token = refresh_token
-            payment_method.save()
-
-            self.payment_method = payment_method
 
-            messages.success(request, _("Authorization succeed"))
+            note = requests.get(settings.NOTE_KFET_URL + "api/note/note/",
+                                headers={"Authorization": f"Bearer {access_token}"}).json()['results'][0]
+
+            response = requests.post(
+                settings.NOTE_KFET_URL + "api/note/transaction/transaction/",
+                json={
+                    'source_alias': note['name'],
+                    'destination_alias': 'Crans',
+                    'quantity': 1,
+                    'amount': invoice.price,
+                    'reason': f'Facture n°{invoice.id}',
+                    'valid': True,
+                    'polymorphic_ctype': 26,
+                    'source': note['id'],
+                    'destination': 2088,
+                    'resourcetype': 'Transaction',
+
+                },
+                headers={"Authorization": f"Bearer {access_token}"},
+            )
+
+            if response.status_code == 201:
+                messages.success(request, _("Payment succeed!"))
+                invoice.valid = True
+                invoice.save()
+            else:
+                messages.error(request, _("Error:") + " " + str(response.json()))
         else:
             messages.error(request, _("Error:") + " " + str(resp_json))
 
         return super().get(request, *args, **kwargs)
-
-    def get_redirect_url(self, *args, **kwargs):
-        if hasattr(self, 'payment_method'):
-            return reverse_lazy('admin:billing_paymentmethod_change', args=(self.payment_method.pk,))
-        else:
-            return reverse_lazy('admin:billing_paymentmethod_changelist')
-
-
-class NoteKfetPayView(LoginRequiredMixin, FormView, DetailView):
-    model = Invoice
-    # Don't pay validated invoices
-    queryset = Invoice.objects.filter(valid=False, payment_method__notekfetpaymentmethod__isnull=False)
-    form_class = NoteKfetForm
-    template_name = "billing/note_kfet_pay.html"
-
-    def post(self, request, *args, **kwargs):
-        self.object = self.get_object()
-        return super().post(request, *args, **kwargs)
-
-    def form_valid(self, form):
-        alias = form.cleaned_data['alias']
-        note_id = form.cleaned_data['note_id']
-        access_token = NoteKfetPaymentMethod.objects.get().access_token
-        invoice = self.get_object()
-
-        response = requests.post(
-            settings.NOTE_KFET_URL + "api/note/transaction/transaction/",
-            json={
-                'source_alias': alias,
-                'destination_alias': 'Crans',
-                'quantity': 1,
-                'amount': invoice.price,
-                'reason': f'Facture n°{invoice.id}',
-                'valid': True,
-                'polymorphic_ctype': 26,
-                'source': note_id,
-                'destination': 2088,
-                'resourcetype': 'Transaction',
-
-            },
-            headers={"Authorization": f"Bearer {access_token}"},
-        )
-
-        if response.status_code == 201:
-            messages.success(self.request, _("Payment succeed!"))
-            invoice.valid = True
-            invoice.save()
-        else:
-            messages.error(self.request, str(response.json()))
-            return super().form_invalid(form)
-
-        return super().form_valid(form)
-
-    def get_success_url(self):
-        return reverse_lazy('index')
diff --git a/billing/views/purchase.py b/billing/views/purchase.py
index a78c289..50ef9e8 100644
--- a/billing/views/purchase.py
+++ b/billing/views/purchase.py
@@ -85,7 +85,7 @@ class PurchaseView(LoginRequiredMixin, CreateView):
         return super().form_valid(form)
 
     def get_success_url(self):
-        return self.object.payment_method.get_payment_url(self.object)
+        return self.object.payment_method.get_payment_url(self.object, self.request)
 
 
 class InvoiceListView(LoginRequiredMixin, SingleTableView):
diff --git a/constellation/settings.py b/constellation/settings.py
index d49454c..4f31b9c 100644
--- a/constellation/settings.py
+++ b/constellation/settings.py
@@ -250,7 +250,7 @@ STRIPE_PRIVATE_KEY = "CHANGEME"
 NOTE_KFET_URL = "https://note.crans.org/"
 NOTE_KFET_CLIENT_ID = "CHANGEME"
 NOTE_KFET_CLIENT_SECRET = "CHANGEME"
-NOTE_KFET_SCOPES = "6_1 20_23"  # View aliases, make a transaction between a user and Crans
+NOTE_KFET_SCOPES = "3_1 17_1"  # View own note, create transaction from our own note
 
 try:
     from .settings_local import *  # noqa: F401, F403&
-- 
GitLab