Code Monkey home page Code Monkey logo

dj-paddle's Introduction

dj-paddle

PyPI Travis Documentation Supported Python versions License

Django + Paddle Made Easy

(this project is heavily inspired by dj-stripe)

Introduction

dj-paddle implements Paddle models (currently Subscription only), for Django. Set up your webhook and start receiving model updates. You will then have a copy of all Paddle subscriptions available in Django, no API traffic required!

The full documentation is available at https://dj-paddle.readthedocs.io.

Features

  • Django Signals for all incoming webhook events from paddle
  • Subscriptions

Requirements

  • Django >= 2.1
  • Python >= 3.5

Quickstart

Install dj-paddle:

pip install dj-paddle

Add djpaddle to your INSTALLED_APPS:

INSTALLED_APPS =(
    ...
    "djpaddle",
    ...
)

Add to urls.py:

path("paddle/", include("djpaddle.urls", namespace="djpaddle")),

Tell paddle about the webhook (paddle webhook docs can be found here) using the full URL of your endpoint from the urls.py step above (e.g. https://example.com/paddle/webhook/).

Add your paddle keys and set the operating mode:

# can be found at https://vendors.paddle.com/authentication
DJPADDLE_VENDOR_ID = '<your-vendor-id>'

# create one at https://vendors.paddle.com/authentication
DJPADDLE_API_KEY = '<your-api-key>'

# can be found at https://vendors.paddle.com/public-key
DJPADDLE_PUBLIC_KEY = '<your-public-key>'

# More info at https://developer.paddle.com/getting-started/sandbox
DJPADDLE_SANDBOX = False

djpaddle includes vendor_id and sandbox template context processors which adds your vendor ID as DJPADDLE_VENDOR_ID and if you want to use the sandbox as DJPADDLE_SANDBOX to each template context:

TEMPLATES = [
{
    ...
    'OPTIONS': {
        ...
        'context_processors': [
            ...
            'djpaddle.context_processors.vendor_id',
            'djpaddle.context_processors.sandbox',
            ...
        ]
    }
}

Run the commands:

python manage.py migrate

# fetches all subscription plans from paddle
python manage.py djpaddle_sync_plans_from_paddle

Paddle Checkout

Next to setup a PaddleJS checkout page

First load in PaddleJS and initialise it by including the dj-paddle PaddleJS template in your own template to load PaddleJS:

{% include "djpaddle_paddlejs.html" %}

Next add a Paddle product or subscription plan into the page context. Below is an example of how to do this using a class based view where plan_id is passed through as a value from the URL:

from django.conf import settings
from django.views.generic import TemplateView
from djpaddle.models import Plan


class Checkout(TemplateView):
    template_name = 'checkout.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['paddle_plan'] = Plan.objects.get(pk=kwargs['plan_id'])
        # If you have not added 'djpaddle.context_processors.vendor_id' as a template context processors
        context['DJPADDLE_VENDOR_ID'] = settings.DJPADDLE_VENDOR_ID
        # If you have not added 'djpaddle.context_processors.sandbox' as a template context processors
        context['DJPADDLE_SANDBOX'] = settings.DJPADDLE_SANDBOX
        return context

Finally put a Buy Now! button for the plan subscription you added to the context:

<a href="#!" class="paddle_button" data-product="{{ paddle_plan.id }}">Buy Now!</a>

You can pass data to Paddle JS by add data attributes to the button. For example to set the users email you can use the data-email attribute:

<a href="#!" class="paddle_button" data-product="{{ paddle_plan.id }}" data-email="{{ user.email }}" >Buy Now!</a>

A full list of parameters can be found on the PaddleJS parameters page

For more information about options on what to do after a successful checkout please see our Checkout success documentation

Subscription model

You can override the model that subscriptions are attached to using the DJPADDLE_SUBSCRIBER_MODEL setting. This setting must use the string model reference in the style 'app_label.ModelName'.

The model chosen must have an email field.

# Defaults to AUTH_USER_MODEL
DJPADDLE_SUBSCRIBER_MODEL = 'myapp.MyModel'

Warning: To use this setting you must have already created and ran the initial migration for the app/model before adding djpadding to INSTALLED_APPS.

Reporting Security Issues

Please do not report security issues in public, but email the authors directly.

dj-paddle's People

Contributors

adamchainz avatar ekmecic avatar fpurchess avatar kennell avatar matteing avatar pyepye avatar timhanlon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dj-paddle's Issues

djpaddle_sync_plans_from_paddle errors out with Paddle error 107 - You don't have permission to access this resource

dj-paddle version: 0.1.2

user@b52850d97f85:/app$ ./manage.py djpaddle_sync_plans_from_paddle
Traceback (most recent call last):
  File "/app/./manage.py", line 22, in <module>
    main()
  File "/app/./manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 402, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.10/site-packages/djpaddle/management/commands/djpaddle_sync_plans_from_paddle.py", line 16, in handle
    for plan_data in Plan.api_list():
  File "/usr/local/lib/python3.10/site-packages/djpaddle/models.py", line 55, in api_list
    return paddle_client.list_plans()
  File "/usr/local/lib/python3.10/site-packages/paddle/_plans.py", line 19, in list_plans
    return self.post(url=url)
  File "/usr/local/lib/python3.10/site-packages/paddle/paddle.py", line 182, in post
    return self.request(**kwargs)
  File "/usr/local/lib/python3.10/site-packages/paddle/paddle.py", line 154, in request
    raise PaddleException(response_json['error'])
paddle.paddle.PaddleException: Paddle error 107 - You don't have permission to access this resource

I'm trying to test paddle integration with the sandbox environment. I have set the DJPADDLE_VENDOR_ID, DJ_PADDLE_API_KEY (the default one already created in the sandbox), DJPADDLE_PUBLIC_KEY with values from the sandbox-vendors.paddle.com. I've also set DJPADDLE_SANDBOX=True.

Codecov migration to marketplace app

Hi, Tom from Codecov here.

We noticed that you are using Codecov with fairly high frequency, and we’re so excited to see that! However, because you are not using our app, you may have experienced issues with uploading reports or viewing coverage information. This is due to rate-limiting issues from GitHub.

In order to prevent any future outages, we ask that you move over to our GitHub app integration.

The process is extremely simple and shouldn’t require more than a few clicks, and you should not expect any downtime. By moving to our app, you will no longer need an admin or separate account to manage the relationship with GitHub as the team bot.

Let me know if you have any questions, or if I can help at all with this process.

cancellation_effective_date missing in subscription model

Paddle provides a cancellation_effective_date field which is currently missing in the subscription model:
https://developer.paddle.com/webhook-reference/3c0355dc446b0-subscription-cancelled
"The date the cancellation should come into effect, taking the customer’s most recent payment into account. The customer should be able to use the service they've subscribed to up until this date."

This is an important field because it is otherwise unknown how long the service should still be provided after cancellation.

Required line to be added in models.py under 'class Subscription(PaddleBaseModel):':
cancellation_effective_date = models.DateTimeField(null=True, blank=True)

Race condition when listening on both `subscription_payment_succeeded` and `subscription_created` for new subscriptions

  • dj-paddle version:
  • 0.1.2

Description

After upgrading to 0.1.2 I started to see errors with every new subscription:

IntegrityError at /paddle/webhook/
(1062, "Duplicate entry 'xxxxxxx' for key 'PRIMARY'")

It turns out the problem is that now both subscription_payment_succeeded and subscription_created try to create a new subscription entry in the database in parallel. subscription_payment_succeeded was added to the subscription_event with 0.1.2.

The code documentation for Subscription does not mention subscription_payment_succeeded as possible trigger.

Fortunately, the problem is not severe, as the first webhook invocation (subscription_created) will succeed at creating the subscription. The second call will fail with no consequence, the retry from Paddle will then succeed when the Subscription instance can already be found in the database.

A get_or_create should solve the problem here:

        try:
            subscription = cls.objects.get(pk=pk)
        except cls.DoesNotExist:
            return cls.objects.create(pk=pk, **data)

Configurable Stale Linking Fields

Description

Ability to set custom linking fields for stale records. For example passthrough on Paddle side and customer_id on the app model side. It defaults to email on both sides, like the current code.

Implementation here: chrisgrande@ebdfaac

I had trouble getting the tests to run locally so I didn't touch them at the moment.

Thoughts?

Latest version needs to be released on PyPi

  • dj-paddle version:
  • Django version:
  • Python version:
  • Operating System:

Description

The latest version with the Checkout ID extended to 64 Chars is not on PyPy - it's still installing the old version.

What I Did

pip install dj-paddle

Sandbox webhook/sync failures

  • dj-paddle version: 0.1.2
  • Django version: 3.1.5
  • Python version: 3.8
  • Operating System: Manjaro Linux

Description

I am trying to use the sandbox for development.

  • I have configured my Sandbox with Plans
  • I have set DJPADDLE_SANDBOX to True
  • I have set both DJPADDLE_API_KEY and DJPADDLE_VENDOR_ID to the Sandbox values

What goes wrong:

  1. djpaddle_sync_plans_from_paddle fails with paddle.paddle.PaddleException: Paddle error 107 - You don't have permission to access this resource
  2. Webhooks return with a webhook validation failed (both the Webhook simulator, and when using the checkout)

What I expect:

  • Plans can sync
  • Webhook verification passes

What I Did

Notes:

  • The sync and webhooks work when using the production (non-Sandbox) paddle account.
  • I am using a React frontend but am able to successfully render both Sandbox and Production checkout's when I pass in the Vendor and Product id's for either.

For clarity what my script tags look like when using React:

// when using Prod, sandbox must be removed/commented out
    <script src="https://cdn.paddle.com/paddle/paddle.js"></script>
    <script type="text/javascript">
      Paddle.Setup({ vendor: xxxxxx, debug: true });
      Paddle.Environment.set('sandbox');
    </script>

Also, I copied and pasted in the is_valid_webhook and created my own webhook endpoint to figure out what's going wrong. The is_valid_webhook is returning a False when I use the sandbox but is True in production.

The verifier.verify(digest, signature) is failing, and returns a False.

Last point

I have tested the sandbox against paddle-client and it works. I can list plans, users, etc.


Tracebacks and responses below

Webhook Response

Bad Request: /paddle/webhook/
INFO:     172.23.0.1:50524 - "POST /paddle/webhook/ HTTP/1.1" 400 Bad Request

Sync traceback

root@5a4ce255489d:/app# python manage.py djpaddle_sync_plans_from_paddle
Traceback (most recent call last):
  File "manage.py", line 31, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.8/site-packages/djpaddle/management/commands/djpaddle_sync_plans_from_paddle.py", line 16, in handle
    for plan_data in Plan.api_list():
  File "/usr/local/lib/python3.8/site-packages/djpaddle/models.py", line 55, in api_list
    return paddle_client.list_plans()
  File "/usr/local/lib/python3.8/site-packages/paddle/_plans.py", line 19, in list_plans
    return self.post(url=url)
  File "/usr/local/lib/python3.8/site-packages/paddle/paddle.py", line 182, in post
    return self.request(**kwargs)
  File "/usr/local/lib/python3.8/site-packages/paddle/paddle.py", line 154, in request
    raise PaddleException(response_json['error'])
paddle.paddle.PaddleException: Paddle error 107 - You don't have permission to access this resource

Fin

I hope that is enough information to be helpful, and also, not a waste of your time.

Thanks for providing a great open-source package!

checkout_id is not long enough

  • dj-paddle version: 0.1.1
  • Django version: 3.1
  • Python version: 3.7
  • Operating System: Linux

Description

Subscription checkout_id has max_length of 32. However Paddle can send checkout_id's that are longer than that. In my instance I got a 35 character id.

Add post_save signals to Subscriber models

A great idea would be to add post_save signals to subscriber models, so for example instead of working with raw request payloads in signals we can instead just use an instance of Subscriber passed to the signal.

Subscriptions have no cancel url

  • dj-paddle version: 0.1.2
  • Django version:.4.0.2
  • Python version: 3.9.4

Description

We were quite suprised that out of 7 subscription only 2 have a cancel_url attached to it (the same 2 are the only ones with an update_url). Our cancel UX depends on this to be defined so I was wondering when the cancel_url should be created and how I could force a resync.

Subscription Created webhook silently fails

  • dj-paddle version: 0.1.0.dev3
  • Django version: 2.1.15
  • Python version: 3.7
  • Operating System: OS X

Description

Webhook for Subscription Created silently fails.

What I Did

Using Paddle's Webhook Alert Testing to send a Subscription Created webhook returns a 200 OK, but fails to create objects for djpaddle.subscription or the subscriber model.

I have a branch ready here which successfully creates the objects.

mock Paddle API

In order to be able to extensively test the API integration, we need to decide on and implement a way of mocking the Paddle API.

Uppercase letters in user's email address causes webhook to fail on subscription creation

  • dj-paddle version: 0.1.2
  • Django version: 4.2.3
  • Python version: 3.9.9
  • Operating System: Linux

Description

In production, we had an error when a user created a subscription and a webhook was sent:

"Subscriber could not be found for subscription <subscription_id> with payload".

We narrowed down the problem to a case sensitivity issue. Specifically if a user registered on our site with an uppercase letter and then tried to subscribe, the Paddle JS front end would lowercase the email address by default. When the subscription_created webhook was fired, the mapping would fail to find the associated user and they wouldn't be successfully marked as having a paid subscription. Unfortunately this affected our very first paying customer - talk about bad luck!

What I Did

  • Create a user like '[email protected]' with an uppercase letter in their email address.
  • Try to initiate a subscription using the Paddle UI
  • Note that the UI will default to lowercasing their email address (you can also probably just manually change the casing in the Paddle modal).
  • Complete the payment
  • You get an error when the subscription_created webhook is fired stating that the user is not found.

Workaround

You can override the default mapping by setting the settings param DJPADDLE_SUBSCRIBER_BY_PAYLOAD. Then you can replace the default djpaddle.mappers.subscriber_by_payload() and make sure you're doing a case-insensitive search.

Fix

I believe the user search should be case-insensitive, since subscriptions_by_subscriber() is also doing a case-insensitive search. I'll try to submit a pull request.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.