-
Notifications
You must be signed in to change notification settings - Fork 34
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
Implement subscriber sync from Paddle API #19
base: master
Are you sure you want to change the base?
Changes from all commits
68d9f03
14d1882
1e8b2cd
444dbdc
e371caf
ffef1a6
4ccee16
c68fe29
ff86a94
8102da8
a72e46f
0d2ac0d
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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
""" | ||
sync_subscribers_from_paddle command. | ||
""" | ||
from django.core.management.base import BaseCommand | ||
|
||
from ...models import Subscription | ||
|
||
|
||
class Command(BaseCommand): | ||
"""Sync subscribers from paddle.""" | ||
|
||
help = "Sync subscribers from paddle." | ||
|
||
def handle(self, *args, **options): | ||
"""Call sync_from_paddle_data for each subscriber returned by api_list.""" | ||
for sub_data in Subscription.api_list(): | ||
sub = Subscription.sync_from_paddle_data(sub_data) | ||
print("Synchronized {0}".format(str(sub))) | ||
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.
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 3.0.5 on 2020-04-10 00:56 | ||
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.
|
||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('djpaddle', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='subscription', | ||
name='next_bill_date', | ||
field=models.DateTimeField(blank=True, null=True), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Generated by Django 3.0.5 on 2020-04-10 04:55 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('djpaddle', '0002_auto_20200410_0056'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='subscription', | ||
name='checkout_id', | ||
field=models.TextField(), | ||
), | ||
migrations.AlterField( | ||
model_name='subscription', | ||
name='id', | ||
field=models.TextField(primary_key=True, serialize=False), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
from django.db.models.signals import post_save | ||
from django.utils.translation import gettext_lazy as _ | ||
from django.dispatch import receiver | ||
from django.utils import timezone | ||
|
||
from . import settings, signals, api | ||
from .fields import PaddleCurrencyCodeField | ||
|
@@ -123,8 +124,9 @@ class Subscription(PaddleBaseModel): | |
(STATUS_PAUSED, _("paused")), | ||
(STATUS_DELETED, _("deleted")), | ||
) | ||
PADDLE_URI_LIST = 'subscription/users' | ||
|
||
id = models.CharField(max_length=32, primary_key=True) | ||
id = models.TextField(primary_key=True) | ||
subscriber = models.ForeignKey( | ||
settings.DJPADDLE_SUBSCRIBER_MODEL, | ||
related_name="subscriptions", | ||
|
@@ -134,12 +136,13 @@ class Subscription(PaddleBaseModel): | |
) | ||
|
||
cancel_url = models.URLField() | ||
checkout_id = models.CharField(max_length=32) | ||
checkout_id = models.TextField() | ||
currency = models.CharField(max_length=3) | ||
email = models.EmailField() | ||
event_time = models.DateTimeField() | ||
marketing_consent = models.BooleanField() | ||
next_bill_date = models.DateTimeField() | ||
next_bill_date = models.DateTimeField( | ||
null=True, blank=True) | ||
passthrough = models.TextField() | ||
quantity = models.IntegerField() | ||
source = models.URLField() | ||
|
@@ -151,6 +154,51 @@ class Subscription(PaddleBaseModel): | |
class Meta: | ||
ordering = ["created_at"] | ||
|
||
@classmethod | ||
def api_list(cls): | ||
return api.retrieve(uri=cls.PADDLE_URI_LIST) | ||
|
||
@classmethod | ||
def sync_from_paddle_data(cls, data): | ||
pk = data.get('subscription_id', None) | ||
# First, find and drop current sub with this data. | ||
cls.objects.filter(pk=pk).delete() | ||
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. please initialize
|
||
kwargs = {} | ||
|
||
try: | ||
kwargs['subscriber'] = settings.get_subscriber_model().objects.get( | ||
email=data["user_email"] | ||
) | ||
except settings.get_subscriber_model().DoesNotExist: | ||
pass | ||
|
||
try: | ||
kwargs['plan'] = Plan.objects.get(pk=data.get("plan_id")) | ||
except Plan.DoesNotExist: | ||
print("Skipping, plan not found.") | ||
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.
|
||
return | ||
|
||
# Now, create object with this pk. | ||
sub = Subscription.objects.create( | ||
id=pk, | ||
cancel_url=data.get("cancel_url"), | ||
checkout_id="", # ??? | ||
currency=data.get('last_payment').get("currency"), | ||
email=data.get("user_email"), | ||
event_time=timezone.now(), # ??? | ||
marketing_consent=data.get('marketing_consent'), | ||
# next_bill_date can be null if user won't pay again... | ||
next_bill_date=data.get("next_payment", {}).get("date", None), | ||
passthrough="", # ??? | ||
quantity=data.get("last_payment", {}).get("amount", 0), | ||
source="", # ??? | ||
status=data.get("state"), | ||
unit_price=0.00, # ??? | ||
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. @matteing shall we rather make this a default in the model field? |
||
update_url = data.get('update_url'), | ||
**kwargs | ||
) | ||
return sub | ||
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.
|
||
|
||
@classmethod | ||
def _sanitize_webhook_payload(cls, payload): | ||
data = {} | ||
|
@@ -160,10 +208,14 @@ def _sanitize_webhook_payload(cls, payload): | |
# transform `user_id` to subscriber ref | ||
data["subscriber"] = None | ||
subscriber_id = payload.pop("user_id", None) | ||
if subscriber_id not in ["", None]: | ||
data["subscriber"], created = settings.get_subscriber_model().objects.get_or_create( | ||
email=payload["email"] | ||
) | ||
try: | ||
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 subscriber_id not in ["", None]: | ||
data[ | ||
"subscriber"] = settings.get_subscriber_model().objects.get( | ||
email=payload["email"] | ||
) | ||
except settings.get_subscriber_model().DoesNotExist: | ||
pass | ||
|
||
# transform `subscription_plan_id` to plan ref | ||
data["plan"] = None | ||
|
@@ -192,7 +244,7 @@ def from_subscription_created(cls, payload): | |
def update_by_payload(cls, payload): | ||
data = cls._sanitize_webhook_payload(payload) | ||
pk = data.pop("id") | ||
return cls.objects.update_or_create(pk, defaults=data) | ||
return cls.objects.update_or_create(pk=pk, defaults=data) | ||
|
||
def __str__(self): | ||
return "Subscription <{}:{}>".format(str(self.subscriber), str(self.id)) | ||
|
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.
subscription_data