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:
parent
98d04b9093
commit
b3555a7807
@ -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,))
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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"),
|
||||||
|
@ -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
86
tfjm/helloasso.py
Normal 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()
|
@ -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 = [
|
||||||
|
Loading…
Reference in New Issue
Block a user