From e4585a59a0772d2c21dcb3840c9a0fa585cd117c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radek=20Hol=C3=BD?= Date: Thu, 30 May 2024 18:23:44 +0200 Subject: [PATCH] Replace the subscription logic with a wallet logic as per https://github.com/jazzband/django-payments/pull/217#issuecomment-2122739440 --- payments/__init__.py | 12 ++++++++ payments/core.py | 16 +++++----- payments/models.py | 71 ++++++++++---------------------------------- 3 files changed, 34 insertions(+), 65 deletions(-) diff --git a/payments/__init__.py b/payments/__init__.py index 3ca8e04d9..443705a6b 100644 --- a/payments/__init__.py +++ b/payments/__init__.py @@ -61,6 +61,18 @@ class FraudStatus: ] +class WalletStatus: + PENDING = "pending" + ACTIVE = "active" + ERASED = "erased" + + CHOICES = [ + (PENDING, pgettext_lazy("wallet status", "Pending")), + (ACTIVE, pgettext_lazy("wallet status", "Active")), + (ERASED, pgettext_lazy("wallet status", "Erased")), + ] + + class RedirectNeeded(Exception): pass diff --git a/payments/core.py b/payments/core.py index 8b8ff0c97..703623886 100644 --- a/payments/core.py +++ b/payments/core.py @@ -9,6 +9,8 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.module_loading import import_string +from . import WalletStatus + if TYPE_CHECKING: from django.http import HttpRequest @@ -143,26 +145,22 @@ def get_return_url( return url + "?" + qs return url - def autocomplete_with_subscription(self, payment): + def autocomplete_with_wallet(self, payment): """ - Complete the payment with subscription + Complete the payment with wallet If the provider uses workflow such that the payments are initiated from implementer's side. The users of django-payments will create a payment and call - Payment.autocomplete_with_subscription() right before the subscription end. + Payment.autocomplete_with_wallet(). Throws RedirectNeeded if there is problem with the payment that needs to be solved by user. """ raise NotImplementedError - def cancel_subscription(self, subscription): - """ - Cancel subscription - Used by providers, that use provider initiated cancellation workflow - """ - raise NotImplementedError + def erase_wallet(self, wallet): + wallet.status = WalletStatus.ERASED def capture(self, payment, amount=None): raise NotImplementedError diff --git a/payments/models.py b/payments/models.py index e26da1fbb..9ef439153 100644 --- a/payments/models.py +++ b/payments/models.py @@ -1,6 +1,5 @@ from __future__ import annotations -import enum import json from typing import Iterable from uuid import uuid4 @@ -13,6 +12,7 @@ from . import FraudStatus from . import PaymentStatus from . import PurchasedItem +from . import WalletStatus from .core import provider_factory @@ -40,58 +40,29 @@ def __setattr__(self, key, value): return None -class BaseSubscription(models.Model): +class BaseWallet(models.Model): token = models.CharField( - _("subscription token/id"), - help_text=_("Token/id used to identify subscription by provider"), + _("wallet token/id"), + help_text=_("Token/id used to identify wallet by provider"), max_length=255, default=None, null=True, blank=True, ) - payment_provider = models.CharField( - _("payment provider"), - help_text=_("Provider variant, that will be used for payment renewal"), - max_length=255, - default=None, - null=True, - blank=True, + status = models.CharField( + max_length=10, choices=WalletStatus.CHOICES, default=WalletStatus.PENDING ) - subscribtion_data = models.JSONField( - _("subscription data"), - help_text=_("Provider-specific data associated with subscription"), + extra_data = models.JSONField( + _("extra data"), + help_text=_("Provider-specific data associated with wallet"), default=dict, ) - class TimeUnit(enum.Enum): - year = "year" - month = "month" - day = "day" - - def set_recurrence(self, token: str, **kwargs): - """ - Sets token and other values associated with subscription recurrence - Kwargs can contain provider-specific values - """ - self.token = token - self.subscribtion_data = kwargs - - def get_period(self) -> int: - raise NotImplementedError - - def get_unit(self) -> TimeUnit: - raise NotImplementedError - - def cancel(self): + def payment_completed(self, payment): """ - Cancel the subscription by provider - Used by providers, that use provider initiated subscription workflow - Implementer is responsible for cancelling the subscription model - - Raises PaymentError if the cancellation didn't pass through + Concrete implementation specific logic called whenever a payment is completed + using this wallet. """ - provider = provider_factory(self.variant) - provider.cancel_subscription(self) class Meta: abstract = True @@ -251,30 +222,18 @@ def get_payment_url(self) -> str: """ raise NotImplementedError - def get_subscription(self) -> BaseSubscription | None: - """ - Returns subscription object associated with this payment - or None if the payment is not recurring - """ - return None - - def is_recurring(self) -> bool: - return self.get_subscription() is not None - - def autocomplete_with_subscription(self): + def autocomplete_with_wallet(self): """ - Complete the payment with subscription + Complete the payment with wallet If the provider uses workflow such that the payments are initiated from implementer's side. - Call this function right before the subscription end to - make a new subscription payment. Throws RedirectNeeded if there is problem with the payment that needs to be solved by user """ provider = provider_factory(self.variant) - provider.autocomplete_with_subscription(self) + provider.autocomplete_with_wallet(self) def capture(self, amount=None): """Capture a pre-authorized payment.