Documentation for the payment provider plugin API

This commit is contained in:
Raphael Michel
2015-03-15 19:48:42 +01:00
parent 41f816388b
commit 13f88346d4
6 changed files with 274 additions and 69 deletions
+103 -5
View File
@@ -8,21 +8,119 @@ In this document, we will walk through the creation of a payment provider plugin
Please read :ref:`Creating a plugin <pluginsetup>` first, if you haven't already.
The signal
----------
Provider registration
---------------------
The payment provider API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available payment providers. Your plugin
should listen for this signal and return the subclass of ``pretix.base.payment.PaymentProvider``
should listen for this signal and return the subclass of ``pretix.base.payment.BasePaymentProvider``
that we'll soon create::
from django.dispatch import receiver
from pretix.base.signals import register_payment_providers
from .payment import BankTransfer
from .payment import Paypal
@receiver(register_payment_providers)
def register_payment_provider(sender, **kwargs):
return BankTransfer
return Paypal
The provider class
------------------
.. class:: pretix.base.payment.BasePaymentProvider
The central object of each payment provider is the subclass of ``BasePaymentProvider``
we already mentioned above. In this section, we will discuss it's interface in detail.
.. py:attribute:: BasePaymentProvider.event
The default constructor sets this property to the event we are currently
working for.
.. py:attribute:: BasePaymentProvider.settings
The default constructor sets this property to a ``SettingsSandbox`` object. You can
use this object to store settings using its ``get`` and ``set`` methods. All settings
you store are transparently prefixed, so you get your very own settings namespace.
.. autoattribute:: identifier
This is an abstract attribute, you **must** override this!
.. autoattribute:: verbose_name
This is an abstract attribute, you **must** override this!
.. autoattribute:: is_enabled
.. automethod:: calculate_fee
.. autoattribute:: settings_form_fields
.. automethod:: checkout_form_render
.. automethod:: checkout_form
.. autoattribute:: checkout_form_fields
.. automethod:: checkout_prepare
.. automethod:: checkout_is_valid_session
.. automethod:: checkout_confirm_render
This is an abstract method, you **must** override this!
.. automethod:: checkout_perform
.. automethod:: order_pending_render
This is an abstract method, you **must** override this!
.. automethod:: order_paid_render
Additional views
----------------
For most simple payment providers it is more than sufficient to implement
some of the :py:class:`BasePaymentProvider` methods. However, in some cases
it is necessary to introduce additional views. One example is the PayPal
provider. It redirects the user to a paypal website in the
:py:meth:`BasePaymentProvider.checkout_prepare`` step of the checkout process
and provides PayPal with an URL to redirect back to. This URL points to a
view which looks roughly like this::
@login_required
def success(request):
pid = request.GET.get('paymentId')
payer = request.GET.get('PayerID')
# We stored some information in the session in checkout_prepare(),
# let's compare the new information to double-check that this is about
# the same payment
if pid == request.session['payment_paypal_id']:
# Save the new information to the user's session
request.session['payment_paypal_payer'] = payer
try:
# Redirect back to the confirm page. We chose to save the
# event ID in the user's session. We could also put this
# information into an URL parameter.
event = Event.objects.current.get(identity=request.session['payment_paypal_event'])
return redirect(reverse('presale:event.checkout.confirm', kwargs={
'event': event.slug,
'organizer': event.organizer.slug,
}))
except Event.DoesNotExist:
pass # TODO: Display error message
else:
pass # TODO: Display error message
If you do not want to provide a view of your own, you could even let PayPal
redirect directly back to the confirm page and handle the query parameters
inside :py:meth:`BasePaymentProvider.checkout_is_valid_session``. However,
because some external providers (not PayPal) force you to have a *constant*
redirect URL, it might be necessary to define custom views.
+55 -18
View File
@@ -13,23 +13,45 @@ require two steps to install:
* Add it to the ``INSTALLED_APPS`` setting of Django in ``pretix/settings.py``
* Perform database migrations by using ``python manage.py migrate``
The communication between pretix and the plugins happens via Django's
`signal dispatcher`_ pattern. The core modules of pretix, ``pretixbase``,
The communication between pretix and the plugins happens mostly using Django's
`signal dispatcher`_ feature. The core modules of pretix, ``pretixbase``,
``pretixcontrol`` and ``pretixpresale`` expose a number of signals which are documented
on the next pages.
.. _`pluginsetup`:
Creating a plugin
-----------------
To create a new plugin, create a new python package which must be a vaild `Django app`_
and must contain plugin metadata, as described below.
To create a new plugin, create a new python package.
The following pages go into detail about the several types of plugins currently
supported. While these instructions don't assume that you know a lot about pretix,
they do assume that you have prior knowledge about Django (e.g. it's view layer,
how it's ORM works, etc.).
Inside your newly created folder, you'll probably need the three python modules ``__init__.py``,
``models.py`` and ``signals.py``, although this is up to you. You can take the following
example, taken from the time restriction module (see next chapter) as a template for your
``__init__.py`` module::
Plugin metadata
---------------
The plugin metadata lives inside a ``PretixPluginMeta`` class inside your app's
configuration class. The metadata class must define the following attributes:
``type`` (``pretix.base.plugins.PluginType``):
The type of plugin. Currently available: ``RESTRICTION``, ``PAYMENT``
``name`` (``str``):
The human-readable name of your plugin
``author`` (``str``):
Your name
``version`` (``str``):
A human-readable version code of your plugin
``description`` (``str``):
A more verbose description of what your plugin does.
A working example would be::
# file: pretix/plugins/timerestriction/__init__.py
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
from pretix.base.plugins import PluginType
@@ -48,21 +70,36 @@ example, taken from the time restriction module (see next chapter) as a template
"of a given item or variation to a certain timeframe " +
"or change its price during a certain period.")
def ready(self):
from . import signals # NOQA
default_app_config = 'pretix.plugins.timerestriction.TimeRestrictionApp'
.. IMPORTANT::
You have to implement a ``PretixPluginMeta`` class like in the example to make your
plugin available to the users.
Currently, the ``PluginType`` enum has the following values defined:
Signals
-------
* ``RESTRICTION``
* ``PAYMENT``
The various components of pretix define a number of signals which your plugin can
listen for. We will go into the details of the different signals in the following
pages. We suggest that you put your signal receivers into a ``signals`` submodule
of your plugin. You should extend your ``AppConfig`` (see above) by the following
method to make your receivers available::
The next pages provide details on their usage.
class TimeRestrictionApp(AppConfig):
def ready(self):
from . import signals # NOQA
Views
-----
Your plugin may define custom views. If you put an ``urls`` submodule into your
plugin module, pretix will automatically import it and include it into the root
URL configuration.
.. WARNING:: If you define custom URLs and views, you are currently on your own
with checking that the calling user is logged in, has appropriate permissions,
etc. We plan on providing native support for this in a later version.
.. _Django app: https://docs.djangoproject.com/en/1.7/ref/applications/
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/
+3 -3
View File
@@ -4,9 +4,9 @@
Writing a restriction plugin
============================
Please make sure you have read and understood the :ref:`basic idea being pretix's restrictions
<restrictionconcept>`. In this document, we will walk through the creation of a restriction
plugin using the example of a restriction by date and time.
Please make sure you have read and understood the :ref:`basic idea <restrictionconcept>` behind
what pretix calls *restrictions*. In this document, we will walk through the creation of a
restriction plugin using the example of a restriction by date and time.
Also, read :ref:`Creating a plugin <pluginsetup>` first.