1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2024-12-25 06:22:22 +00:00

Create Hello Asso checkout intents

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-02-19 00:17:14 +01:00
parent 98d04b9093
commit b3555a7807
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
6 changed files with 157 additions and 6 deletions

View File

@ -1,13 +1,13 @@
# Copyright (C) 2020 by Animath # Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from datetime import date from datetime import date, datetime
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.template import loader from django.template import loader
from django.urls import reverse_lazy from django.urls import reverse_lazy, reverse
from django.utils import timezone from django.utils import timezone
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
@ -16,6 +16,8 @@ from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
from tfjm import helloasso
from tfjm.tokens import email_validation_token from tfjm.tokens import email_validation_token
@ -564,6 +566,46 @@ class Payment(models.Model):
default=False, default=False,
) )
def get_checkout_intent(self):
if self.checkout_intent_id is None:
return None
return helloasso.get_checkout_intent(self.checkout_intent_id)
def create_checkout_intent(self):
checkout_intent = self.get_checkout_intent()
if checkout_intent is not None:
return checkout_intent
from participation.models import Tournament
tournament = self.registrations.first().team.participation.tournament \
if not self.final else Tournament.final_tournament()
year = datetime.now().year
base_site = "https://" + Site.objects.first().domain
checkout_intent = helloasso.create_checkout_intent(
amount=100 * self.amount,
name=f"Participation au TFJM² {year} - {tournament.name}",
back_url=base_site + reverse('registration:update_payment', args=(self.id,)),
error_url=base_site + reverse('registration:update_payment', args=(self.id,)),
return_url=base_site + reverse('registration:update_payment', args=(self.id,)),
contains_donation=False,
metadata=dict(
users=[
dict(user_id=registration.user.id,
first_name=registration.user.first_name,
last_name=registration.user.last_name,
email=registration.user.email,)
for registration in self.registrations.all()
],
payment_id=self.id,
final=self.final,
tournament_id=tournament.id,
)
)
self.checkout_intent_id = checkout_intent["id"]
self.save()
return checkout_intent
def get_absolute_url(self): def get_absolute_url(self):
return reverse_lazy("registration:update_payment", args=(self.pk,)) return reverse_lazy("registration:update_payment", args=(self.pk,))

View File

@ -86,7 +86,7 @@
payer pour vous. payer pour vous.
<div class="text-center"> <div class="text-center">
<a href="#" class="btn btn-primary"> <a href="{% url "registration:payment_hello_asso" pk=payment.pk %}" class="btn btn-primary">
<i class="fas fa-credit-card"></i> Aller sur la page Hello Asso <i class="fas fa-credit-card"></i> Aller sur la page Hello Asso
</a> </a>
</div> </div>

View File

@ -4,8 +4,8 @@
from django.urls import path from django.urls import path
from .views import AddOrganizerView, AdultPhotoAuthorizationTemplateView, ChildPhotoAuthorizationTemplateView, \ from .views import AddOrganizerView, AdultPhotoAuthorizationTemplateView, ChildPhotoAuthorizationTemplateView, \
InstructionsTemplateView, MyAccountDetailView, ParentalAuthorizationTemplateView, PaymentUpdateView, \ InstructionsTemplateView, MyAccountDetailView, ParentalAuthorizationTemplateView, \
PaymentUpdateGroupView, \ PaymentUpdateGroupView, PaymentUpdateView, PaymenRedirectHelloAssoView, \
ResetAdminView, SignupView, UserDetailView, UserImpersonateView, UserListView, UserResendValidationEmailView, \ ResetAdminView, SignupView, UserDetailView, UserImpersonateView, UserListView, UserResendValidationEmailView, \
UserUpdateView, UserUploadHealthSheetView, UserUploadParentalAuthorizationView, UserUploadPhotoAuthorizationView, \ UserUpdateView, UserUploadHealthSheetView, UserUploadParentalAuthorizationView, UserUploadPhotoAuthorizationView, \
UserUploadVaccineSheetView, UserValidateView, UserValidationEmailSentView UserUploadVaccineSheetView, UserValidateView, UserValidationEmailSentView
@ -40,6 +40,7 @@ urlpatterns = [
path("update-payment/<int:pk>/", PaymentUpdateView.as_view(), name="update_payment"), path("update-payment/<int:pk>/", PaymentUpdateView.as_view(), name="update_payment"),
path("update-payment/<int:pk>/toggle-group-mode/", PaymentUpdateGroupView.as_view(), path("update-payment/<int:pk>/toggle-group-mode/", PaymentUpdateGroupView.as_view(),
name="update_payment_group_mode"), name="update_payment_group_mode"),
path("update-payment/<int:pk>/hello-asso/", PaymenRedirectHelloAssoView.as_view(), name="payment_hello_asso"),
path("user/<int:pk>/impersonate/", UserImpersonateView.as_view(), name="user_impersonate"), path("user/<int:pk>/impersonate/", UserImpersonateView.as_view(), name="user_impersonate"),
path("user/list/", UserListView.as_view(), name="user_list"), path("user/list/", UserListView.as_view(), name="user_list"),
path("reset-admin/", ResetAdminView.as_view(), name="reset_admin"), path("reset-admin/", ResetAdminView.as_view(), name="reset_admin"),

View File

@ -506,6 +506,7 @@ class PaymentUpdateGroupView(LoginRequiredMixin, DetailView):
payment.grouped = False payment.grouped = False
tournament = first_reg.team.participation.tournament if not payment.final else Tournament.final_tournament() tournament = first_reg.team.participation.tournament if not payment.final else Tournament.final_tournament()
payment.amount = tournament.price payment.amount = tournament.price
payment.checkout_intent_id = None
payment.save() payment.save()
for registration in registrations[1:]: for registration in registrations[1:]:
p = Payment.objects.create(type=payment.type, p = Payment.objects.create(type=payment.type,
@ -525,11 +526,30 @@ class PaymentUpdateGroupView(LoginRequiredMixin, DetailView):
payment.registrations.add(student) payment.registrations.add(student)
payment.amount = tournament.price * reg.team.students.count() payment.amount = tournament.price * reg.team.students.count()
payment.grouped = True payment.grouped = True
payment.checkout_intent_id = None
payment.save() payment.save()
return redirect(reverse_lazy("registration:update_payment", args=(payment.pk,))) return redirect(reverse_lazy("registration:update_payment", args=(payment.pk,)))
class PaymenRedirectHelloAssoView(LoginRequiredMixin, DetailView):
model = Payment
def dispatch(self, request, *args, **kwargs):
if not self.request.user.is_authenticated or \
not self.request.user.registration.is_admin \
and (self.request.user.registration not in self.get_object().registrations.all()
or self.get_object().valid is not False):
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
payment = self.get_object()
checkout_intent = payment.create_checkout_intent()
return redirect(checkout_intent["redirectUrl"])
class PhotoAuthorizationView(LoginRequiredMixin, View): class PhotoAuthorizationView(LoginRequiredMixin, View):
""" """
Display the sent photo authorization. Display the sent photo authorization.

86
tfjm/helloasso.py Normal file
View File

@ -0,0 +1,86 @@
# Copyright (C) 2024 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import datetime, timedelta
from django.conf import settings
import requests
_access_token = None
_refresh_token = None
_expires_at = None
def get_hello_asso_access_token():
global _access_token, _refresh_token, _expires_at
now = datetime.now()
if _access_token is None:
response = requests.post(
"https://api.helloasso.com/oauth2/token",
data={
"grant_type": "client_credentials",
"client_id": settings.HELLOASSO_CLIENT_ID,
"client_secret": settings.HELLOASSO_CLIENT_SECRET,
},
)
response.raise_for_status()
data = response.json()
_access_token = data["access_token"]
_refresh_token = data["refresh_token"]
_expires_at = now + timedelta(seconds=data["expires_in"])
elif now >= _expires_at:
response = requests.post(
"https://api.helloasso.com/oauth2/token",
data={
"grant_type": "refresh_token",
"refresh_token": _refresh_token,
},
)
response.raise_for_status()
data = response.json()
_access_token = data["access_token"]
_refresh_token = data["refresh_token"]
_expires_at = now + timedelta(seconds=data["expires_in"])
return _access_token
def get_checkout_intent(checkout_id):
token = get_hello_asso_access_token()
response = requests.get(
f"https://api.helloasso.com/v5/organizations/animath/checkout-intents/{checkout_id}",
headers={"Authorization": f"Bearer {token}"},
)
if response.status_code == 404:
return None
response.raise_for_status()
checkout_intent = response.json()
if requests.head(checkout_intent["redirectUrl"]).status_code == 404:
return None
return checkout_intent
def create_checkout_intent(amount, name, back_url, error_url, return_url, contains_donation=False, metadata=None):
token = get_hello_asso_access_token()
metadata = metadata or {}
response = requests.post(
"https://api.helloasso.com/v5/organizations/animath/checkout-intents/",
headers={"Authorization": f"Bearer {token}"},
json={
"totalAmount": amount,
"initialAmount": amount,
"itemName": name,
"backUrl": back_url,
"errorUrl": error_url,
"returnUrl": return_url,
"containsDonation": contains_donation,
"metadata": metadata,
},
)
print(response.text)
response.raise_for_status()
return response.json()

View File

@ -238,7 +238,9 @@ else:
PHONENUMBER_DB_FORMAT = 'NATIONAL' PHONENUMBER_DB_FORMAT = 'NATIONAL'
PHONENUMBER_DEFAULT_REGION = 'FR' PHONENUMBER_DEFAULT_REGION = 'FR'
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") # Hello Asso API creds
HELLOASSO_CLIENT_ID = os.getenv('HELLOASSO_CLIENT_ID', 'CHANGE_ME_IN_ENV_SETTINGS')
HELLOASSO_CLIENT_SECRET = os.getenv('HELLOASSO_CLIENT_SECRET', 'CHANGE_ME_IN_ENV_SETTINGS')
# Custom parameters # Custom parameters
PROBLEMS = [ PROBLEMS = [