Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payment review #67

Merged
merged 62 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
814ce48
adding payment application
ShiroUsagi-san Sep 6, 2023
4a16881
adding transaction model 1/n
ShiroUsagi-san Sep 6, 2023
c72c47d
starting the payment view
Leyknn Sep 12, 2023
c94c670
implement token class, managing access token to helloasso
Leyknn Sep 12, 2023
9d46cd6
correcting some bugs on the pay method
Leyknn Sep 12, 2023
5df59dc
added product model, work on views
Leyknn Sep 28, 2023
f39c06d
fixed tokens and init url
Leyknn Sep 28, 2023
57859cb
added static urls object
Leyknn Sep 28, 2023
0d5ec99
adding more routes, add requirements requests, adding Models (not
ShiroUsagi-san Oct 9, 2023
e529063
struggling against the serializer (not working yet)
ShiroUsagi-san Oct 9, 2023
a76b5cf
trying to debug pay View
ShiroUsagi-san Oct 11, 2023
5f1623b
this commit adds:
ShiroUsagi-san Oct 12, 2023
369bdd6
adding verbose_name
ShiroUsagi-san Oct 12, 2023
08b5869
Fixing typo in tokens.py, correct amount computation, add ordering in
ShiroUsagi-san Oct 13, 2023
6b1adce
⬆️ Add admin models for payment, centralize amount
Lymkwi Oct 13, 2023
2bdb165
cleaning code
ShiroUsagi-san Oct 13, 2023
b7b7e0b
forget ()
ShiroUsagi-san Oct 13, 2023
0cbdc3b
clarify doc
ShiroUsagi-san Oct 13, 2023
c0b51a4
Remove adding/editing/deleting abilities from the admin panel
ShiroUsagi-san Oct 13, 2023
39e0731
🥖translating
ShiroUsagi-san Oct 13, 2023
eac5b84
Modify product M2M to use a through field
Lymkwi Oct 13, 2023
dcd79a5
Implement basic handling of payment return
Lymkwi Oct 14, 2023
9c6df4d
💰 Formatting of payment models file
Lymkwi Oct 15, 2023
696c5cb
💸 Introduct more product fields
Lymkwi Oct 15, 2023
2170196
💶 Introduce the product category hook system
Lymkwi Oct 15, 2023
2c540c8
🫰 Show new fields in the admin view of a product
Lymkwi Oct 15, 2023
2c31f3d
🪙 Execute product callback hooks on payment
Lymkwi Oct 15, 2023
35e0e9a
📒 Several tournament models modifications
Lymkwi Oct 15, 2023
0b724a3
🔄 Move payment callbacks in Transaction
Lymkwi Oct 15, 2023
03e83b4
🌐 Use HELLOASSO_HOSTNAME to compute CSRF URL
Lymkwi Oct 15, 2023
2ea997c
📜 Set root logger level based on DEBUG status
Lymkwi Oct 15, 2023
8929620
🎟️ Refactor and document the HA OAuth2 token class
Lymkwi Oct 15, 2023
6bf2727
FIX: default to localhost for helloasso hostname
Lymkwi Oct 15, 2023
6d7781c
Creating associated products
ShiroUsagi-san Oct 19, 2023
42e2a16
Writing test and fixing broken test
ShiroUsagi-san Oct 19, 2023
bbc7eb5
Use the is_announced field (#81)
ShiroUsagi-san Oct 20, 2023
6e51076
🎟️ Link a ticket with a tournament
Lymkwi Oct 20, 2023
21972a7
💰 Add cashprizes to the Tournament fields
Lymkwi Oct 20, 2023
803f01b
📅 Add product and tournament expiry/origin times
Lymkwi Oct 20, 2023
d8cd512
❌ Do not reuse args/kwargs in second save
Lymkwi Oct 20, 2023
c4f0331
🌍 Translate tiem check validation errors
Lymkwi Oct 20, 2023
dee6811
🏆 Add a method to compute the validity of a Team
Lymkwi Oct 23, 2023
ddd7423
🛑 Add django admin action to refund a transaction
Lymkwi Oct 23, 2023
644d6e8
Remove usage of `%` string formatting
Lymkwi Oct 25, 2023
447e2b8
Formatting and proper errors
Lymkwi Oct 25, 2023
7c24b9e
Improve Token class, add refresh on demand
Lymkwi Oct 25, 2023
53a4f7e
Clean up HA vars: stop reading env all day long
Lymkwi Oct 25, 2023
2e0f90e
Display all fields in a serialized tournament
Lymkwi Oct 25, 2023
07a7ea2
Foundations of the notification system
Lymkwi Oct 25, 2023
2539fa3
Fix #74 by matching external port to protocol
Lymkwi Oct 25, 2023
4c4ac80
Fix tests and data leak in event deref route
Lymkwi Oct 25, 2023
ba5ff57
Payment return routes moved to the back
Lymkwi Oct 25, 2023
e58831e
Refactor tournament payment hooks, add prepare
Lymkwi Oct 26, 2023
8b3de70
Payment admin: add Payment admin view
Lymkwi Oct 26, 2023
76c8bfb
Introduct order_id into Transaction
Lymkwi Oct 26, 2023
419b296
Fix initial HA token retrieval using wrong grant
Lymkwi Oct 26, 2023
ad23c69
Implement proper use of prepare_hook + redirect
Lymkwi Oct 26, 2023
e0120a0
Implement the notification system + refund
Lymkwi Oct 26, 2023
41645af
Properly handle unknown transactions notified
Lymkwi Oct 26, 2023
3de3597
Remove refund action, normalize state methods
Lymkwi Oct 26, 2023
f777427
Re-add use of `%` for gettext_lazy interpolation
Lymkwi Oct 26, 2023
9a45e6e
Fix typo in tourney serializer RO fields
Lymkwi Oct 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added insalan/payment/__init__.py
Empty file.
103 changes: 103 additions & 0 deletions insalan/payment/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Payment Admin Panel Code"""
# Disable lints:
# "Too few public methods"
# pylint: disable=R0903

from django import forms
from django.contrib import admin, messages

from django.utils.translation import gettext_lazy as _

from .models import Product, Transaction, Payment


class ProductAdmin(admin.ModelAdmin):
"""Admin handler for Products"""

list_display = ("id", "price", "name", "desc", "category", "associated_tournament")
search_fields = ["id", "price", "name"]


admin.site.register(Product, ProductAdmin)


@admin.action(description=_("Rembourser la transaction"))
def reimburse_transactions(modeladmin, request, queryset):
"""Reimburse all selected actions"""
for transaction in queryset:
(is_err, msg) = transaction.refund_transaction(request.user.username)
if is_err:
modeladmin.message_user(
request, _("Erreur: %(msg)s") % {"msg": msg}, messages.ERROR
)
break


class PaymentAdmin(admin.ModelAdmin):
"""
Admin handler for payments"
"""

list_display = ("id", "amount", "transaction")

def has_add_permission(self, _request):
"""Remove the ability to add a payment from the backoffice"""
return False

def has_change_permission(self, _request, _obj=None):
"""Remove the ability to edit a payment from the backoffice"""
return False

def has_delete_permission(self, _request, _obj=None):
"""Remove the ability to edit a payment from the backoffice"""
return False


admin.site.register(Payment, PaymentAdmin)


class TransactionAdmin(admin.ModelAdmin):
"""
Admin handler for Transactions
In the backoffice, Transactions can only be seen, they cannot be add,
removed or changed this way
"""

list_display = (
"id",
"payer",
"payment_status",
"creation_date",
"intent_id",
"order_id",
"last_modification_date",
"amount",
)
search_fields = [
"id",
"payer",
"products",
"payment_status",
"creation_date",
"intent_id",
"order_id",
"last_modification_date",
"amount",
]

# actions = [reimburse_transactions]

def has_add_permission(self, _request):
"""Remove the ability to add a transaction from the backoffice"""
return False

def has_change_permission(self, _request, _obj=None):
"""Remove the ability to edit a transaction from the backoffice"""
return False

def has_delete_permission(self, _request, _obj=None):
"""Remove the ability to edit a transaction from the backoffice"""
return False


admin.site.register(Transaction, TransactionAdmin)
8 changes: 8 additions & 0 deletions insalan/payment/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _


class PaymentConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name ='insalan.payment'
verbose_name = _('Paiement')
118 changes: 118 additions & 0 deletions insalan/payment/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
Hooks module for payment

This file is not part of the Django rest framework. Rather, it is a component
designed for other components of the application to come and register custom
handlers for payment success or failure.
"""

import logging

from django.utils.translation import gettext_lazy as _

from .models import ProductCategory


class PaymentCallbackSystem:
"""
Interface to register your hooks with, and get them
"""

# The dictionary of hooks
__HOOKS = {}
__logger = logging.getLogger("insalan.payment.hooks.PaymentCallbackSystem")

@classmethod
def register_handler(cls, prodcat, handler, overwrite=False):
"""
Register a handler for a product category

You give it a product (any instance of
`payment.models.ProductCategory`), and a handler (any subclass of
`payment.hooks.PaymentHooks`).

Overwriting an existing hook will trigger an error, unless you set
`overwrite=False`.
"""

# Verify arguments
# 1. product is an instance of Product
if not isinstance(prodcat, ProductCategory):
raise ValueError(_("Produit qui n'est pas une instance de Produit"))

if not issubclass(handler, PaymentHooks):
raise ValueError(_("Descripteur qui ne dérive pas de PaymentHooks"))

if cls.__HOOKS.get(prodcat):
if overwrite:
cls.__logger.warning(
"Overwriting handler for product category %s", prodcat
)
else:
raise ValueError(_(f"Descripteur déjà défini pour {prodcat}"))

cls.__HOOKS[prodcat] = handler

@classmethod
def retrieve_handler(cls, prodcat):
"""
Retrieve a handler class for a product category

If a handler is not registered, returns None
"""
return cls.__HOOKS.get(prodcat)


# Base class/interface
class PaymentHooks:
"""
Payment Hooks Class

This is a base class that must be derived by all hooks implementers, who
will then implement their way of handling payment success and failure.
"""

@staticmethod
def prepare_transaction(_transaction, _product, _count) -> bool:
"""
Prepare things that may have to be created prior to payment

Arguments are the preliminary transaction, product and count.
This hook is ran by the payment view exactly right before forwarding the
user to HelloAsso.
"""

@staticmethod
def payment_success(_transaction, _product, _count):
"""
Payment Success Handler

This method handles the process of validating a transaction, with the
transaction object, product and count given.
By that point, you can safely assume that the payment succeeded, and
that the `.payment_status` field of the transaction is set to
`SUCCEEDED`.
"""

@staticmethod
def payment_failure(_transaction, _product, _count):
"""
Payment Failure Handler

This method handles the process of cleaning up after a failed
transaction. By this point you can safely assume that the payment failed
and that `.payment_status` on the transaction object is set to `FAILED`.
"""

@staticmethod
def payment_refunded(_transaction, _product, _count):
"""
Payment Refund Handler

This method handles the process of cleaning up after a refund of a
transaction. By this point, you can safely assume that the payment has
been refunded on the side of helloasso.
"""


# vim: set tw=80:
Empty file.
Loading
Loading