forked from CGM_Public/pretix_original
Documentation for the payment provider plugin API
This commit is contained in:
@@ -19,7 +19,11 @@ import os
|
|||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.insert(0, os.path.abspath('.'))
|
sys.path.insert(0, os.path.abspath('../src'))
|
||||||
|
|
||||||
|
import django
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix.settings")
|
||||||
|
django.setup()
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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
|
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
|
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::
|
that we'll soon create::
|
||||||
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from pretix.base.signals import register_payment_providers
|
from pretix.base.signals import register_payment_providers
|
||||||
|
|
||||||
from .payment import BankTransfer
|
from .payment import Paypal
|
||||||
|
|
||||||
|
|
||||||
@receiver(register_payment_providers)
|
@receiver(register_payment_providers)
|
||||||
def register_payment_provider(sender, **kwargs):
|
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.
|
||||||
|
|||||||
@@ -13,23 +13,45 @@ require two steps to install:
|
|||||||
* Add it to the ``INSTALLED_APPS`` setting of Django in ``pretix/settings.py``
|
* Add it to the ``INSTALLED_APPS`` setting of Django in ``pretix/settings.py``
|
||||||
* Perform database migrations by using ``python manage.py migrate``
|
* Perform database migrations by using ``python manage.py migrate``
|
||||||
|
|
||||||
The communication between pretix and the plugins happens via Django's
|
The communication between pretix and the plugins happens mostly using Django's
|
||||||
`signal dispatcher`_ pattern. The core modules of pretix, ``pretixbase``,
|
`signal dispatcher`_ feature. The core modules of pretix, ``pretixbase``,
|
||||||
``pretixcontrol`` and ``pretixpresale`` expose a number of signals which are documented
|
``pretixcontrol`` and ``pretixpresale`` expose a number of signals which are documented
|
||||||
on the next pages.
|
on the next pages.
|
||||||
|
|
||||||
.. _`pluginsetup`:
|
.. _`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``,
|
Plugin metadata
|
||||||
``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::
|
|
||||||
|
|
||||||
|
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.apps import AppConfig
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pretix.base.plugins import PluginType
|
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 " +
|
"of a given item or variation to a certain timeframe " +
|
||||||
"or change its price during a certain period.")
|
"or change its price during a certain period.")
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
from . import signals # NOQA
|
|
||||||
|
|
||||||
default_app_config = 'pretix.plugins.timerestriction.TimeRestrictionApp'
|
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``
|
The various components of pretix define a number of signals which your plugin can
|
||||||
* ``PAYMENT``
|
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/
|
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
|
||||||
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/
|
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
Writing a restriction plugin
|
Writing a restriction plugin
|
||||||
============================
|
============================
|
||||||
|
|
||||||
Please make sure you have read and understood the :ref:`basic idea being pretix's restrictions
|
Please make sure you have read and understood the :ref:`basic idea <restrictionconcept>` behind
|
||||||
<restrictionconcept>`. In this document, we will walk through the creation of a restriction
|
what pretix calls *restrictions*. In this document, we will walk through the creation of a
|
||||||
plugin using the example of a restriction by date and time.
|
restriction plugin using the example of a restriction by date and time.
|
||||||
|
|
||||||
Also, read :ref:`Creating a plugin <pluginsetup>` first.
|
Also, read :ref:`Creating a plugin <pluginsetup>` first.
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ from decimal import Decimal
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from django.forms import Form
|
from django.forms import Form
|
||||||
|
from django.http import HttpRequest
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pretix.base.forms import SettingsForm
|
from pretix.base.forms import SettingsForm
|
||||||
|
from pretix.base.models import Order
|
||||||
|
|
||||||
from pretix.base.settings import SettingsSandbox
|
from pretix.base.settings import SettingsSandbox
|
||||||
|
|
||||||
@@ -66,11 +68,30 @@ class BasePaymentProvider:
|
|||||||
def settings_form_fields(self) -> dict:
|
def settings_form_fields(self) -> dict:
|
||||||
"""
|
"""
|
||||||
When the event's administrator administrator visits the event configuration
|
When the event's administrator administrator visits the event configuration
|
||||||
page,
|
page, this method is called to return the configuration fields available.
|
||||||
A dictionary. The keys should be (unprefixed) EventSetting keys,
|
|
||||||
the values should be corresponding django form fields.
|
|
||||||
|
|
||||||
We suggest returning a collections.OrderedDict object instead of a dict.
|
It should therefore return a dictionary where the keys should be (unprefixed)
|
||||||
|
settings keys and the values should be corresponding Django form fields.
|
||||||
|
|
||||||
|
The default implementation returns the appropriate fields for the ``_enabled``,
|
||||||
|
``_fee_abs`` and ``_fee_percent`` settings mentioned above.
|
||||||
|
|
||||||
|
We suggest that you return an ``OrderedDict`` object instead of a dictionary
|
||||||
|
and make use of the default implementation. Your implementation could look
|
||||||
|
like this::
|
||||||
|
|
||||||
|
@property
|
||||||
|
def settings_form_fields(self):
|
||||||
|
return OrderedDict(
|
||||||
|
list(super().settings_form_fields.items()) + [
|
||||||
|
('bank_details',
|
||||||
|
forms.CharField(
|
||||||
|
widget=forms.Textarea,
|
||||||
|
label=_('Bank account details'),
|
||||||
|
required=False
|
||||||
|
))
|
||||||
|
]
|
||||||
|
)
|
||||||
"""
|
"""
|
||||||
return OrderedDict([
|
return OrderedDict([
|
||||||
('_enabled',
|
('_enabled',
|
||||||
@@ -96,18 +117,20 @@ class BasePaymentProvider:
|
|||||||
@property
|
@property
|
||||||
def checkout_form_fields(self) -> dict:
|
def checkout_form_fields(self) -> dict:
|
||||||
"""
|
"""
|
||||||
A dictionary. The keys should be unprefixed field names,
|
This is used by the default implementation of :py:meth:`checkout_form`.
|
||||||
the values should be corresponding django form fields.
|
It should return an object similar to :py:attr:`settings_form_fields`.
|
||||||
|
|
||||||
We suggest returning a collections.OrderedDict object instead of a dict.
|
The default implementation returns an empty dictionary.
|
||||||
"""
|
"""
|
||||||
# TODO: Proper handling of required=True fields in HTML
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def checkout_form(self, request) -> Form:
|
def checkout_form(self, request: HttpRequest) -> Form:
|
||||||
"""
|
"""
|
||||||
Returns the Form object of the form that should be displayed when the
|
This is called by the default implementation of :py:meth:`checkout_form_render`
|
||||||
user selects this provider as his payment method.
|
to obtain the form that is displayed to the user during the checkout
|
||||||
|
process. The default implementation constructs the form using
|
||||||
|
:py:attr:`checkout_form_fields` and sets appropriate prefixes for the form
|
||||||
|
and all fields and fills the form with data form the user's session.
|
||||||
"""
|
"""
|
||||||
form = Form(
|
form = Form(
|
||||||
data=(request.POST if request.method == 'POST' else None),
|
data=(request.POST if request.method == 'POST' else None),
|
||||||
@@ -121,10 +144,15 @@ class BasePaymentProvider:
|
|||||||
form.fields = self.checkout_form_fields
|
form.fields = self.checkout_form_fields
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def checkout_form_render(self, request) -> str:
|
def checkout_form_render(self, request: HttpRequest) -> str:
|
||||||
"""
|
"""
|
||||||
Returns the HTML of the form that should be displayed when the user
|
When the user selects this provider as his prefered payment method,
|
||||||
selects this provider as his payment method.
|
he will be shown the HTML you return from this method.
|
||||||
|
|
||||||
|
The default implementation will call :py:meth:`checkout_form`
|
||||||
|
and render the returned form. If your payment method doesn't require
|
||||||
|
the user to fill out form fields, you should just return a paragraph
|
||||||
|
of explainatory text.
|
||||||
"""
|
"""
|
||||||
form = self.checkout_form(request)
|
form = self.checkout_form(request)
|
||||||
template = get_template('pretixpresale/event/checkout_payment_form_default.html')
|
template = get_template('pretixpresale/event/checkout_payment_form_default.html')
|
||||||
@@ -133,23 +161,53 @@ class BasePaymentProvider:
|
|||||||
|
|
||||||
def checkout_confirm_render(self, request) -> str:
|
def checkout_confirm_render(self, request) -> str:
|
||||||
"""
|
"""
|
||||||
Returns the HTML that should be displayed when the user selected this provider
|
If the user successfully filled in his payment data, he will be redirected
|
||||||
on the 'confirm order' page.
|
to a confirmation page which lists all details of his order for a final review.
|
||||||
|
This method should return the HTML which should be displayed inside the
|
||||||
|
'Payment' box on this page.
|
||||||
|
|
||||||
|
In most cases, this should include a short summary of the user's input and
|
||||||
|
a short explaination on how the payment process will continue.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError() # NOQA
|
raise NotImplementedError() # NOQA
|
||||||
|
|
||||||
def checkout_prepare(self, request, cart) -> "bool|HttpResponse":
|
def checkout_prepare(self, request: HttpRequest, cart: dict) -> "bool|str":
|
||||||
"""
|
"""
|
||||||
Will be called if the user selects this provider as his payment method.
|
Will be called after the user selected this provider as his payment method.
|
||||||
If the payment provider provides a form to the user to enter payment data,
|
If you provided a form to the user to enter payment data, this method should
|
||||||
this method should at least store the user's input into his session.
|
at least store the user's input into his session.
|
||||||
|
|
||||||
It should return True or False, depending of the validity of the user's input,
|
This method should return ``False``, if the user's input was invalid, ``True``
|
||||||
if the frontend should continue with default behaviour, or a redirect URL,
|
if the input was valid and the frontend should continue with default behaviour
|
||||||
if you need special behaviour.
|
or a string containing an URL, if the user should be redirected somewhere else.
|
||||||
|
|
||||||
On errors, it should use Django's message framework to display an error message
|
On errors, you should use Django's message framework to display an error message
|
||||||
to the user (or the normal form validation error messages).
|
to the user (or the normal form validation error messages).
|
||||||
|
|
||||||
|
The default implementation stores the input into the form returned by
|
||||||
|
:py:meth:`checkout_form` in the user's session.
|
||||||
|
|
||||||
|
If your payment method requires you to redirect the user to an external provider,
|
||||||
|
this might be the place to do so.
|
||||||
|
|
||||||
|
.. IMPORTANT:: If this is called, the user has not yet confirmed his or her order.
|
||||||
|
You may NOT do anything which actually moves money.
|
||||||
|
|
||||||
|
:param cart: This dictionary contains at least the following keys:
|
||||||
|
|
||||||
|
positions:
|
||||||
|
A list of ``CartPosition`` objects that are annotated with the special
|
||||||
|
attributes ``count`` and ``total`` because multiple objects of the
|
||||||
|
same content are grouped into one.
|
||||||
|
|
||||||
|
raw:
|
||||||
|
The raw list of ``CartPosition`` objects in the users cart
|
||||||
|
|
||||||
|
total:
|
||||||
|
The overall total *including* the fee for the payment method.
|
||||||
|
|
||||||
|
payment_fee:
|
||||||
|
The fee for the payment method.
|
||||||
"""
|
"""
|
||||||
form = self.checkout_form(request)
|
form = self.checkout_form(request)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
@@ -159,49 +217,58 @@ class BasePaymentProvider:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def checkout_is_valid_session(self, request) -> bool:
|
def checkout_is_valid_session(self, request: HttpRequest) -> bool:
|
||||||
"""
|
"""
|
||||||
This is called at the time the user tries to place the order. It should return
|
This is called at the time the user tries to place the order. It should return
|
||||||
True, if the user's session is valid and all data your payment provider requires
|
``True``, if the user's session is valid and all data your payment provider requires
|
||||||
in future steps is present.
|
in future steps is present.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError() # NOQA
|
raise NotImplementedError() # NOQA
|
||||||
|
|
||||||
def checkout_perform(self, request, order) -> str:
|
def checkout_perform(self, request: HttpRequest, order: Order) -> str:
|
||||||
"""
|
"""
|
||||||
Will be called if the user submitted his order successfully to initiate the
|
After the user confirmed his purchase, this method will be called to complete
|
||||||
payment process.
|
the payment process. This is the place to actually move the money, if applicable.
|
||||||
|
If you need any speical behaviour, you can return a string
|
||||||
|
containing an URL the user will be redirected to. If you are done with your process
|
||||||
|
you should return the user to the order's detail page.
|
||||||
|
|
||||||
It should return a custom redirct URL, if you need special behaviour, or None to
|
If the payment is completed, you should call ``order.mark_paid(provider, info)``
|
||||||
continue with default behaviour.
|
with ``provider`` being your :py:attr:`identifier` and ``info`` being any string
|
||||||
|
you might want to store for later usage. Please note, that if you want to store
|
||||||
|
something inside ``order.payment_info``, please do a ``order = order.clone()`` before
|
||||||
|
modifying or saving the order object.
|
||||||
|
|
||||||
On errors, it should use Django's message framework to display an error message
|
The default implementation just returns ``None`` and therefore leaves the
|
||||||
to the user (or the normal form validation error messages).
|
order unpaid. The user will be redirected to the order's detail page by default.
|
||||||
|
|
||||||
|
On errors, you should use Django's message framework to display an error message
|
||||||
|
to the user.
|
||||||
|
|
||||||
:param order: The order object
|
:param order: The order object
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def order_pending_render(self, request, order) -> str:
|
def order_pending_render(self, request: HttpRequest, order: Order) -> str:
|
||||||
"""
|
"""
|
||||||
Will be called if the user views the detail page of an unpaid order which is
|
If the user visits a detail page of an order which has not yet been paid but
|
||||||
associated with this payment provider.
|
this payment method was selected during checkout, this method will be called
|
||||||
|
to provide HTML content for the 'payment' box on the page.
|
||||||
|
|
||||||
It should return HTML code which should be displayed to the user. It should contian
|
It should contain instructions on how to continue with the payment process,
|
||||||
instructions on how to continue with the payment process, either in form of text
|
either in form of text or buttons/links/etc.
|
||||||
or buttons/links/etc.
|
|
||||||
|
|
||||||
:param order: The order object
|
:param order: The order object
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError() # NOQA
|
raise NotImplementedError() # NOQA
|
||||||
|
|
||||||
def order_paid_render(self, request, order) -> str:
|
def order_paid_render(self, request: HttpRequest, order: Order) -> str:
|
||||||
"""
|
"""
|
||||||
Will be called if the user views the detail page of an paid order which is
|
Will be called if the user views the detail page of an paid order which is
|
||||||
associated with this payment provider.
|
associated with this payment provider.
|
||||||
|
|
||||||
It should return HTML code which should be displayed to the user or None,
|
It should return HTML code which should be displayed to the user or None,
|
||||||
if there is nothing to say.
|
if there is nothing to say (like the default implementation does).
|
||||||
|
|
||||||
:param order: The order object
|
:param order: The order object
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from django.contrib import messages
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils.functional import cached_property
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext as __
|
from django.utils.translation import ugettext as __
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|||||||
Reference in New Issue
Block a user