|
1 | | -import logging |
2 | 1 | import json |
| 2 | +import logging |
3 | 3 | from decimal import Decimal |
4 | 4 |
|
| 5 | +from base.models.project import Project |
| 6 | +from certification.models.certifying_organisation import CertifyingOrganisation |
| 7 | +from certification.models.credits_order import CreditsOrder |
| 8 | +from certification.utilities import PayrexxService |
5 | 9 | from django.conf import settings |
6 | | -from django.http import HttpResponse, HttpResponseForbidden, Http404 |
7 | | -from django.shortcuts import render, redirect, get_object_or_404 |
| 10 | +from django.core.mail import send_mail |
| 11 | +from django.http import Http404, HttpResponse, HttpResponseForbidden |
| 12 | +from django.shortcuts import get_object_or_404, redirect, render |
8 | 13 | from django.urls import reverse |
9 | 14 | from django.utils.decorators import method_decorator |
10 | 15 | from django.views import View |
11 | | -from django.views.generic import TemplateView |
12 | 16 | from django.views.decorators.csrf import csrf_exempt |
13 | | - |
14 | | -from certification.models.certifying_organisation import CertifyingOrganisation |
15 | | -from certification.models.credits_order import CreditsOrder |
16 | | -from certification.utilities import PayrexxService |
17 | | -from base.models.project import Project |
18 | | - |
19 | | -from django.core.mail import send_mail |
| 17 | +from django.views.generic import TemplateView |
20 | 18 |
|
21 | 19 | logger = logging.getLogger(__name__) |
22 | 20 |
|
23 | 21 |
|
24 | 22 | class PayrexxTopUpView(TemplateView): |
25 | | - template_name = 'certificate/top_up.html' |
26 | | - project_slug = '' |
27 | | - organisation_slug = '' |
| 23 | + template_name = "certificate/top_up.html" |
| 24 | + project_slug = "" |
| 25 | + organisation_slug = "" |
28 | 26 |
|
| 27 | + def get_context_data(self, **kwargs): |
| 28 | + context = super(PayrexxTopUpView, self).get_context_data(**kwargs) |
| 29 | + self.project_slug = "qgis" |
| 30 | + self.organisation_slug = self.kwargs.get("organisation_slug", None) |
29 | 31 |
|
30 | | - def get_context_data(self, **kwargs): |
31 | | - context = super(PayrexxTopUpView, self).get_context_data(**kwargs) |
32 | | - self.project_slug = 'qgis' |
33 | | - self.organisation_slug = self.kwargs.get('organisation_slug', None) |
| 32 | + certifying_organisation = CertifyingOrganisation.objects.get( |
| 33 | + slug=self.organisation_slug |
| 34 | + ) |
| 35 | + project = Project.objects.get(slug=self.project_slug) |
34 | 36 |
|
35 | | - certifying_organisation = ( |
36 | | - CertifyingOrganisation.objects.get(slug=self.organisation_slug) |
37 | | - ) |
38 | | - project = Project.objects.get(slug=self.project_slug) |
| 37 | + context["the_project"] = project |
| 38 | + context["cert_organisation"] = certifying_organisation |
39 | 39 |
|
40 | | - context['the_project'] = project |
41 | | - context['cert_organisation'] = certifying_organisation |
| 40 | + return context |
42 | 41 |
|
43 | | - return context |
44 | | - |
45 | | - def post(self, request, *args, **kwargs): |
46 | | - """Post the project_slug from the URL and define the Project |
| 42 | + def post(self, request, *args, **kwargs): |
| 43 | + """Post the project_slug from the URL and define the Project |
47 | 44 |
|
48 | | - :param request: HTTP request object |
49 | | - :type request: HttpRequest |
| 45 | + :param request: HTTP request object |
| 46 | + :type request: HttpRequest |
50 | 47 |
|
51 | | - :param args: Positional arguments |
52 | | - :type args: tuple |
| 48 | + :param args: Positional arguments |
| 49 | + :type args: tuple |
53 | 50 |
|
54 | | - :param kwargs: Keyword arguments |
55 | | - :type kwargs: dict |
| 51 | + :param kwargs: Keyword arguments |
| 52 | + :type kwargs: dict |
56 | 53 |
|
57 | | - :returns: Unaltered request object |
58 | | - :rtype: HttpResponse |
59 | | - """ |
| 54 | + :returns: Unaltered request object |
| 55 | + :rtype: HttpResponse |
| 56 | + """ |
60 | 57 |
|
61 | | - total_credits = self.request.POST.get('total-credits', None) |
| 58 | + total_credits = self.request.POST.get("total-credits", None) |
62 | 59 |
|
63 | | - self.project_slug = 'qgis' |
64 | | - self.organisation_slug = self.kwargs.get('organisation_slug', None) |
| 60 | + self.project_slug = "qgis" |
| 61 | + self.organisation_slug = self.kwargs.get("organisation_slug", None) |
65 | 62 |
|
66 | | - project = Project.objects.get( |
67 | | - slug=self.project_slug |
68 | | - ) |
| 63 | + project = Project.objects.get(slug=self.project_slug) |
69 | 64 |
|
70 | | - organisation = CertifyingOrganisation.objects.get( |
71 | | - slug=self.organisation_slug |
72 | | - ) |
| 65 | + organisation = CertifyingOrganisation.objects.get(slug=self.organisation_slug) |
73 | 66 |
|
74 | | - if not total_credits: |
75 | | - raise Http404('Missing important value') |
| 67 | + if not total_credits: |
| 68 | + raise Http404("Missing important value") |
76 | 69 |
|
77 | | - try: |
78 | | - total_credits_decimal = Decimal(total_credits) |
79 | | - total_credits = int(total_credits) |
80 | | - except ValueError: |
81 | | - raise Http404('Wrong total credits format') |
| 70 | + try: |
| 71 | + total_credits_decimal = Decimal(total_credits) |
| 72 | + total_credits = int(total_credits) |
| 73 | + except ValueError: |
| 74 | + raise Http404("Wrong total credits format") |
82 | 75 |
|
83 | | - cost_of_credits = project.credit_cost * total_credits_decimal |
84 | | - description = f"Top up {total_credits} \ |
| 76 | + cost_of_credits = project.credit_cost * total_credits_decimal |
| 77 | + description = f"Top up {total_credits} \ |
85 | 78 | credit{'s' if total_credits > 1 else ''} \ |
86 | 79 | for {organisation.name}" |
87 | 80 |
|
88 | | - payrexx = PayrexxService() |
89 | | - |
90 | | - # Create a new CreditsOrder instance |
91 | | - credits_order = CreditsOrder.objects.create( |
92 | | - organisation=organisation, |
93 | | - credits_requested=total_credits, |
94 | | - ) |
95 | | - |
96 | | - redirect_url = reverse('certifyingorganisation-detail', kwargs={'slug': self.organisation_slug}) |
97 | | - response = payrexx.create_gateway( |
98 | | - reference_id=credits_order.pk, |
99 | | - amount=cost_of_credits, |
100 | | - currency=project.credit_cost_currency, |
101 | | - purpose=description, |
102 | | - redirect_url=redirect_url, |
103 | | - firstname=request.user.first_name, |
104 | | - lastname=request.user.last_name, |
105 | | - email=request.user.email, |
106 | | - ) |
107 | | - if response.get('status') == 'success': |
108 | | - gateway = response['data'][0] |
109 | | - return redirect(gateway['link']) |
110 | | - else: |
111 | | - credits_order.delete() |
112 | | - |
113 | | - # Handle error |
114 | | - return render(request, '500.html', {'error': response}) |
115 | | - |
116 | | - |
117 | | -@method_decorator(csrf_exempt, name='dispatch') # disable CSRF for webhooks |
| 81 | + payrexx = PayrexxService() |
| 82 | + |
| 83 | + # Create a new CreditsOrder instance |
| 84 | + credits_order = CreditsOrder.objects.create( |
| 85 | + organisation=organisation, |
| 86 | + credits_requested=total_credits, |
| 87 | + ) |
| 88 | + |
| 89 | + redirect_url = request.build_absolute_uri( |
| 90 | + reverse( |
| 91 | + "certifyingorganisation-detail", kwargs={"slug": self.organisation_slug} |
| 92 | + ) |
| 93 | + ) |
| 94 | + response = payrexx.create_gateway( |
| 95 | + reference_id=credits_order.pk, |
| 96 | + amount=cost_of_credits, |
| 97 | + currency=project.credit_cost_currency, |
| 98 | + purpose=description, |
| 99 | + redirect_url=redirect_url, |
| 100 | + firstname=request.user.first_name, |
| 101 | + lastname=request.user.last_name, |
| 102 | + email=request.user.email, |
| 103 | + ) |
| 104 | + if response.get("status") == "success": |
| 105 | + gateway = response["data"][0] |
| 106 | + return redirect(gateway["link"]) |
| 107 | + else: |
| 108 | + credits_order.delete() |
| 109 | + |
| 110 | + # Handle error |
| 111 | + return render(request, "500.html", {"error": response}) |
| 112 | + |
| 113 | + |
| 114 | +@method_decorator(csrf_exempt, name="dispatch") # disable CSRF for webhooks |
118 | 115 | class PayrexxWebhookView(View): |
119 | 116 |
|
120 | 117 | def post(self, request, *args, **kwargs): |
121 | | - # Step 1: Get POST data |
122 | | - try: |
123 | | - payload = json.loads(request.body) # Parse JSON data from request body |
124 | | - except json.JSONDecodeError: |
125 | | - return HttpResponseForbidden("Invalid JSON") |
126 | | - |
127 | | - # Step 2: Verify the transaction |
128 | | - transaction = payload.get('transaction', None) |
129 | | - transaction_id = transaction.get('id', None) if transaction else None |
130 | | - reference_id = transaction.get('referenceId', None) if transaction else None |
131 | | - if not transaction or not transaction_id or not reference_id: |
132 | | - return HttpResponse('Webhook triggered with non-credits order data. No action taken.', status=200) |
133 | | - |
134 | | - # Step 3: Get the CreditsOrder instance |
135 | | - get_object_or_404(CreditsOrder, pk=int(reference_id)) |
136 | | - |
137 | | - try: |
138 | | - payrexx = PayrexxService() |
139 | | - verified_transation = payrexx.get_transaction(transaction_id) |
140 | | - if verified_transation.get('status') != 'confirmed': |
141 | | - return HttpResponse("Transaction not confirmed! No credits issued.", status=200) |
142 | | - except Exception: |
143 | | - return HttpResponseForbidden("Transaction not found!") |
144 | | - |
145 | | - verified_reference_id = verified_transation.get('referenceId', None) |
146 | | - # Step 4: Update the organization's credits |
147 | | - credits_order = get_object_or_404(CreditsOrder, pk=int(verified_reference_id)) |
148 | | - if credits_order.credits_issued: |
149 | | - return HttpResponse("Credits already issued. No action taken.", status=200) |
150 | | - organisation = credits_order.organisation |
151 | | - current_credits = organisation.organisation_credits or 0 |
152 | | - organisation.organisation_credits = current_credits + credits_order.credits_requested |
153 | | - organisation.save() |
154 | | - credits_order.credits_issued = True |
155 | | - credits_order.save() |
156 | | - |
157 | | - # Step 5: Send email to organisation owners |
158 | | - organisation_owners = ( |
159 | | - organisation.organisation_owners.all() |
160 | | - ) |
161 | | - send_mail( |
162 | | - subject=f"QGIS Certification: Credits Top Up for {organisation.name}", |
163 | | - message=f"Your organisation has been credited with {credits_order.credits_requested} credits.", |
164 | | - from_email=settings.DEFAULT_FROM_EMAIL, |
165 | | - recipient_list=[owner.email for owner in organisation_owners], |
166 | | - ) |
167 | | - |
168 | | - # Step 6: Respond 200 OK |
169 | | - return HttpResponse('Credits updated with success!', status=200) |
| 118 | + # Step 1: Get POST data |
| 119 | + try: |
| 120 | + payload = json.loads(request.body) # Parse JSON data from request body |
| 121 | + except json.JSONDecodeError: |
| 122 | + return HttpResponseForbidden("Invalid JSON") |
| 123 | + |
| 124 | + # Step 2: Verify the transaction |
| 125 | + transaction = payload.get("transaction", None) |
| 126 | + transaction_id = transaction.get("id", None) if transaction else None |
| 127 | + reference_id = transaction.get("referenceId", None) if transaction else None |
| 128 | + if not transaction or not transaction_id or not reference_id: |
| 129 | + return HttpResponse( |
| 130 | + "Webhook triggered with non-credits order data. No action taken.", |
| 131 | + status=200, |
| 132 | + ) |
| 133 | + |
| 134 | + # Step 3: Get the CreditsOrder instance |
| 135 | + get_object_or_404(CreditsOrder, pk=int(reference_id)) |
| 136 | + |
| 137 | + try: |
| 138 | + payrexx = PayrexxService() |
| 139 | + verified_transation = payrexx.get_transaction(transaction_id) |
| 140 | + if verified_transation.get("status") != "confirmed": |
| 141 | + return HttpResponse( |
| 142 | + "Transaction not confirmed! No credits issued.", status=200 |
| 143 | + ) |
| 144 | + except Exception: |
| 145 | + return HttpResponseForbidden("Transaction not found!") |
| 146 | + |
| 147 | + verified_reference_id = verified_transation.get("referenceId", None) |
| 148 | + # Step 4: Update the organization's credits |
| 149 | + credits_order = get_object_or_404(CreditsOrder, pk=int(verified_reference_id)) |
| 150 | + if credits_order.credits_issued: |
| 151 | + return HttpResponse("Credits already issued. No action taken.", status=200) |
| 152 | + organisation = credits_order.organisation |
| 153 | + current_credits = organisation.organisation_credits or 0 |
| 154 | + organisation.organisation_credits = ( |
| 155 | + current_credits + credits_order.credits_requested |
| 156 | + ) |
| 157 | + organisation.save() |
| 158 | + credits_order.credits_issued = True |
| 159 | + credits_order.save() |
| 160 | + |
| 161 | + # Step 5: Send email to organisation owners |
| 162 | + organisation_owners = organisation.organisation_owners.all() |
| 163 | + send_mail( |
| 164 | + subject=f"QGIS Certification: Credits Top Up for {organisation.name}", |
| 165 | + message=f"Your organisation has been credited with {credits_order.credits_requested} credits.", |
| 166 | + from_email=settings.DEFAULT_FROM_EMAIL, |
| 167 | + recipient_list=[owner.email for owner in organisation_owners], |
| 168 | + ) |
| 169 | + |
| 170 | + # Step 6: Respond 200 OK |
| 171 | + return HttpResponse("Credits updated with success!", status=200) |
0 commit comments