Skip to content

Commit 8006e9f

Browse files
authored
Merge pull request #57 from Xpirix/fix_payrexx_redirect_url
Use absolute URI for redirect URL in PayrexxTopUpView
2 parents f781040 + 6cf1951 commit 8006e9f

File tree

1 file changed

+136
-134
lines changed

1 file changed

+136
-134
lines changed
Lines changed: 136 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,171 @@
1-
import logging
21
import json
2+
import logging
33
from decimal import Decimal
44

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
59
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
813
from django.urls import reverse
914
from django.utils.decorators import method_decorator
1015
from django.views import View
11-
from django.views.generic import TemplateView
1216
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
2018

2119
logger = logging.getLogger(__name__)
2220

2321

2422
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 = ""
2826

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)
2931

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)
3436

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
3939

40-
context['the_project'] = project
41-
context['cert_organisation'] = certifying_organisation
40+
return context
4241

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
4744
48-
:param request: HTTP request object
49-
:type request: HttpRequest
45+
:param request: HTTP request object
46+
:type request: HttpRequest
5047
51-
:param args: Positional arguments
52-
:type args: tuple
48+
:param args: Positional arguments
49+
:type args: tuple
5350
54-
:param kwargs: Keyword arguments
55-
:type kwargs: dict
51+
:param kwargs: Keyword arguments
52+
:type kwargs: dict
5653
57-
:returns: Unaltered request object
58-
:rtype: HttpResponse
59-
"""
54+
:returns: Unaltered request object
55+
:rtype: HttpResponse
56+
"""
6057

61-
total_credits = self.request.POST.get('total-credits', None)
58+
total_credits = self.request.POST.get("total-credits", None)
6259

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)
6562

66-
project = Project.objects.get(
67-
slug=self.project_slug
68-
)
63+
project = Project.objects.get(slug=self.project_slug)
6964

70-
organisation = CertifyingOrganisation.objects.get(
71-
slug=self.organisation_slug
72-
)
65+
organisation = CertifyingOrganisation.objects.get(slug=self.organisation_slug)
7366

74-
if not total_credits:
75-
raise Http404('Missing important value')
67+
if not total_credits:
68+
raise Http404("Missing important value")
7669

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")
8275

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} \
8578
credit{'s' if total_credits > 1 else ''} \
8679
for {organisation.name}"
8780

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
118115
class PayrexxWebhookView(View):
119116

120117
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

Comments
 (0)