-
-
Notifications
You must be signed in to change notification settings - Fork 282
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
(WIP) PayPal subscribtions #274
base: main
Are you sure you want to change the base?
Changes from all commits
6bd1ef1
6de5e33
98aa87b
285a836
b8360f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
import enum | ||
import json | ||
from typing import Iterable | ||
from typing import Optional | ||
from typing import Union | ||
from uuid import uuid4 | ||
|
||
|
@@ -36,6 +38,60 @@ def __setattr__(self, key, value): | |
self._payment.extra_data = json.dumps(data) | ||
|
||
|
||
class BaseSubscription(models.Model): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand correctly, applications need to provide a subclass of this model, correct? Can you add a brief docstring mentioning this? |
||
token = models.CharField( | ||
_("subscription token/id"), | ||
help_text=_("Token/id used to identify subscription 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, | ||
) | ||
|
||
class TimeUnit(enum.Enum): | ||
year = "year" | ||
month = "month" | ||
day = "day" | ||
Comment on lines
+59
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps you're looking for https://github.com/jazzband/django-recurrence/? |
||
|
||
def get_token(self) -> str: | ||
return self.token | ||
|
||
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 | ||
|
||
def get_period(self) -> int: | ||
raise NotImplementedError() | ||
Comment on lines
+74
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what this is supposed to return either. |
||
|
||
def get_unit(self) -> TimeUnit: | ||
raise NotImplementedError() | ||
|
||
def cancel(self): | ||
""" | ||
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 | ||
""" | ||
provider = provider_factory(self.variant) | ||
provider.cancel_subscription(self) | ||
|
||
class Meta: | ||
abstract = True | ||
|
||
|
||
class BasePayment(models.Model): | ||
""" | ||
Represents a single transaction. Each instance has one or more PaymentItem. | ||
|
@@ -144,6 +200,33 @@ def get_success_url(self) -> str: | |
def get_process_url(self) -> str: | ||
return reverse("process_payment", kwargs={"token": self.token}) | ||
|
||
def get_payment_url(self) -> str: | ||
""" | ||
Get the url the view that handles the payment (payment_details() in documentation) | ||
For now used only by PayU provider to redirect users back to CVV2 form | ||
""" | ||
raise NotImplementedError() | ||
|
||
def get_subscription(self) -> Optional[BaseSubscription]: | ||
""" | ||
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): | ||
""" | ||
Complete the payment with subscription | ||
Used by providers, that use server initiated subscription workflow | ||
|
||
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) | ||
|
||
def capture(self, amount=None): | ||
if self.status != PaymentStatus.PREAUTH: | ||
raise ValueError("Only pre-authorized payments can be captured.") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what this method should do.
What does
autocomplete
mean in this context?