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

Replace the subscription logic with a wallet logic #5

Merged
merged 1 commit into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions payments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
16 changes: 7 additions & 9 deletions payments/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
71 changes: 15 additions & 56 deletions payments/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import enum
import json
from typing import Iterable
from uuid import uuid4
Expand All @@ -13,6 +12,7 @@
from . import FraudStatus
from . import PaymentStatus
from . import PurchasedItem
from . import WalletStatus
from .core import provider_factory


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
Loading