Compare commits

..

7 Commits

Author SHA1 Message Date
Raphael Michel
77ffe55453 Bump version to 1.13.1 2018-03-07 10:37:23 +01:00
Raphael Michel
ab865e716f Allow admin to create invoice if invoice setting is set to "all orders" 2018-03-07 10:37:13 +01:00
Raphael Michel
0bf1832b23 Allow customer to manually generate invoices if order is older than invoice setting 2018-03-07 10:36:00 +01:00
Raphael Michel
650adb9235 pretixdroid: Online search should include name of parent position 2018-03-07 10:36:00 +01:00
Raphael Michel
e2d55fed0d Fix issue with fees without tax rules 2018-03-07 10:36:00 +01:00
Raphael Michel
aef751dbee Contact form data was only saved to session if invoice addresses where active 2018-03-07 10:36:00 +01:00
Raphael Michel
cd084fe8d1 Show "continue" instead of "checkout" also if order is free 2018-03-07 10:36:00 +01:00
868 changed files with 19194 additions and 452295 deletions

27
.gitattributes vendored
View File

@@ -1,17 +1,16 @@
src/pretix/static/fontawesome/* linguist-vendored
src/pretix/static/lightbox/* linguist-vendored
src/pretix/static/typeahead/* linguist-vendored
src/pretix/static/moment/* linguist-vendored
src/pretix/static/datetimepicker/* linguist-vendored
src/pretix/static/colorpicker/* linguist-vendored
src/pretix/static/fileupload/* linguist-vendored
src/pretix/static/vuejs/* linguist-vendored
src/pretix/static/select2/* linguist-vendored
src/pretix/static/charts/* linguist-vendored
src/pretix/static/rrule/* linguist-vendored
src/pretix/static/iframeresizer/* linguist-vendored
src/pretix/static/pdfjs/* linguist-vendored
src/pretix/static/fabric/* linguist-vendored
src/static/fontawesome/* linguist-vendored
src/static/lightbox/* linguist-vendored
src/static/typeahead/* linguist-vendored
src/static/moment/* linguist-vendored
src/static/datetimepicker/* linguist-vendored
src/static/colorpicker/* linguist-vendored
src/static/fileupload/* linguist-vendored
src/static/vuejs/* linguist-vendored
src/static/select2/* linguist-vendored
src/static/charts/* linguist-vendored
src/static/iframeresizer/* linguist-vendored
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/fabric.* linguist-vendored
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/pdf.* linguist-vendored
# Denote all files that are truly binary and should not be modified.
*.eot binary

View File

@@ -17,7 +17,7 @@ pypi:
- virtualenv env
- source env/bin/activate
- pip install -U pip wheel setuptools
- XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt
- XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt -r src/requirements/py34.txt
- cd src
- python setup.py sdist
- pip install dist/pretix-*.tar.gz

View File

@@ -1 +1,2 @@
-r src/requirements/py34.txt
-r doc/requirements.txt

View File

@@ -11,20 +11,21 @@ fi
if [ "$PRETIX_CONFIG_FILE" == "tests/travis_postgres.cfg" ]; then
psql -c 'create database travis_ci_test;' -U postgres
pip3 install -Ur src/requirements/postgres.txt
fi
if [ "$1" == "style" ]; then
XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt
XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt -r src/requirements/py34.txt
cd src
flake8 .
isort -c -rc -df .
fi
if [ "$1" == "doctests" ]; then
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt -r src/requirements/py34.txt
cd doc
make doctest
fi
if [ "$1" == "doc-spelling" ]; then
if [ "$1" == "spelling" ]; then
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt
cd doc
make spelling
@@ -32,27 +33,22 @@ if [ "$1" == "doc-spelling" ]; then
exit 1
fi
fi
if [ "$1" == "translation-spelling" ]; then
XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements/dev.txt
cd src
potypo
fi
if [ "$1" == "tests" ]; then
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
cd src
python manage.py check
make all compress
py.test --reruns 5 -n 3 tests
py.test --reruns 5 tests
fi
if [ "$1" == "tests-cov" ]; then
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
cd src
python manage.py check
make all compress
coverage run -m py.test --reruns 5 tests && codecov
fi
if [ "$1" == "plugins" ]; then
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
cd src
python setup.py develop
make all compress

View File

@@ -1,8 +1,7 @@
language: python
dist: xenial
sudo: false
install:
- pip install -U pip wheel setuptools
- pip install -U pip wheel setuptools==28.6.1
script:
- bash .travis.sh $JOB
cache:
@@ -13,33 +12,34 @@ services:
- postgresql
matrix:
include:
- python: 3.7
- python: 3.6
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_sqlite.cfg
- python: 3.7
env: JOB=tests-cov PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
- python: 3.7
- python: 3.6
env: JOB=tests-cov
- python: 3.6
env: JOB=style
- python: 3.7
- python: 3.4
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_sqlite.cfg
- python: 3.5
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_sqlite.cfg
- python: 3.4
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_mysql.cfg
- python: 3.7
- python: 3.5
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_mysql.cfg
- python: 3.6
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_mysql.cfg
- python: 3.4
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
- python: 3.5
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
- python: 3.7
env: JOB=doc-spelling
- python: 3.7
env: JOB=translation-spelling
- python: 3.6
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
- python: 3.6
env: JOB=plugins
- python: 3.6
env: JOB=spelling
addons:
postgresql: "9.4"
mariadb: '10.3'
apt:
packages:
- enchant
- myspell-de-de
- aspell-en
- sqlite3
sources:
- travis-ci/sqlite3
branches:
except:
- /^weblate-.*/

View File

@@ -1,26 +1,10 @@
FROM python:3.6
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
default-libmysqlclient-dev \
gettext \
git \
libffi-dev \
libjpeg-dev \
libmemcached-dev \
libpq-dev \
libssl-dev \
libxml2-dev \
libxslt1-dev \
locales \
nginx \
python-dev \
python-virtualenv \
python3-dev \
sudo \
supervisor \
zlib1g-dev && \
apt-get install -y git libxml2-dev libxslt1-dev python-dev python-virtualenv locales \
libffi-dev build-essential python3-dev zlib1g-dev libssl-dev gettext libpq-dev \
libmysqlclient-dev libmemcached-dev libjpeg-dev supervisor nginx sudo \
--no-install-recommends && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
dpkg-reconfigure locales && \
@@ -35,22 +19,6 @@ RUN apt-get update && \
ENV LC_ALL=C.UTF-8 \
DJANGO_SETTINGS_MODULE=production_settings
# To copy only the requirements files needed to install from PIP
COPY src/requirements /pretix/src/requirements
COPY src/requirements.txt /pretix/src
RUN pip3 install -U \
pip \
setuptools \
wheel && \
cd /pretix/src && \
pip3 install \
-r requirements.txt \
-r requirements/memcached.txt \
-r requirements/mysql.txt \
-r requirements/redis.txt \
gunicorn && \
rm -rf ~/.cache/pip
COPY deployment/docker/pretix.bash /usr/local/bin/pretix
COPY deployment/docker/supervisord.conf /etc/supervisord.conf
COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf
@@ -59,8 +27,11 @@ COPY src /pretix/src
RUN chmod +x /usr/local/bin/pretix && \
rm /etc/nginx/sites-enabled/default && \
pip3 install -U pip wheel setuptools && \
cd /pretix/src && \
rm -f pretix.cfg && \
pip3 install -r requirements.txt -r requirements/mysql.txt -r requirements/postgres.txt \
-r requirements/memcached.txt -r requirements/redis.txt gunicorn && \
mkdir -p data && \
chown -R pretixuser:pretixuser /pretix /data data && \
sudo -u pretixuser make production

View File

@@ -40,9 +40,6 @@ Contributing
If you want to contribute to pretix, please read the `developer documentation`_
in our documentation. If you have any further questions, please do not hesitate to ask!
.. image:: https://translate.pretix.eu/widgets/pretix/-/pretix/multi-blue.svg
:target: https://translate.pretix.eu/engage/pretix/
Code of Conduct
---------------
We have a `Code of Conduct`_ in place that applies to all project contributions,

View File

@@ -3,7 +3,7 @@ cd /pretix/src
export DJANGO_SETTINGS_MODULE=production_settings
export DATA_DIR=/data/
export HOME=/pretix
export NUM_WORKERS=$((2 * $(nproc --all)))
NUM_WORKERS=10
if [ ! -d /data/logs ]; then
mkdir /data/logs;

View File

@@ -53,10 +53,6 @@ Example::
A comma-separated list of plugins that are enabled by default for all new events.
Defaults to ``pretix.plugins.sendmail,pretix.plugins.statistics``.
``plugins_exclude``
A comma-separated list of plugins that are not available even though they are installed.
Defaults to an empty string.
``cookie_domain``
The cookie domain to be set. Defaults to ``None``.
@@ -74,10 +70,6 @@ Example::
that are used to print tax amounts in the customer currency on invoices for some currencies. Set to ``off`` to
disable this feature. Defaults to ``on``.
``audit_comments``
Enables or disables nagging staff users for leaving comments on their sessions for auditability.
Defaults to ``off``.
Locale settings
---------------
@@ -125,27 +117,6 @@ Example::
Indicates if the database backend is a MySQL/MariaDB Galera cluster and
turns on some optimizations/special case handlers. Default: ``False``
.. _`config-replica`:
Database replica settings
-------------------------
If you use a replicated database setup, pretix expects that the default database connection always points to the primary database node.
Routing read queries to a replica on database layer is **strongly** discouraged since this can lead to inaccurate such as more tickets
being sold than are actually available.
However, pretix can still make use of a database replica to keep some expensive queries with that can tolerate some latency from your
primary database, such as backend search queries. The ``replica`` configuration section can have the same settings as the ``database``
section (except for the ``backend`` setting) and will default back to the ``database`` settings for all values that are not given. This
way, you just need to specify the settings that are different for the replica.
Example::
[replica]
host=192.168.0.2
.. _`config-urls`:
URLs
----
@@ -273,24 +244,6 @@ to speed up various operations::
If redis is not configured, pretix will store sessions and locks in the database. If memcached
is configured, memcached will be used for caching instead of redis.
Translations
------------
pretix comes with a number of translations. Some of them are marked as "incubating", which means
they can usually only be selected in development mode. If you want to use them nevertheless, you
can activate them like this::
[languages]
allow_incubating=pt-br,da
You can also tell pretix about additional paths where it will search for translations::
[languages]
path=/path/to/my/translations
For a given language (e.g. ``pt-br``), pretix will then look in the
specific sub-folder, e.g. ``/path/to/my/translations/pt_BR/LC_MESSAGES/django.po``.
Celery task queue
-----------------
@@ -334,13 +287,5 @@ various places like order codes, secrets in the ticket QR codes, etc. Example::
; Voucher code needs to be < 255 characters, default is 16
voucher_code=16
External tools
--------------
pretix can make use of some external tools if they are installed. Currently, they are all optional. Example::
[tools]
pdftk=/usr/bin/pdftk
.. _Python documentation: https://docs.python.org/3/library/configparser.html?highlight=configparser#supported-ini-file-structure
.. _Celery documentation: http://docs.celeryproject.org/en/latest/userguide/configuration.html

View File

@@ -11,4 +11,3 @@ This documentation is for everyone who wants to install pretix on a server.
installation/index
config
maintainance
scaling

View File

@@ -1,37 +0,0 @@
.. highlight:: none
Installing a development version
================================
If you want to use a feature of pretix that is not yet contained in the last monthly release, you can also
install a development version with pretix.
.. warning:: When in production, we strongly recommend only installing released versions. Development versions might
be broken, incompatible to plugins, or in rare cases incompatible to upgrade later on.
Manual installation
-------------------
You can use ``pip`` to update pretix directly to the development branch. Then, upgrade as usual::
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U "git+https://github.com/pretix/pretix.git#egg=pretix&subdirectory=src"
(venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild
(venv)$ python -m pretix updatestyles
# systemctl restart pretix-web pretix-worker
Docker installation
-------------------
To use the latest development version with Docker, first pull it from Docker Hub::
$ docker pull pretix/standalone:latest
Then change your ``/etc/systemd/system/pretix.service`` file to use the ``:latest`` tag instead of ``:stable`` as well
and upgrade as usual::
$ systemctl restart pretix.service
$ docker exec -it pretix.service pretix upgrade

View File

@@ -26,7 +26,7 @@ installation guides):
* `Docker`_
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
* A `PostgreSQL`_, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
* A `MySQL`_ or `PostgreSQL`_ database server
* A `redis`_ server
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
@@ -36,9 +36,6 @@ Linux and firewalls, we recommend that you start with `ufw`_.
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
installations except for evaluation purposes.
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
**MariaDB 10.2.7 or newer**.
On this guide
-------------
@@ -58,29 +55,16 @@ Database
--------
Next, we need a database and a database user. We can create these with any kind of database managing tool or directly on
our database's shell. For PostgreSQL, we would do::
our database's shell, e.g. for MySQL::
# sudo -u postgres createuser -P pretix
# sudo -u postgres createdb -O pretix pretix
$ mysql -u root -p
mysql> CREATE DATABASE pretix DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
mysql> GRANT ALL PRIVILEGES ON pretix.* TO pretix@'localhost' IDENTIFIED BY '*********';
mysql> FLUSH PRIVILEGES;
Make sure that your database listens on the network. If PostgreSQL on the same same host as docker, but not inside a docker container, we recommend that you just listen on the Docker interface by changing the following line in ``/etc/postgresql/<version>/main/postgresql.conf``::
listen_addresses = 'localhost,172.17.0.1'
You also need to add a new line to ``/etc/postgresql/<version>/main/pg_hba.conf`` to allow network connections to this user and database::
host pretix pretix 172.17.0.1/16 md5
Restart PostgreSQL after you changed these files::
# systemctl restart postgresql
If you have a firewall running, you should also make sure that port 5432 is reachable from the ``172.17.0.1/16`` subnet.
For MySQL, you can either also use network-based connections or mount the ``/var/run/mysqld/mysqld.sock`` socket into the docker container.
When using MySQL, make sure you set the character set of the database to ``utf8mb4``, e.g. like this::
mysql > CREATE DATABASE pretix DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
Replace the asterisks with a password of your own. For MySQL, we will use a unix domain socket to connect to the
database. For PostgreSQL, be sure to configure the interface binding and your firewall so that the docker container
can reach PostgreSQL.
Redis
-----
@@ -127,16 +111,13 @@ Fill the configuration file ``/etc/pretix/pretix.cfg`` with the following conten
datadir=/data
[database]
; Replace postgresql with mysql for MySQL
backend=postgresql
; Replace mysql with postgresql_psycopg2 for PostgreSQL
backend=mysql
name=pretix
user=pretix
; Replace with the password you chose above
password=*********
; In most docker setups, 172.17.0.1 is the address of the docker host. Adjuts
; this to wherever your database is running, e.g. the name of a linked container
; or of a mounted MySQL socket.
host=172.17.0.1
; Replace with host IP address for PostgreSQL
host=/var/run/mysqld/mysqld.sock
[mail]
; See config file documentation for more options
@@ -180,15 +161,14 @@ named ``/etc/systemd/system/pretix.service`` with the following content::
-v /var/pretix-data:/data \
-v /etc/pretix:/etc/pretix \
-v /var/run/redis:/var/run/redis \
-v /var/run/mysqld:/var/run/mysqld \
pretix/standalone:stable all
ExecStop=/usr/bin/docker stop %n
[Install]
WantedBy=multi-user.target
When using MySQL and socket mounting, you'll need the additional flag ``-v /var/run/mysqld:/var/run/mysqld`` in the command.
You can now run the following commands
You can leave the MySQL socket volume out if you're using PostgreSQL. You can now run the following commands
to enable and start the service::
# systemctl daemon-reload
@@ -288,8 +268,8 @@ to re-build your custom image after you pulled ``pretix/standalone`` if you want
.. _pretix.eu: https://pretix.eu/
.. _MySQL: https://dev.mysql.com/doc/refman/5.7/en/linux-installation-apt-repo.html
.. _PostgreSQL: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8
.. _redis: https://blog.programster.org/debian-8-install-redis-server/
.. _redis: http://blog.programster.org/debian-8-install-redis-server/
.. _ufw: https://en.wikipedia.org/wiki/Uncomplicated_Firewall
.. _redis website: https://redis.io/topics/security
.. _redis website: http://redis.io/topics/security
.. _redis in docker: https://hub.docker.com/r/_/redis/
.. _strong encryption settings: https://mozilla.github.io/server-side-tls/ssl-config-generator/

View File

@@ -1,84 +0,0 @@
.. highlight:: none
Installing pretix Enterprise plugins
====================================
If you want to use a feature of pretix that is part of our commercial offering pretix Enterprise, you need to follow
some extra steps. Installation works similar to normal pretix plugins, but involves a few extra steps.
Buying the license
------------------
To obtain a license, please get in touch at sales@pretix.eu. Please let us know how many tickets you roughly intend
to sell per year and how many servers you want to use the plugin on. We recommend having a look at our `price list`_
first.
Manual installation
-------------------
First, generate an SSH key for the system user that you install pretix as. In our tutorial, that would be the user
``pretix``. Choose an empty passphrase::
# su pretix
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/var/pretix/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/pretix/.ssh/id_rsa.
Your public key has been saved in /var/pretix/.ssh/id_rsa.pub.
Next, send the content of the *public* key to your sales representative at pretix::
$ cat /var/pretix/.ssh/id_rsa.pub
ssh-rsa AAAAB3N...744HZawHlD pretix@foo
After we configured your key in our system, you can install the plugin directly using ``pip`` from the URL we told
you, for example::
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack"
(venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild
# systemctl restart pretix-web pretix-worker
Docker installation
-------------------
To install a plugin, you need to build your own docker image. To do so, create a new directory to work in. As a first
step, generate a new SSH key in that directory to use for authentication with us::
$ cd /home/me/mypretixdocker
$ ssh-keygen -N "" -f id_pretix_enterprise
Next, send the content of the *public* key to your sales representative at pretix::
$ cat id_pretix_enterprise.pub
ssh-rsa AAAAB3N...744HZawHlD pretix@foo
After we configured your key in our system, you can add a ``Dockerfile`` in your directory that includes the newly
generated key and installs the plugin from the URL we told you::
FROM pretix/standalone:stable
USER root
COPY id_pretix_enterprise /root/.ssh/id_rsa
COPY id_pretix_enterprise.pub /root/.ssh/id_rsa.pub
RUN chmod -R 0600 /root/.ssh && \
mkdir -p /etc/ssh && \
ssh-keyscan -t rsa -p 10022 code.rami.io >> /root/.ssh/known_hosts && \
echo StrictHostKeyChecking=no >> /root/.ssh/config && \
pip3 install -Ue "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack" && \
cd /pretix/src && \
sudo -u pretixuser make production
USER pretixuser
Then, build the image for docker::
$ docker build -t mypretix
You can now use that image ``mypretix`` instead of ``pretix/standalone:stable`` in your ``/etc/systemd/system/pretix.service``
service file. Be sure to re-build your custom image after you pulled ``pretix/standalone`` if you want to perform an
update to a new version of pretix.
.. _price list: https://pretix.eu/about/en/pricing

View File

@@ -21,9 +21,6 @@ To use pretix, you will need the following things:
.. warning:: Do not ever use SQLite in production. It will break.
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
**MariaDB 10.2.7 or newer**.
* A **reverse proxy**. pretix needs to deliver some static content to your users (e.g. CSS, images, ...). While pretix
is capable of doing this, having this handled by a proper web server like **nginx** or **Apache** will be much
faster. Also, you need a proxying web server in front to provide SSL encryption.

View File

@@ -1,5 +1,3 @@
.. _`installation`:
Installation guide
==================
@@ -12,5 +10,3 @@ for your needs.
general
docker_smallscale
manual_smallscale
dev_version
enterprise

View File

@@ -23,7 +23,7 @@ installation guides):
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
* A `PostgreSQL`_, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
* A `MySQL`_ or `PostgreSQL`_ database server
* A `redis`_ server
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
@@ -33,9 +33,6 @@ Linux and firewalls, we recommend that you start with `ufw`_.
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
installations except for evaluation purposes.
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
**MariaDB 10.2.7 or newer**.
Unix user
---------
@@ -50,23 +47,21 @@ Database
--------
Having the database server installed, we still need a database and a database user. We can create these with any kind
of database managing tool or directly on our database's shell. For PostgreSQL, we would do::
of database managing tool or directly on our database's shell, e.g. for MySQL::
# sudo -u postgres createuser pretix
# sudo -u postgres createdb -O pretix pretix
When using MySQL, make sure you set the character set of the database to ``utf8mb4``, e.g. like this::
mysql > CREATE DATABASE pretix DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
$ mysql -u root -p
mysql> CREATE DATABASE pretix DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
mysql> GRANT ALL PRIVILEGES ON pretix.* TO pretix@'localhost' IDENTIFIED BY '*********';
mysql> FLUSH PRIVILEGES;
Package dependencies
--------------------
To build and run pretix, you will need the following debian packages::
# apt-get install git build-essential python-dev python3-venv python3 python3-pip \
# apt-get install git build-essential python-dev python-virtualenv python3 python3-pip \
python3-dev libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
gettext libpq-dev libmariadbclient-dev libjpeg-dev libopenjp2-7-dev
gettext libpq-dev libmysqlclient-dev libjpeg-dev
Config file
-----------
@@ -87,18 +82,13 @@ Fill the configuration file ``/etc/pretix/pretix.cfg`` with the following conten
datadir=/var/pretix/data
[database]
; For MySQL, replace with "mysql"
backend=postgresql
; Replace mysql with postgresql_psycopg2 for PostgreSQL
backend=mysql
name=pretix
user=pretix
; For MySQL, enter the user password. For PostgreSQL on the same host,
; we don't need one because we can use peer authentification if our
; PostgreSQL user matches our unix user.
password=
; For MySQL, use local socket, e.g. /var/run/mysqld/mysqld.sock
; For a remote host, supply an IP address
; For local postgres authentication, you can leave it empty
host=
password=*********
; Replace with host IP address for PostgreSQL
host=/var/run/mysqld/mysqld.sock
[mail]
; See config file documentation for more options
@@ -122,16 +112,17 @@ Now we will install pretix itself. The following steps are to be executed as the
actually install pretix, we will create a virtual environment to isolate the python packages from your global
python installation::
$ python3 -m venv /var/pretix/venv
$ virtualenv -p python3 /var/pretix/venv
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U pip setuptools wheel
We now install pretix, its direct dependencies and gunicorn. Replace ``postgres`` with ``mysql`` in the following
command if you're running MySQL::
We now install pretix, its direct dependencies and gunicorn. Replace ``mysql`` with ``postgres`` in the following
command if you're running PostgreSQL::
(venv)$ pip3 install "pretix[postgres]" gunicorn
(venv)$ pip3 install "pretix[mysql]" gunicorn
Note that you need Python 3.5 or newer. You can find out your Python version using ``python -V``.
If you are running Python 3.4, you also need to ``pip3 install typing``. This is not required on 3.5 or newer.
You can find out your Python version using ``python -V``.
We also need to create a data directory::
@@ -275,10 +266,10 @@ Updates
.. warning:: While we try hard not to break things, **please perform a backup before every upgrade**.
To upgrade to a new pretix release, pull the latest code changes and run the following commands (again, replace
``postgres`` with ``mysql`` if necessary)::
``mysql`` with ``postgres`` if necessary)::
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U pretix[postgres] gunicorn
(venv)$ pip3 install -U pretix[mysql] gunicorn
(venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild
(venv)$ python -m pretix updatestyles
@@ -307,6 +298,6 @@ example::
.. _pretix.eu: https://pretix.eu/
.. _MySQL: https://dev.mysql.com/doc/refman/5.7/en/linux-installation-apt-repo.html
.. _PostgreSQL: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8
.. _redis: https://blog.programster.org/debian-8-install-redis-server/
.. _redis: http://blog.programster.org/debian-8-install-redis-server/
.. _ufw: https://en.wikipedia.org/wiki/Uncomplicated_Firewall
.. _strong encryption settings: https://mozilla.github.io/server-side-tls/ssl-config-generator/

View File

@@ -1,236 +0,0 @@
.. _`scaling`:
Scaling guide
=============
Our :ref:`installation guide <installation>` only covers "small-scale" setups, by which we mostly mean
setups that run on a **single (virtual) machine** and do not encounter large traffic peaks.
We do not offer an installation guide for larger-scale setups of pretix, mostly because we believe that
there is no one-size-fits-all solution for this and the desired setup highly depends on your use case,
the platform you run pretix on, and your technical capabilities. We do not recommend trying set up pretix
in a multi-server environment if you do not already have experience with managing server clusters.
This document is intended to give you a general idea on what issues you will encounter when you scale up
and what you should think of.
.. tip::
If you require more help on this, we're happy to help. Our pretix Enterprise support team has built
and helped building, scaling and load-testing pretix installations at any scale and we're looking
forward to work with you on fine-tuning your system. If you intend to sell **more than a thousand
tickets in a very short amount of time**, we highly recommend reaching out and at least talking this
through. Just get in touch at sales@pretix.eu!
Scaling reasons
---------------
There's mainly two reasons to scale up a pretix installation beyond a single server:
* **Availability:** Distributing pretix over multiple servers can allow you to survive failure of one or more single machines, leading to a higher uptime and reliability of your system.
* **Traffic and throughput:** Distributing pretix over multiple servers can allow you to process more web requests and ticket sales at the same time.
You are very unlikely to require scaling for other reasons, such as having too much data in your database.
Components
----------
A pretix installation usually consists of the following components which run performance-relevant processes:
* ``pretix-web`` is the Django-based web application that serves all user interaction.
* ``pretix-worker`` is a Celery-based application that processes tasks that should be run asynchronously outside of the web application process.
* A **SQL database** keeps all the important data and processes the actual transactions. We recommend using PostgreSQL, but MySQL/MariaDB works as well.
* A **web server** that terminates TLS and HTTP connections and forwards them to ``pretix-web``. In some cases, e.g. when serving static files, the web servers might return a response directly. We recommend using ``nginx``.
* A **redis** server responsible for the communication between ``pretix-web`` and ``pretix-worker``, as well as for caching.
* A directory of **media files** such as user-uploaded files or generated files (tickets, invoices, …) that are created and used by ``pretix-web``, ``pretix-worker`` and the web server.
In the following, we will discuss the scaling behavior of every component individually. In general, you can run all of the components
on the same server, but you can just as well distribute every component to its own server, or even use multiple servers for some single
components.
.. warning::
When setting up your system, don't forget about security. In a multi-server environment,
you need to take special care to ensure that no unauthorized access to your database
is possible through the network and that it's not easy to wiretap your connections. We
recommend a rigorous use of firewalls and encryption on all communications. You can
ensure this either on an application level (such as using the TLS support in your
database) or on a network level with a VPN solution.
Web server
""""""""""
Your web server is at the very front of your installation. It will need to absorb all of the traffic, and it should be able to
at least show a decent error message, even when everything else fails. Luckily, web servers are really fast these days, so this
can be achieved without too much work.
We recommend reading up on tuning your web server for high concurrency. For nginx, this means thinking about the number of worker
processes and the number of connections each worker process accepts. Double-check that TLS session caching works, because TLS
handshakes can get really expensive.
During a traffic peak, your web server will be able to make us of more CPU resources, while memory usage will stay comparatively low,
so if you invest in more hardware here, invest in more and faster CPU cores.
Make sure that pretix' static files (such as CSS and JavaScript assets) as well as user-uploaded media files (event logos, etc)
are served directly by your web server and your web server caches them in-memory (nginx does it by default) and sets useful
headers for client-side caching. As an additional performance improvement, you can turn of access logging for these types of files.
If you want, you can even farm out serving static files to a different web server entirely and :ref:`configure pretix to reference
them from a different URL <config-urls>`.
.. tip::
If you expect *really high traffic* for your very popular event, you might want to do some rate limiting on this layer, or,
if you want to ensure a fair and robust first-come-first-served experience and prefer letting users wait over showing them
errors, consider a queuing solution. We're happy to provide you with such systems, just get in touch at sales@pretix.eu.
pretix-web
""""""""""
The ``pretix-web`` process does not carry any internal state can be easily started on as many machines as you like, and you can
use the load balancing features of your frontend web server to redirect to all of them.
You can adjust the number of processes in the ``gunicorn`` command line, and we recommend choosing roughly two times the number
of CPU cores available. Under load, the memory consumption of ``pretix-web`` will stay comparatively constant, while the CPU usage
will increase a lot. Therefore, if you can add more or faster CPU cores, you will be able to serve more users.
pretix-worker
"""""""""""""
The ``pretix-worker`` process performs all operations that are not directly executed in the request-response-cycle of ``pretix-web``.
Just like ``pretix-web`` you can easily start up as many instances as you want on different machines to share the work. As long as they
all talk to the same redis server, they will all receive tasks from ``pretix-web``, work on them and post their result back.
You can configure the number of threads that run tasks in parallel through the ``--concurrency`` command line option of ``celery``.
Just like ``pretix-web``, this process is mostly heavy on CPU, disk IO and network IO, although memory peaks can occur e.g. during the
generation of large PDF files, so we recommend having some reserves here.
``pretix-worker`` performs a variety of tasks which are of different importance.
Some of them are mission-critical and need to be run quickly even during high load (such as
creating a cart or an order), others are irrelevant and can easily run later (such as
distributing tickets on the waiting list). You can fine-tune the capacity you assign to each
of these tasks by running ``pretix-worker`` processes that only work on a specific **queue**.
For example, you could have three servers dedicated only to process order creations and one
server dedicated only to sending emails. This allows you to set priorities and also protects
you from e.g. a slow email server lowering your ticket throughput.
You can do so by specifying one or more queues on the ``celery`` command line of this process, such as ``celery -A pretix.celery_app worker -Q notifications,mail``. Currently,
the following queues exist:
* ``checkout`` -- This queue handles everything related to carts and orders and thereby everything required to process a sale. This includes adding and deleting items from carts as well as creating and canceling orders.
* ``mail`` -- This queue handles sending of outgoing emails.
* ``notifications`` -- This queue handles the processing of any outgoing notifications, such as email notifications to admin users (except for the actual sending) or API notifications to registered webhooks.
* ``background`` -- This queue handles tasks that are expected to take long or have no human waiting for their result immediately, such as refreshing caches, re-generating CSS files, assigning tickets on the waiting list or parsing bank data files.
* ``default`` -- This queue handles everything else with "medium" or unassigned priority, most prominently the generation of files for tickets, invoices, badges, admin exports, etc.
Media files
"""""""""""
Both ``pretix-web``, ``pretix-worker`` and in some cases your webserver need to work with
media files. Media files are all files generated *at runtime* by the software. This can
include files uploaded by the event organizers, such as the event logo, files uploaded by
ticket buyers (if you use such features) or files generated by the software, such as
ticket files, invoice PDFs, data exports or customized CSS files.
Those files are by default stored to the ``media/`` sub-folder of the data directory given
in the ``pretix.cfg`` configuration file. Inside that ``media/`` folder, you will find a
``pub/`` folder containing the subset of files that should be publicly accessible through
the web server. Everything else only needs to be accessible by ``pretix-web`` and
``pretix-worker`` themselves.
If you distribute ``pretix-web`` or ``pretix-worker`` across more than one machine, you
**must** make sure that they all have access to a shared storage to read and write these
files, otherwise you **will** run into errors with the user interface.
The easiest solution for this is probably to store them on a NFS server that you mount
on each of the other servers.
Since we use Django's file storage mechanism internally, you can in theory also use a object-storage solution like Amazon S3, Ceph, or Minio to store these files, although we currently do not expose this through pretix' configuration file and this would require you to ship your own variant of ``pretix/settings.py`` and reference it through the ``DJANGO_SETTINGS_MODULE`` environment variable.
At pretix.eu, we use a custom-built `object storage cluster`_.
SQL database
""""""""""""
One of the most critical parts of the whole setup is the SQL database -- and certainly the
hardest to scale. Tuning relational databases is an art form, and while there's lots of
material on it on the internet, there's not a single recipe that you can apply to every case.
As a general rule of thumb, the more resources you can give your databases, the better.
Most databases will happily use all CPU cores available, but only use memory up to an amount
you configure, so make sure to set this memory usage as high as you can afford. Having more
memory available allows your database to make more use of caching, which is usually good.
Scaling your database to multiple machines needs to be treated with great caution. It's a
good to have a replica of your database for availability reasons. In case your primary
database server fails, you can easily switch over to the replica and continue working.
However, using database replicas for performance gains is much more complicated. When using
replicated database systems, you are always trading in consistency or availability to get
additional performance and the consequences of this can be subtle and it is important
that you have a deep understanding of the semantics of your replication mechanism.
.. warning::
Using an off-the-shelf database proxy solution that redirects read queries to your
replicas and write queries to your primary database **will lead to very nasty bugs.**
As an example, if you buy a ticket, pretix first needs to calculate how many tickets
are left to sell. If this calculation is done on a database replica that lags behind
even for fractions of a second, the decision to allow selling the ticket will be made
on out-of-data data and you can end up with more tickets sold than configured. Similarly,
you could imagine situations leading to double payments etc.
If you do have a replica, you *can* tell pretix about it :ref:`in your configuration <config-replica>`.
This way, pretix can offload complex read-only queries to the replica when it is safe to do so.
As of pretix 2.7, this is mainly used for search queries in the backend and for rendering the
product list and event lists in the frontend, but we plan on expanding this in the future.
Therefore, for now our clear recommendation is: Try to scale your database vertically and put
it on the most powerful machine you have available.
redis
"""""
While redis is a very important part that glues together some of the components, it isn't used
heavily and can usually handle a fairly large pretix installation easily on a single modern
CPU core.
Having some memory available is good in case of e.g. lots of tasks queuing up during a traffic peak, but we wouldn't expect ever needing more than a gigabyte of it.
Feel free to set up a redis cluster for availability but you won't need it for performance in a long time.
The limitations
---------------
Up to a certain point, pretix scales really well. However, there are a few things that we consider
even more important than scalability, and those are correctness and reliability. We want you to be
able to trust that pretix will not sell more tickets than you intended or run into similar error
cases.
Combined with pretix' flexibility and complexity, especially around vouchers and quotas, this creates
some hard issues. In many cases, we need to fall back to event-global locking for some actions which
are likely to run with high concurrency and cause harm.
For every event, only one of these locking actions can be run at the same time. Examples for this are
adding products limited by a quota to a cart, adding items to a cart using a voucher or placing an order
consisting of cart positions that don't have a valid reservation for much longer. In these cases, it is
currently not realistically possible to exceed selling **approx. 500 orders per minute per event**, even
if you add more hardware.
If you have an unlimited number of tickets, we can apply fewer locking and we've reached **approx.
1500 orders per minute per event** in benchmarks, although even more should be possible.
We're working to reduce the number of cases in which this is relevant and thereby improve the possible
throughput. If you want to use pretix for an event with 10,000+ tickets that are likely to be sold out
within minutes, please get in touch to discuss possible solutions. We'll work something out for you!
.. _object storage cluster: https://behind.pretix.eu/2018/03/20/high-available-cdn/

View File

@@ -1,9 +0,0 @@
Authentication
==============
.. toctree::
:maxdepth: 2
tokenauth
oauth
deviceauth

View File

@@ -1,137 +0,0 @@
.. _`rest-deviceauth`:
Device authentication
=====================
Initializing a new device
-------------------------
Users can create new devices in the "Device" section of their organizer settings. When creating
a new device, users can specify a list of events the device is allowed to access. After a new
device is created, users will be presented initialization instructions, consisting of an URL
and an initialization token. They will also be shown as a QR code with the following contents::
{"handshake_version": 1, "url": "https://pretix.eu", "token": "kpp4jn8g2ynzonp6"}
Your application should be able to scan a QR code of this type, or allow to enter the URL and the
initialization token manually. The handshake version is not used for manual initialization. When a
QR code is scanned with a higher handshake version than you support, you should reject the request
and prompt the user to update the client application.
After your application received the token, you need to call the initialization endpoint to obtain
a proper API token. At this point, you need to identify the name and version of your application,
as well as the type of underlying hardware. Example:
.. sourcecode:: http
POST /api/v1/device/initialize HTTP/1.1
Host: pretix.eu
Content-Type: application/json
{
"token": "kpp4jn8g2ynzonp6",
"hardware_brand": "Samsung",
"hardware_model": "Galaxy S",
"software_brand": "pretixdroid",
"software_version": "4.0.0"
}
Every initialization token can only be used once. On success, you will receive a response containing
information on your device as well as your API token:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
{
"organizer": "foo",
"device_id": 5,
"unique_serial": "HHZ9LW9JWP390VFZ",
"api_token": "1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd",
"name": "Bar"
}
Please make sure that you store this ``api_token`` value. We also recommend storing your device ID, your assigned
``unique_serial``, and the ``organizer`` you have access to, but that's up to you.
In case of an error, the response will look like this:
.. sourcecode:: http
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"token":["This initialization token has already been used."]}
Performing API requests
-----------------------
You need to include the API token with every request to pretix' API in the ``Authorization`` header
like the following:
.. sourcecode:: http
:emphasize-lines: 3
GET /api/v1/organizers/ HTTP/1.1
Host: pretix.eu
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
Updating the software version
-----------------------------
If your application is updated, we ask you to tell the server about the new version in use. You can do this at the
following endpoint:
.. sourcecode:: http
POST /api/v1/device/update HTTP/1.1
Host: pretix.eu
Content-Type: application/json
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
{
"hardware_brand": "Samsung",
"hardware_model": "Galaxy S",
"software_brand": "pretixdroid",
"software_version": "4.1.0"
}
Creating a new API key
----------------------
If you think your API key might have leaked or just want to be extra cautious, the API allows you to create a new key.
The old API key will be invalid immediately. A request for a new key looks like this:
.. sourcecode:: http
POST /api/v1/device/roll HTTP/1.1
Host: pretix.eu
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
The response will look like the response to the initialization request.
Removing a device
-----------------
If you want implement a way to to deprovision a device in your software, you can call the ``revoke`` endpoint to
invalidate your API key. There is no way to reverse this operation.
.. sourcecode:: http
POST /api/v1/device/revoke HTTP/1.1
Host: pretix.eu
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
This can also be done by the user through the web interface.
Permissions
-----------
Device authentication is currently hardcoded to grant the following permissions:
* View event meta data and products etc.
* View and change orders
Devices cannot change events or products and cannot access vouchers.

View File

@@ -6,42 +6,43 @@ with pretix' REST API, such as authentication, pagination and similar definition
.. _`rest-auth`:
Obtaining an API token
----------------------
To authenticate your API requests, you need to obtain an API token. You can create a
token in the pretix web interface on the level of organizer teams. Create a new team
or choose an existing team that has the level of permissions the token should have and
create a new token using the form below the list of team members:
.. image:: img/token_form.png
:class: screenshot
You can enter a description for the token to distinguish from other tokens later on.
Once you click "Add", you will be provided with an API token in the success message.
Copy this token, as you won't be able to retrieve it again.
.. image:: img/token_success.png
:class: screenshot
Authentication
--------------
To access the API, you need to present valid authentication credentials. pretix currently
supports the following authorization schemes:
You need to include the API token with every request to pretix' API in the ``Authorization`` header
like the following:
* :ref:`rest-tokenauth`: This is the simplest way and recommended for server-side applications
that interact with pretix without user interaction.
* :ref:`rest-oauth`: This is the recommended way to use if you write a third-party application
that users can connect with their pretix account. It provides the best user experience, but
requires user interaction and slightly more implementation effort.
* :ref:`rest-deviceauth`: This is the recommended way if you build apps or hardware devices that can
connect to pretix, e.g. for processing check-ins or to sell tickets offline. It provides a way
to uniquely identify devices and allows for a quick configuration flow inside your software.
* Authentication using browser sessions: This is used by the pretix web interface and it is *not*
officially supported for use by third-party applications. It might change or be removed at any
time without prior notice. If you use it, you need to comply with Django's `CSRF policies`_.
.. sourcecode:: http
:emphasize-lines: 3
Permissions
-----------
GET /api/v1/organizers/ HTTP/1.1
Host: pretix.eu
Authorization: Token e1l6gq2ye72thbwkacj7jbri7a7tvxe614ojv8ybureain92ocub46t5gab5966k
The API follows pretix team based permissions model. Each organizer can have several teams
each with it's own set of permissions. Each team can have any number of API keys attached.
To access a given endpoint the team the API key belongs to needs to have the corresponding
permission for the organizer/event being accessed.
Possible permissions are:
* Can create events
* Can change event settings
* Can change product settings
* Can view orders
* Can change orders
* Can view vouchers
* Can change vouchers
.. note:: The API currently also supports authentication via browser sessions, i.e. the
same way that you authenticate with pretix when using the browser interface.
Using this type of authentication is *not* officially supported for use by
third-party clients and might change or be removed at any time. We plan on
adding OAuth2 support in the future for user-level authentication. If you want
to use session authentication, be sure to comply with Django's `CSRF policies`_.
Compatibility
-------------
@@ -89,41 +90,6 @@ respective page.
The field ``results`` contains a list of objects representing the first results. For most
objects, every page contains 50 results.
Conditional fetching
--------------------
If you pull object lists from pretix' APIs regularly, we ask you to implement conditional fetching
to avoid unnecessary data traffic. This is not supported on all resources and we currently implement
two different mechanisms for different resources, which is necessary because we can only obtain best
efficiency for resources that do not support deletion operations.
Object-level conditional fetching
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The :ref:`rest-orders` resource list contains an HTTP header called ``X-Page-Generated`` containing the
current time on the server in ISO 8601 format. On your next request, you can pass this header
(as is, without any modifications necessary) as the ``modified_since`` query parameter and you will receive
a list containing only objects that have changed in the time since your last request.
List-level conditional fetching
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If modification checks are not possible with this granularity, you can instead check for the full list.
In this case, the list of objects may contain a regular HTTP header ``Last-Modified`` with the date of the
last modification to any item of that resource. You can then pass this date back in your next request in the
``If-Modified-Since`` header. If the any object has changed in the meantime, you will receive back a full list
(if something it missing, this means the object has been deleted). If nothing happened, we'll send back a
``304 Not Modified`` return code.
This is currently implemented on the following resources:
* :ref:`rest-categories`
* :ref:`rest-items`
* :ref:`rest-questions`
* :ref:`rest-quotas`
* :ref:`rest-subevents`
* :ref:`rest-taxrules`
Errors
------
@@ -148,7 +114,6 @@ Field specific input errors include the name of the offending fields as keys in
{"amount": ["A valid integer is required."], "description": ["This field may not be blank."]}
If you see errors of type ``429 Too Many Requests``, you should read our documentation on :ref:`rest-ratelimit`.
Data types
----------
@@ -181,37 +146,4 @@ as the string values ``true`` and ``false``.
If the ``ordering`` parameter is documented for a resource, you can use it to sort the result set by one of the allowed
fields. Prepend a ``-`` to the field name to reverse the sort order.
Idempotency
-----------
Our API supports an idempotency mechanism to make sure you can safely retry operations without accidentally performing
them twice. This is useful if an API call experiences interruptions in transit, e.g. due to a network failure, and you
do not know if it completed successfully.
To perform an idempotent request, add a ``X-Idempotency-Key`` header with a random string value (we recommend a version
4 UUID) to your request. If we see a second request with the same ``X-Idempotency-Key`` and the same ``Authorization``
and ``Cookie`` headers, we will not perform the action for a second time but return the exact same response instead.
Please note that this also goes for most error responses. For example, if we returned you a ``403 Permission Denied``
error and you retry with the same ``X-Idempotency-Key``, you will get the same error again, even if you were granted
permission in the meantime! This includes internal server errors on our side that might have been fixed in the meantime.
There are only three exceptions to the rule:
* Responses with status code ``409 Conflict`` are not cached. If you send the request again, it will be executed as a
new request, since these responses are intended to be retried.
* Rate-limited responses with status code ``429 Too Many Requests`` are not cached and you can safely retry them.
* Responses with status code ``503 Service Unavailable`` are not cached and you can safely retry them.
If you send a request with an ``X-Idempotency-Key`` header that we have seen before but that has not yet received a
response, you will receive a response with status code ``409 Conflict`` and are asked to retry after five seconds.
We store idempotency keys for 24 hours, so you should never retry a request after a longer time period.
All ``POST``, ``PUT``, ``PATCH``, or ``DELETE`` api calls support idempotency keys. Adding an idempotency key to a
``GET``, ``HEAD``, or ``OPTIONS`` request has no effect.
.. _CSRF policies: https://docs.djangoproject.com/en/1.11/ref/csrf/#ajax
.. _CSRF policies: https://docs.djangoproject.com/en/1.11/ref/csrf/#ajax

View File

@@ -14,7 +14,4 @@ in functionality over time.
:maxdepth: 2
fundamentals
auth
resources/index
ratelimit
webhooks

View File

@@ -1,207 +0,0 @@
.. _`rest-oauth`:
OAuth authentication / "Connect with pretix"
============================================
In addition to static tokens, pretix supports `OAuth2`_-based authentication starting with
pretix 1.16. This allows you to put a "Connect with pretix" button into your website or tool
that allows the user to easily set up a connection between the two systems.
If you haven't worked with OAuth before, have a look at the `OAuth2 Simplified`_ tutorial.
Registering an application
--------------------------
To use OAuth, you need to register your application with the pretix instance you want to connect to.
In order to do this, log in to your pretix account and go to your user settings. Click on "Authorized applications"
first and then on "Manage your own apps". From there, you can "Create a new application".
You should fill in a descriptive name of your application that allows users to recognize who you are. You also need to
give a list of fully-qualified URLs that users will be redirected to after a successful authorization. After you pressed
"Save", you will be presented with a client ID and a client secret. Please note them down and treat the client secret
like a password; it should not become available to your users.
Obtaining an authorization grant
--------------------------------
To authorize a new user, link or redirect them to the ``authorize`` endpoint, passing your client ID as a query
parameter. Additionally, you can pass a scope (currently either ``read``, ``write``, or ``read write``)
and an URL the user should be redirected to after successful or failed authorization. You also need to pass the
``response_type`` parameter with a value of ``code``. Example::
https://pretix.eu/api/v1/oauth/authorize?client_id=lsLi0hNL0vk53mEdYjNJxHUn1PcO1R6wVg81dLNT&response_type=code&scope=read+write&redirect_uri=https://pretalx.com
To prevent CSRF attacks, you can also optionally pass a ``state`` parameter with a random string. Later, when
redirecting back to your application, we will pass the same ``state`` parameter back to you, so you can compare if they
match.
After the user granted or denied access, they will be redirected back either to the ``redirect_url`` you passed in the
query or to the first redirect URL configured in your application settings.
On successful registration, we will append the query parameter ``code`` to the URL containing an authorization code.
For example, we might redirect the user to this URL::
https://pretalx.com/?code=eYBBf8gmeD4E01HLoj0XflqO4Lg3Cw&state=e3KCh9mfx07qxU4bRpXk
You will need this ``code`` parameter to perform the next step.
On a failed registration, a query string like ``?error=access_denied`` will be appended to the redirection URL.
.. note:: In this step, the user is allowed to restrict your access to certain organizer accounts. If you try to
re-authenticate the user later, the user might be instantly redirected back to you if authorization is already
given and would therefore be unable to review their organizer restriction settings. You can append the
``approval_prompt=force`` query parameter if you want to make sure the user actively needs to confirm the
authorization.
Getting an access token
-----------------------
Using the ``code`` value you obtained above and your client ID, you can now request an access token that actually gives
access to the API. The ``token`` endpoint expects you to authenticate using `HTTP Basic authentication`_ using your client
ID as a username and your client secret as a password. You are also required to again supply the same ``redirect_uri``
parameter that you used for the authorization.
.. http:get:: /api/v1/oauth/token
Request a new access token
**Example request**:
.. sourcecode:: http
POST /api/v1/oauth/token HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Authorization: Basic bHNMaTBoTkwwdms1M21FZFlqTkp4SFVuMVBjTzFSNndWZzgxZExOVDplSmpzZVA0UjJMN0hMcjBiS0p1b3BmbnJtT2cyY3NDeTdYaFVVZ0FoalhUU0NhZHhRTjk3cVNvMkpPaXlWTFpQOEozaTVQd1FVdFIwNUNycG5ac2Z0bXJjdmNTbkZ1SkFmb2ZsUTdZUDRpSjZNTWFYTHIwQ0FpNlhIRFJjV1Awcg==
grant_type=authorization_code&code=eYBBf8gmeD4E01HLoj0XflqO4Lg3Cw&redirect_uri=https://pretalx.com
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"access_token": "i3ytqTSRWsKp16fqjekHXa4tdM4qNC",
"expires_in": 86400,
"token_type": "Bearer",
"scope": "read write",
"refresh_token": "XBK0r8z4A4TTeR9LyMUyU2AM5rqpXp"
}
:statuscode 200: no error
:statuscode 401: Authentication failure
As you can see, you receive two types of tokens: One "access token", and one "refresh token". The access token is valid
for a day and can be used to actually access the API. The refresh token does not have an expiration date and can be used
to obtain a new access_token after a day, so you should make sure to store the access token safely if you need long-term
access.
Using the API with an access token
----------------------------------
You can supply a valid access token as a ``Bearer``-type token in the ``Authorization`` header to get API access.
.. sourcecode:: http
:emphasize-lines: 3
GET /api/v1/organizers/ HTTP/1.1
Host: pretix.eu
Authorization: Bearer i3ytqTSRWsKp16fqjekHXa4tdM4qNC
Refreshing an access token
--------------------------
You can obtain a new access token using your refresh token any time. This can be done using the same ``token`` endpoint
used to obtain the first access token above, but with a different set of parameters:
.. sourcecode:: http
POST /api/v1/oauth/token HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Authorization: Basic bHNMaTBoTkwwdms1M21FZFlqTkp4SFVuMVBjTzFSNndWZzgxZExOVDplSmpzZVA0UjJMN0hMcjBiS0p1b3BmbnJtT2cyY3NDeTdYaFVVZ0FoalhUU0NhZHhRTjk3cVNvMkpPaXlWTFpQOEozaTVQd1FVdFIwNUNycG5ac2Z0bXJjdmNTbkZ1SkFmb2ZsUTdZUDRpSjZNTWFYTHIwQ0FpNlhIRFJjV1Awcg==
grant_type=refresh_token&refresh_token=XBK0r8z4A4TTeR9LyMUyU2AM5rqpXp
The previous access token will instantly become invalid.
Revoking a token
----------------
If you don't need a token any more or if you believe it may have been compromised, you can use the ``revoke_token``
endpoint to revoke it.
.. http:get:: /api/v1/oauth/revoke_token
Revoke an access or refresh token. If you revoke an access token, you can still create a new one using the refresh token. If you
revoke a refresh token, the connected access token will also be revoked.
**Example request**:
.. sourcecode:: http
POST /api/v1/oauth/revoke_token HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Authorization: Basic bHNMaTBoTkwwdms1M21FZFlqTkp4SFVuMVBjTzFSNndWZzgxZExOVDplSmpzZVA0UjJMN0hMcjBiS0p1b3BmbnJtT2cyY3NDeTdYaFVVZ0FoalhUU0NhZHhRTjk3cVNvMkpPaXlWTFpQOEozaTVQd1FVdFIwNUNycG5ac2Z0bXJjdmNTbkZ1SkFmb2ZsUTdZUDRpSjZNTWFYTHIwQ0FpNlhIRFJjV1Awcg==
token=XBK0r8z4A4TTeR9LyMUyU2AM5rqpXp
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
:statuscode 200: no error
:statuscode 401: Authentication failure
If you want to revoke your client secret, you can generate a new one in the list of your managed applications in the
pretix user interface.
Fetching the user profile
-------------------------
If you need the user's meta data, you can fetch it here:
.. http:get:: /api/v1/me
Returns the profile of the authenticated user
**Example request**:
.. sourcecode:: http
GET /api/v1/me HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Authorization: Bearer i3ytqTSRWsKp16fqjekHXa4tdM4qNC
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
email: "admin@localhost",
fullname: "John Doe",
locale: "de",
timezone: "Europe/Berlin"
}
:statuscode 200: no error
:statuscode 401: Authentication failure
.. _OAuth2: https://en.wikipedia.org/wiki/OAuth
.. _OAuth2 Simplified: https://aaronparecki.com/oauth-2-simplified/
.. _HTTP Basic authentication: https://en.wikipedia.org/wiki/Basic_access_authentication

View File

@@ -1,31 +0,0 @@
.. _`rest-ratelimit`:
Rate limiting
=============
.. note:: This page only applies to the pretix Hosted service at pretix.eu. APIs of custom pretix installations do not
enforce any rate limiting by default.
All authenticated requests to pretix' API are rate limited. If you exceed the limits, you will receive a response
with HTTP status code ``429 Too Many Requests``. This response will have a ``Retry-After`` header, containing the number
of seconds you are supposed to wait until you try again. We expect that all API clients respect this. If you continue
to burst requests after a ``429`` status code, we might get in touch with you or, in extreme cases, disable your API
access.
Currently, the following rate limits apply:
.. rst-class:: rest-resource-table
===================================== =================================================================================
Authentication method Rate limit
===================================== =================================================================================
:ref:`rest-deviceauth` 360 requests per minute per device
:ref:`rest-tokenauth` 360 requests per minute per organizer account
:ref:`rest-oauth` 360 requests per minute per combination of accessed organizer and OAuth application
Session authentication *Not an officially supported authentication method for external access*
===================================== =================================================================================
If you require a higher rate limit, please get in touch at support@pretix.eu and tell us about your use case, we are
sure we can work something out.

View File

@@ -1,131 +0,0 @@
pretix Hosted billing invoices
==============================
This endpoint allows you to access invoices you received for pretix Hosted. It only contains invoices created starting
November 2017.
.. note:: Only available on pretix Hosted, not on self-hosted pretix instances.
Resource description
--------------------
The resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
invoice_number string Invoice number
date_issued date Invoice date
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/billing_invoices/
Returns a list of all invoices to a given organizer.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/billing_invoices/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"invoice_number": "R2019002",
"date_issued": "2019-06-03"
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``date_issued`` and
its reverse, ``-date_issued``. Default: ``date_issued``.
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/billing_invoices/(invoice_number)/
Returns information on one invoice, identified by its invoice number.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/billing_invoices/R2019002/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"invoice_number": "R2019002",
"date_issued": "2019-06-03"
}
:param organizer: The ``slug`` field of the organizer to fetch
:param invoice_number: The ``invoice_number`` field of the invoice to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/billing_invoices/(invoice_number)/download/
Download an invoice in PDF format.
.. warning:: After we created the invoices, they are placed in review with our accounting department. You will
already see them in the API at this point, but you are not able to download them until they completed
review and are sent to you via email. This usually takes a few hours. If you try to download them
in this time frame, you will receive a status code :http:statuscode:`423`.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/billing_invoices/R2019002/download/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/pdf
...
:param organizer: The ``slug`` field of the organizer to fetch
:param invoice_number: The ``invoice_number`` field of the invoice to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 423: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
seconds.

View File

@@ -1,264 +0,0 @@
.. _rest-carts:
Cart positions
==============
The API provides limited access to the cart position data model. This API currently only allows creating and deleting
cart positions to reserve quota.
Cart position resource
----------------------
The cart position resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the cart position
cart_id string Identifier of the cart this belongs to. Needs to end
in "@api" for API-created positions.
datetime datetime Time of creation
expires datetime The cart position will expire at this time and no longer block quota
item integer ID of the item
variation integer ID of the variation (or ``null``)
price money (string) Price of this position
attendee_name string Specified attendee name for this position (or ``null``)
attendee_name_parts object of strings Composition of attendee name (i.e. first name, last name, …)
attendee_email string Specified attendee email address for this position (or ``null``)
voucher integer Internal ID of the voucher used for this position (or ``null``)
addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
subevent integer ID of the date inside an event series this position belongs to (or ``null``).
answers list of objects Answers to user-defined questions
├ question integer Internal ID of the answered question
├ answer string Text representation of the answer
├ question_identifier string The question's ``identifier`` field
├ options list of integers Internal IDs of selected option(s)s (only for choice types)
└ option_identifiers list of strings The ``identifier`` fields of the selected option(s)s
===================================== ========================== =======================================================
.. versionchanged:: 1.17
This resource has been added.
Cart position endpoints
-----------------------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/
Returns a list of API-created cart positions.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/cartpositions/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
X-Page-Generated: 2017-12-01T10:00:00Z
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"cart_id": "XwokV8FojQviD9jhtDzKvHFdlLRNMhlfo3cNjGbuK6MUTQDT@api",
"item": 1,
"variation": null,
"price": "23.00",
"attendee_name": null,
"attendee_name_parts": {},
"attendee_email": null,
"voucher": null,
"addon_to": null,
"subevent": null,
"datetime": "2018-06-11T10:00:00Z",
"expires": "2018-06-11T10:00:00Z",
"includes_tax": true,
"answers": []
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/(id)/
Returns information on one cart position, identified by its internal ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/cartpositions/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"cart_id": "XwokV8FojQviD9jhtDzKvHFdlLRNMhlfo3cNjGbuK6MUTQDT@api",
"item": 1,
"variation": null,
"price": "23.00",
"attendee_name": null,
"attendee_name_parts": {},
"attendee_email": null,
"voucher": null,
"addon_to": null,
"subevent": null,
"datetime": "2018-06-11T10:00:00Z",
"expires": "2018-06-11T10:00:00Z",
"includes_tax": true,
"answers": []
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the position to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested cart position does not exist.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/
Creates a new cart position.
.. warning:: This endpoint is considered **experimental**. It might change at any time without prior notice.
.. warning::
This endpoint is intended for advanced users. It is not designed to be used to build your own shop frontend.
There is a lot that it does not or can not do, and you will need to be careful using it.
It allows to bypass many of the restrictions imposed when creating a cart through the
regular shop.
Specifically, this endpoint currently
* does not validate if products are only to be sold in a specific time frame
* does not validate if the event's ticket sales are already over or haven't started
* does not support add-on products at the moment
* does not check or calculate prices but believes any prices you send
* does not support the redemption of vouchers
* does not prevent you from buying items that can only be bought with a voucher
* does not support file upload questions
You can supply the following fields of the resource:
* ``cart_id`` (optional, needs to end in ``@api``)
* ``item``
* ``variation`` (optional)
* ``price``
* ``attendee_name`` **or** ``attendee_name_parts`` (optional)
* ``attendee_email`` (optional)
* ``subevent`` (optional)
* ``expires`` (optional)
* ``includes_tax`` (optional)
* ``answers``
* ``question``
* ``answer``
* ``options``
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/cartpositions/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"item": 1,
"variation": null,
"price": "23.00",
"attendee_name_parts": {
"given_name": "Peter",
"family_name": "Miller"
},
"attendee_email": null,
"answers": [
{
"question": 1,
"answer": "23",
"options": []
}
],
"subevent": null
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
(Full cart position resource, see above.)
:param organizer: The ``slug`` field of the organizer of the event to create a position for
:param event: The ``slug`` field of the event to create a position for
:statuscode 201: no error
:statuscode 400: The item could not be created due to invalid submitted data or lack of quota.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
order.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/(id)/
Deletes a cart position, identified by its internal ID.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/cartpositions/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
Content-Type: application/json
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the position to delete
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested cart position does not exist.

View File

@@ -1,5 +1,3 @@
.. _`rest-categories`:
Item categories
===============
@@ -16,23 +14,14 @@ Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the category
name multi-lingual string The category's visible name
internal_name string An optional name that is only used in the backend
description multi-lingual string A public description (might include markdown, can
be ``null``)
position integer An integer, used for sorting the categories
is_addon boolean If ``true``, items within this category are not on sale
is_addon boolean If ``True``, items within this category are not on sale
on their own but the category provides a source for
defining add-ons for other products.
===================================== ========================== =======================================================
.. versionchanged:: 1.14
The operations POST, PATCH, PUT and DELETE have been added.
.. versionchanged:: 1.16
The field ``internal_name`` has been added.
Endpoints
---------
@@ -65,7 +54,6 @@ Endpoints
{
"id": 1,
"name": {"en": "Tickets"},
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false
@@ -107,7 +95,6 @@ Endpoints
{
"id": 1,
"name": {"en": "Tickets"},
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false
@@ -119,121 +106,3 @@ Endpoints
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/categories/
Creates a new category
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/categories/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"name": {"en": "Tickets"},
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": {"en": "Tickets"},
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false
}
:param organizer: The ``slug`` field of the organizer of the event to create a category for
:param event: The ``slug`` field of the event to create a category for
:statuscode 201: no error
:statuscode 400: The category could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/categories/(id)/
Update a category. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/categories/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"is_addon": true
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": {"en": "Tickets"},
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": true
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the category to modify
:statuscode 200: no error
:statuscode 400: The category could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/category/(id)/
Delete a category.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/categories/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the category to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.

View File

@@ -44,10 +44,6 @@ include_pending boolean If ``true``, th
Endpoints
---------
.. versionchanged:: 1.15
The ``../status/`` detail endpoint has been added.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
Returns a list of all check-in lists within a given event.
@@ -132,72 +128,6 @@ Endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(id)/status/
Returns detailed status information on a check-in list, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/status/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"checkin_count": 17,
"position_count": 42,
"event": {
"name": "Demo Conference"
},
"items": [
{
"name": "T-Shirt",
"id": 1,
"checkin_count": 1,
"admission": false,
"position_count": 1,
"variations": [
{
"value": "Red",
"id": 1,
"checkin_count": 1,
"position_count": 12
},
{
"value": "Blue",
"id": 2,
"checkin_count": 4,
"position_count": 8
}
]
},
{
"name": "Ticket",
"id": 2,
"checkin_count": 15,
"admission": true,
"position_count": 22,
"variations": []
}
]
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the check-in list to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
Creates a new check-in list.
@@ -324,36 +254,11 @@ Endpoints
Order position endpoints
------------------------
.. versionchanged:: 1.15
The order positions endpoint has been extended by the filter queries ``item__in``, ``variation__in``,
``order__status__in``, ``subevent__in``, ``addon_to__in``, and ``search``. The search for attendee names and order
codes is now case-insensitive.
The ``.../redeem/`` endpoint has been added.
.. versionchanged:: 2.0
The order positions endpoint has been extended by the filter queries ``voucher`` and ``voucher__code``.
.. versionchanged:: 2.7
The resource now contains the new attributes ``require_attention`` and ``order__status`` and accepts the new
``ignore_status`` filter. The ``attendee_name`` field is now "smart" (see below) and the redemption endpoint
returns ``400`` instead of ``404`` on tickets which are known but not paid.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/
Returns a list of all order positions within a given event. The result is the same as
the :ref:`order-position-resource`, with the following differences:
* The ``checkins`` value will only include check-ins for the selected list.
* An additional boolean property ``require_attention`` will inform you whether either the order or the item
have the ``checkin_attention`` flag set.
* If ``attendee_name`` is empty, it will automatically fall back to values from a parent product or from invoice
addresses.
the :ref:`order-position-resource`, with one important difference: the ``checkins`` value will only include
check-ins for the selected list.
**Example request**:
@@ -384,9 +289,6 @@ Order position endpoints
"variation": null,
"price": "23.00",
"attendee_name": "Peter",
"attendee_name_parts": {
"full_name": "Peter",
},
"attendee_email": null,
"voucher": null,
"tax_rate": "0.00",
@@ -395,7 +297,6 @@ Order position endpoints
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
"pseudonymization_id": "MQLJvANO3B",
"checkins": [
{
"list": 1,
@@ -420,32 +321,19 @@ Order position endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string ignore_status: If set to ``true``, results will be returned regardless of the state of
the order they belong to and you will need to do your own filtering by order status.
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``order__code``,
``order__datetime``, ``positionid``, ``attendee_name``, ``last_checked_in`` and ``order__email``. Default:
``attendee_name,positionid``
:query string order: Only return positions of the order with the given order code
:query string search: Fuzzy search matching the attendee name, order code, invoice address name as well as to the beginning of the secret.
:query integer item: Only return positions with the purchased item matching the given ID.
:query integer item__in: Only return positions with the purchased item matching one of the given comma-separated IDs.
:query integer variation: Only return positions with the purchased item variation matching the given ID.
:query integer variation__in: Only return positions with one of the purchased item variation matching the given
comma-separated IDs.
:query string attendee_name: Only return positions with the given value in the attendee_name field. Also, add-on
products positions are shown if they refer to an attendee with the given name.
:query string secret: Only return positions with the given ticket secret.
:query string order__status: Only return positions with the given order status.
:query string order__status__in: Only return positions with one the given comma-separated order status.
:query boolean has_checkin: If set to ``true`` or ``false``, only return positions that have or have not been
checked in already.
:query bollean has_checkin: If set to ``true`` or ``false``, only return positions that have or have not been
checked in already on this list.
:query integer subevent: Only return positions of the sub-event with the given ID
:query integer subevent__in: Only return positions of one of the sub-events with the given comma-separated IDs
:query integer addon_to: Only return positions that are add-ons to the position with the given ID.
:query integer addon_to__in: Only return positions that are add-ons to one of the positions with the given
comma-separated IDs.
:query string voucher: Only return positions with a specific voucher.
:query string voucher__code: Only return positions with a specific voucher code.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param list: The ID of the check-in list to look for
@@ -454,26 +342,17 @@ Order position endpoints
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested check-in list does not exist.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)/
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)
Returns information on one order position, identified by its internal ID.
The result is the same as the :ref:`order-position-resource`, with the following differences:
* The ``checkins`` value will only include check-ins for the selected list.
* An additional boolean property ``require_attention`` will inform you whether either the order or the item
have the ``checkin_attention`` flag set.
* If ``attendee_name`` is empty, it will automatically fall back to values from a parent product or from invoice
addresses.
**Instead of an ID, you can also use the ``secret`` field as the lookup parameter.**
The result format is the same as the :ref:`order-position-resource`, with one important difference: the
``checkins`` value will only include check-ins for the selected list.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/23442/ HTTP/1.1
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
@@ -493,9 +372,6 @@ Order position endpoints
"variation": null,
"price": "23.00",
"attendee_name": "Peter",
"attendee_name_parts": {
"full_name": "Peter",
},
"attendee_email": null,
"voucher": null,
"tax_rate": "0.00",
@@ -504,7 +380,6 @@ Order position endpoints
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
"pseudonymization_id": "MQLJvANO3B",
"checkins": [
{
"list": 1,
@@ -534,139 +409,3 @@ Order position endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested order position or check-in list does not exist.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)/redeem/
Tries to redeem an order position, identified by its internal ID, i.e. checks the attendee in. This endpoint
accepts a number of optional requests in the body.
**Instead of an ID, you can also use the ``secret`` field as the lookup parameter.**
:<json boolean questions_supported: When this parameter is set to ``true``, handling of questions is supported. If
you do not implement question handling in your user interface, you **must**
set this to ``false``. In that case, questions will just be ignored. Defaults
to ``true``.
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
:<json boolean force: Specifies that the check-in should succeed regardless of previous check-ins or required
questions that have not been filled. Defaults to ``false``.
:<json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
Defaults to ``false`` and only works when ``include_pending`` is set on the check-in
list.
:<json string nonce: You can set this parameter to a unique random value to identify this check-in. If you're sending
this request twice with the same nonce, the second request will also succeed but will always
create only one check-in object even when the previous request was successful as well. This
allows for a certain level of idempotency and enables you to re-try after a connection failure.
:<json object answers: If questions are supported/required, you may/must supply a mapping of question IDs to their
respective answers. The answers should always be strings. In case of (multiple-)choice-type
answers, the string should contain the (comma-separated) IDs of the selected options.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/234/redeem/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
{
"force": false,
"ignore_unpaid": false,
"nonce": "Pvrk50vUzQd0DhdpNRL4I4OcXsvg70uA",
"datetime": null,
"questions_supported": true,
"answers": {
"4": "XS"
}
}
**Example successful response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"status": "ok",
"position": {
}
}
**Example response with required questions**:
.. sourcecode:: http
HTTP/1.1 400 Bad Request
Content-Type: text/json
{
"status": "incomplete",
"position": {
},
"questions": [
{
"id": 1,
"question": {"en": "T-Shirt size"},
"type": "C",
"required": false,
"items": [1, 2],
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": true,
"options": [
{
"id": 1,
"identifier": "LVETRWVU",
"position": 0,
"answer": {"en": "S"}
},
{
"id": 2,
"identifier": "DFEMJWMJ",
"position": 1,
"answer": {"en": "M"}
},
{
"id": 3,
"identifier": "W9AH7RDE",
"position": 2,
"answer": {"en": "L"}
}
]
}
]
}
**Example error response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"status": "error",
"reason": "unpaid",
"position": {
}
}
Possible error reasons:
* ``unpaid`` - Ticket is not paid for or has been refunded
* ``already_redeemed`` - Ticket already has been redeemed
* ``product`` - Tickets with this product may not be scanned at this device
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param list: The ID of the check-in list to look for
:param id: The ``id`` field of the order position to fetch
:statuscode 201: no error
:statuscode 400: Invalid or incomplete request, see above
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested order position or check-in list does not exist.

View File

@@ -15,7 +15,6 @@ name multi-lingual string The event's ful
slug string A short form of the name, used e.g. in URLs.
live boolean If ``true``, the event ticket shop is publicly
available.
testmode boolean If ``true``, the ticket shop is in test mode.
currency string The currency this event is handled in.
date_from datetime The event's start date
date_to datetime The event's end date (or ``null``)
@@ -25,35 +24,15 @@ is_public boolean If ``true``, th
presale_start datetime The date at which the ticket shop opens (or ``null``)
presale_end datetime The date at which the ticket shop closes (or ``null``)
location multi-lingual string The event location (or ``null``)
has_subevents boolean ``true`` if the event series feature is active for this
event. Cannot change after event is created.
has_subevents boolean ``True`` if the event series feature is active for this
event
meta_data dict Values set for organizer-specific meta data parameters.
plugins list A list of package names of the enabled plugins for this
event.
===================================== ========================== =======================================================
.. versionchanged:: 1.7
The ``meta_data`` field has been added.
.. versionchanged:: 1.15
The ``plugins`` field has been added.
The operations POST, PATCH, PUT and DELETE have been added.
.. versionchanged:: 2.1
Filters have been added to the list of events.
.. versionchanged:: 2.5
The ``testmode`` attribute has been added.
.. versionchanged:: 2.8
When cloning events, the ``testmode`` attribute will now be cloned, too.
Endpoints
---------
@@ -61,8 +40,6 @@ Endpoints
Returns a list of all events within a given organizer the authenticated user/token has access to.
Permission required: "Can change event settings"
**Example request**:
.. sourcecode:: http
@@ -88,7 +65,6 @@ Endpoints
"name": {"en": "Sample Conference"},
"slug": "sampleconf",
"live": false,
"testmode": false,
"currency": "EUR",
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
@@ -98,27 +74,12 @@ Endpoints
"presale_end": null,
"location": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
"pretix.plugins.banktransfer"
"pretix.plugins.stripe"
"pretix.plugins.paypal"
"pretix.plugins.ticketoutputpdf"
]
"meta_data": {}
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:query is_public: If set to ``true``/``false``, only events with a matching value of ``is_public`` are returned.
:query live: If set to ``true``/``false``, only events with a matching value of ``live`` are returned.
:query has_subevents: If set to ``true``/``false``, only events with a matching value of ``has_subevents`` are returned.
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned. Event series are never (always) returned.
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned. Event series are never (always) returned.
:query ends_after: If set to a date and time, only events that happen during of after the given time are returned. Event series are never returned.
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``date_from`` and
``slug``. Keep in mind that ``date_from`` of event series does not really tell you anything.
Default: ``slug``.
:param organizer: The ``slug`` field of a valid organizer
:statuscode 200: no error
:statuscode 401: Authentication failure
@@ -128,8 +89,6 @@ Endpoints
Returns information on one event, identified by its slug.
Permission required: "Can change event settings"
**Example request**:
.. sourcecode:: http
@@ -150,7 +109,6 @@ Endpoints
"name": {"en": "Sample Conference"},
"slug": "sampleconf",
"live": false,
"testmode": false,
"currency": "EUR",
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
@@ -160,13 +118,7 @@ Endpoints
"presale_end": null,
"location": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
"pretix.plugins.banktransfer"
"pretix.plugins.stripe"
"pretix.plugins.paypal"
"pretix.plugins.ticketoutputpdf"
]
"meta_data": {}
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -174,247 +126,3 @@ Endpoints
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
.. http:post:: /api/v1/organizers/(organizer)/events/
Creates a new event
Please note that events cannot be created as 'live' using this endpoint. Quotas and payment must be added to the
event before sales can go live.
Permission required: "Can create events"
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"name": {"en": "Sample Conference"},
"slug": "sampleconf",
"live": false,
"testmode": false,
"currency": "EUR",
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"is_public": false,
"presale_start": null,
"presale_end": null,
"location": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"name": {"en": "Sample Conference"},
"slug": "sampleconf",
"live": false,
"testmode": false,
"currency": "EUR",
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"is_public": false,
"presale_start": null,
"presale_end": null,
"location": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
]
}
:param organizer: The ``slug`` field of the organizer of the event to create.
:statuscode 201: no error
:statuscode 400: The event could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/clone/
Creates a new event with properties as set in the request body. The properties that are copied are: 'is_public',
`testmode`, settings, plugin settings, items, variations, add-ons, quotas, categories, tax rules, questions.
If the 'plugins' and/or 'is_public' fields are present in the post body this will determine their value. Otherwise
their value will be copied from the existing event.
Please note that you can only copy from events under the same organizer.
Permission required: "Can create events"
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/clone/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"name": {"en": "Sample Conference"},
"slug": "sampleconf",
"live": false,
"testmode": false,
"currency": "EUR",
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"is_public": false,
"presale_start": null,
"presale_end": null,
"location": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"name": {"en": "Sample Conference"},
"slug": "sampleconf",
"live": false,
"testmode": false,
"currency": "EUR",
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"is_public": false,
"presale_start": null,
"presale_end": null,
"location": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
]
}
:param organizer: The ``slug`` field of the organizer of the event to create.
:param event: The ``slug`` field of the event to copy settings and items from.
:statuscode 201: no error
:statuscode 400: The event could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/
Updates an event
Permission required: "Can change event settings"
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"plugins": [
"pretix.plugins.banktransfer",
"pretix.plugins.stripe",
"pretix.plugins.paypal",
"pretix.plugins.pretixdroid"
]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"name": {"en": "Sample Conference"},
"slug": "sampleconf",
"live": false,
"testmode": false,
"currency": "EUR",
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"is_public": false,
"presale_start": null,
"presale_end": null,
"location": null,
"has_subevents": false,
"meta_data": {},
"plugins": [
"pretix.plugins.banktransfer",
"pretix.plugins.stripe",
"pretix.plugins.paypal",
"pretix.plugins.pretixdroid"
]
}
:param organizer: The ``slug`` field of the organizer of the event to update
:param event: The ``slug`` field of the event to update
:statuscode 200: no error
:statuscode 400: The event could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/items/(id)/
Delete an event. Note that events with orders cannot be deleted to ensure data integrity.
Permission required: "Can change event settings"
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.

View File

@@ -11,16 +11,11 @@ Resources and endpoints
categories
items
item_variations
item_bundles
item_add-ons
questions
question_options
quotas
orders
invoices
vouchers
checkinlists
waitinglist
carts
webhooks
billing_invoices

View File

@@ -13,7 +13,7 @@ Field Type Description
===================================== ========================== =======================================================
number string Invoice number (with prefix)
order string Order code of the order this invoice belongs to
is_cancellation boolean ``true``, if this invoice is the cancellation of a
is_cancellation boolean ``True``, if this invoice is the cancellation of a
different invoice.
invoice_from string Sender address
invoice_to string Receiver address
@@ -223,59 +223,3 @@ Endpoints
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
seconds.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/reissue/
Cancels the invoice and creates a new one.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/invoices/00001/reissue/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
Content-Type: application/pdf
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to reissue
:statuscode 200: no error
:statuscode 400: The invoice has already been canceled
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/regenerate/
Re-generates the invoice from order data.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/invoices/00001/regenerate/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
Content-Type: application/pdf
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to regenerate
:statuscode 200: no error
:statuscode 400: The invoice has already been canceled
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.

View File

@@ -148,7 +148,7 @@ Endpoints
.. sourcecode:: http
HTTP/1.1 201 Created
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
@@ -189,7 +189,7 @@ Endpoints
{
"min_count": 0,
"max_count": 10
"max_count": 10,
}
**Example response**:

View File

@@ -1,242 +0,0 @@
Item bundles
============
Resource description
--------------------
With bundles, you can specify products that are included within other products. There are two premier use cases of this:
* Package discounts. For example, you could offer a discounted package that includes three tickets but can only be
bought as a whole. With a bundle including three times the usual product, the package will automatically pull three
sub-items into the cart, making sure of correct quota calculation and issuance of the correct number of tickets.
* Tax splitting. For example, if your conference ticket includes a part that is subject to different taxation and that
you need to put on the invoice separately. When you putting a "designated price" on a bundled sub-item, pretix will
use that price to show a split taxation.
The bundles resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the bundling configuration
bundled_item integer Internal ID of the item that is included.
bundled_variation integer Internal ID of the variation of the item (or ``null``).
count integer Number of items included
designated_price money (string) Designated price of the bundled product. This will be
used to split the price of the base item e.g. for mixed
taxation. This is not added to the price.
===================================== ========================== =======================================================
.. versionchanged:: 2.6
This resource has been added.
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/items/(item)/bundles/
Returns a list of all bundles for a given item.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/items/11/bundles/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 3,
"bundled_item": 3,
"bundled_variation": null,
"count": 1,
"designated_price": "0.00"
},
{
"id": 3,
"bundled_item": 3,
"bundled_variation": null,
"count": 2,
"designated_price": "1.50"
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param item: The ``id`` field of the item to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/item does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/items/(item)/bundles/(id)/
Returns information on one bundle configuration, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/items/1/bundles/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 3,
"bundled_item": 3,
"bundled_variation": null,
"count": 2,
"designated_price": "1.50"
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param item: The ``id`` field of the item to fetch
:param id: The ``id`` field of the bundle to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/bigevents/events/sampleconf/items/1/bundles/
Creates a new bundle configuration
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/(organizer)/events/(event)/items/(item)/bundles/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"bundled_item": 3,
"bundled_variation": null,
"count": 2,
"designated_price": "1.50"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 3,
"bundled_item": 3,
"bundled_variation": null,
"count": 2,
"designated_price": "1.50"
}
:param organizer: The ``slug`` field of the organizer of the event/item to create a bundle-configuration for
:param event: The ``slug`` field of the event to create a bundle configuration for
:param item: The ``id`` field of the item to create a bundle configuration for
:statuscode 201: no error
:statuscode 400: The bundle could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/items/(item)/bundles/(id)/
Update a bundle configuration. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all
fields of the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields
that you want to change.
You can change all fields of the resource except the ``id`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/items/1/bundles/3/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"count": 2
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 3,
"bundled_item": 3,
"bundled_variation": null,
"count": 2,
"designated_price": "1.50"
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param item: The ``id`` field of the item to modify
:param id: The ``id`` field of the bundle to modify
:statuscode 200: no error
:statuscode 400: The bundle configuration could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/items/(id)/bundles/(id)/
Delete a bundle configuration.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/items/1/bundles/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the item to modify
:param id: The ``id`` field of the bundle to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.

View File

@@ -6,7 +6,7 @@ Resource description
Variations of items can be use for products (items) that are available in different sizes, colors or other variations
of the same product.
The variations resource contains the following public fields:
The addons resource contains the following public fields:
.. rst-class:: rest-resource-table
@@ -18,18 +18,12 @@ default_price money (string) The price set d
price money (string) The price used for this variation. This is either the
same as ``default_price`` if that value is set or equal
to the item's ``default_price`` (read-only).
original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
active boolean If ``false``, this variation will not be sold or shown.
active boolean If ``False``, this variation will not be sold or shown.
description multi-lingual string A public description of the variation. May contain
Markdown syntax or can be ``null``.
position integer An integer, used for sorting
===================================== ========================== =======================================================
.. versionchanged:: 2.7
The attribute ``original_price`` has been added.
.. versionchanged:: 1.12
This resource has been added.
@@ -73,8 +67,7 @@ Endpoints
},
"position": 0,
"default_price": "223.00",
"price": 223.0,
"original_price": null,
"price": 223.0
},
{
"id": 3,
@@ -127,7 +120,6 @@ Endpoints
},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": true,
"description": null,
"position": 0
@@ -166,7 +158,7 @@ Endpoints
.. sourcecode:: http
HTTP/1.1 201 Created
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
@@ -175,7 +167,6 @@ Endpoints
"value": {"en": "Student"},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": true,
"description": null,
"position": 0
@@ -225,7 +216,6 @@ Endpoints
"value": {"en": "Student"},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": false,
"description": null,
"position": 1

View File

@@ -1,5 +1,3 @@
.. _rest-items:
Items
=====
@@ -16,40 +14,36 @@ Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the item
name multi-lingual string The item's visible name
internal_name string An optional name that is only used in the backend
default_price money (string) The item price that is applied if the price is not
overwritten by variations or other options.
category integer The ID of the category this item belongs to
(or ``null``).
active boolean If ``false``, the item is hidden from all public lists
active boolean If ``False``, the item is hidden from all public lists
and will not be sold.
description multi-lingual string A public description of the item. May contain Markdown
syntax or can be ``null``.
free_price boolean If ``true``, customers can change the price at which
free_price boolean If ``True``, customers can change the price at which
they buy the product (however, the price can't be set
lower than the price defined by ``default_price`` or
otherwise).
tax_rate decimal (string) The VAT rate to be applied for this item (read-only,
set through ``tax_rule``).
tax_rate decimal (string) The VAT rate to be applied for this item.
tax_rule integer The internal ID of the applied tax rule (or ``null``).
admission boolean ``true`` for items that grant admission to the event
(such as primary tickets) and ``false`` for others
admission boolean ``True`` for items that grant admission to the event
(such as primary tickets) and ``False`` for others
(such as add-ons or merchandise).
position integer An integer, used for sorting
picture string A product picture to be displayed in the shop
(read-only, can be ``null``).
sales_channels list of strings Sales channels this product is available on, such as
``"web"`` or ``"resellers"``. Defaults to ``["web"]``.
(read-only).
available_from datetime The first date time at which this item can be bought
(or ``null``).
available_until datetime The last date time at which this item can be bought
(or ``null``).
require_voucher boolean If ``true``, this item can only be bought using a
require_voucher boolean If ``True``, this item can only be bought using a
voucher that is specifically assigned to this item.
hide_without_voucher boolean If ``true``, this item is only shown during the voucher
hide_without_voucher boolean If ``True``, this item is only shown during the voucher
redemption process, but not in the normal shop
frontend.
allow_cancel boolean If ``false``, customers cannot cancel orders containing
allow_cancel boolean If ``False``, customers cannot cancel orders containing
this item.
min_per_order integer This product can only be bought if it is included at
least this many times in the order (or ``null`` for no
@@ -57,61 +51,31 @@ min_per_order integer This product ca
max_per_order integer This product can only be bought if it is included at
most this many times in the order (or ``null`` for no
limitation).
checkin_attention boolean If ``true``, the check-in app should show a warning
checkin_attention boolean If ``True``, the check-in app should show a warning
that this ticket requires special attention if such
a product is being scanned.
original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
require_approval boolean If ``true``, orders with this product will need to be
approved by the event organizer before they can be
paid.
require_bundling boolean If ``true``, this item is only available as part of bundles.
generate_tickets boolean If ``false``, tickets are never generated for this
product, regardless of other settings. If ``true``,
tickets are generated even if this is a
non-admission or add-on product, regardless of event
settings. If this is ``null``, regular ticketing
rules apply.
has_variations boolean Shows whether or not this item has variations.
variations list of objects A list with one object for each variation of this item.
Can be empty. Only writable during creation,
use separate endpoint to modify this later.
Can be empty. Only writable on POST.
├ id integer Internal ID of the variation
├ value multi-lingual string The "name" of the variation
├ default_price money (string) The price set directly for this variation or ``null``
├ price money (string) The price used for this variation. This is either the
same as ``default_price`` if that value is set or equal
to the item's ``default_price``.
original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
├ active boolean If ``false``, this variation will not be sold or shown.
active boolean If ``False``, this variation will not be sold or shown.
├ description multi-lingual string A public description of the variation. May contain
Markdown syntax or can be ``null``.
└ position integer An integer, used for sorting
addons list of objects Definition of add-ons that can be chosen for this item.
Only writable during creation,
use separate endpoint to modify this later.
Only writable on POST.
├ addon_category integer Internal ID of the item category the add-on can be
chosen from.
├ min_count integer The minimal number of add-ons that need to be chosen.
├ max_count integer The maximal number of add-ons that can be chosen.
position integer An integer, used for sorting
position integer An integer, used for sorting
└ price_included boolean Adding this add-on to the item is free
bundles list of objects Definition of bundles that are included in this item.
Only writable during creation,
use separate endpoint to modify this later.
├ bundled_item integer Internal ID of the item that is included.
├ bundled_variation integer Internal ID of the variation of the item (or ``null``).
├ count integer Number of items included
└ designated_price money (string) Designated price of the bundled product. This will be
used to split the price of the base item e.g. for mixed
taxation. This is not added to the price.
===================================== ========================== =======================================================
.. versionchanged:: 2.7
The attribute ``original_price`` has been added for ``variations``.
.. versionchanged:: 1.7
The attribute ``tax_rule`` has been added. ``tax_rate`` is kept for compatibility. The attribute
@@ -122,36 +86,15 @@ bundles list of objects Definition of b
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
The attribute ``price_included`` has been added to ``addons``.
.. versionchanged:: 1.16
The ``internal_name`` and ``original_price`` fields have been added.
.. versionchanged:: 2.0
The field ``require_approval`` has been added.
.. versionchanged:: 2.3
The ``sales_channels`` attribute has been added.
.. versionchanged:: 2.4
The ``generate_tickets`` attribute has been added.
.. versionchanged:: 2.6
The ``bundles`` and ``require_bundling`` attributes have been added.
Notes
-----
Please note that an item either always has variations or never has. Once created with variations the item can never
change to an item without and vice versa. To create an item with variations ensure that you POST an item with at least
one variation.
Also note that ``variations``, ``bundles``, and ``addons`` are only supported on ``POST``. To update/delete variations,
bundles, and add-ons please use the dedicated nested endpoints. By design this endpoint does not support ``PATCH`` and ``PUT``
with nested ``variations``, ``bundles`` and/or ``addons``.
Also note that ``variations`` and ``addons`` are only supported on ``POST``. To update/delete variations and add-ons please
use the dedicated nested endpoints. By design this endpoint does not support ``PATCH`` and ``PUT`` with nested
``variations`` and/or ``addons``.
Endpoints
---------
@@ -184,10 +127,7 @@ Endpoints
{
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
"category": null,
"active": true,
"description": null,
@@ -206,15 +146,11 @@ Endpoints
"max_per_order": null,
"checkin_attention": false,
"has_variations": false,
"generate_tickets": null,
"require_approval": false,
"require_bundling": false,
"variations": [
{
"value": {"en": "Student"},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": true,
"description": null,
"position": 0
@@ -223,14 +159,12 @@ Endpoints
"value": {"en": "Regular"},
"default_price": null,
"price": "23.00",
"original_price": null,
"active": true,
"description": null,
"position": 1
}
],
"addons": [],
"bundles": []
"addons": []
}
]
}
@@ -275,10 +209,7 @@ Endpoints
{
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
"category": null,
"active": true,
"description": null,
@@ -293,19 +224,15 @@ Endpoints
"require_voucher": false,
"hide_without_voucher": false,
"allow_cancel": true,
"generate_tickets": null,
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"has_variations": false,
"require_approval": false,
"require_bundling": false,
"variations": [
{
"value": {"en": "Student"},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": true,
"description": null,
"position": 0
@@ -314,14 +241,12 @@ Endpoints
"value": {"en": "Regular"},
"default_price": null,
"price": "23.00",
"original_price": null,
"active": true,
"description": null,
"position": 1
}
],
"addons": [],
"bundles": []
"addons": []
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -331,7 +256,7 @@ Endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/items/
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/items/(item)/
Creates a new item
@@ -347,10 +272,7 @@ Endpoints
{
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
"category": null,
"active": true,
"description": null,
@@ -365,18 +287,14 @@ Endpoints
"require_voucher": false,
"hide_without_voucher": false,
"allow_cancel": true,
"generate_tickets": null,
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"require_approval": false,
"require_bundling": false,
"variations": [
{
"value": {"en": "Student"},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": true,
"description": null,
"position": 0
@@ -385,31 +303,26 @@ Endpoints
"value": {"en": "Regular"},
"default_price": null,
"price": "23.00",
"original_price": null,
"active": true,
"description": null,
"position": 1
}
],
"addons": [],
"bundles": []
"addons": []
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
"category": null,
"active": true,
"description": null,
@@ -426,17 +339,13 @@ Endpoints
"allow_cancel": true,
"min_per_order": null,
"max_per_order": null,
"generate_tickets": null,
"checkin_attention": false,
"has_variations": true,
"require_approval": false,
"require_bundling": false,
"variations": [
{
"value": {"en": "Student"},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": true,
"description": null,
"position": 0
@@ -445,14 +354,12 @@ Endpoints
"value": {"en": "Regular"},
"default_price": null,
"price": "23.00",
"original_price": null,
"active": true,
"description": null,
"position": 1
}
],
"addons": [],
"bundles": []
"addons": []
}
:param organizer: The ``slug`` field of the organizer of the event to create an item for
@@ -462,7 +369,7 @@ Endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/items/(id)/
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/items/(item)/
Update an item. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
@@ -497,10 +404,7 @@ Endpoints
{
"id": 1,
"name": {"en": "Ticket"},
"internal_name": "",
"sales_channels": ["web"],
"default_price": "25.00",
"original_price": null,
"category": null,
"active": true,
"description": null,
@@ -514,20 +418,16 @@ Endpoints
"available_until": null,
"require_voucher": false,
"hide_without_voucher": false,
"generate_tickets": null,
"allow_cancel": true,
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"has_variations": true,
"require_approval": false,
"require_bundling": false,
"variations": [
{
"value": {"en": "Student"},
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"active": true,
"description": null,
"position": 0
@@ -536,14 +436,12 @@ Endpoints
"value": {"en": "Regular"},
"default_price": null,
"price": "23.00",
"original_price": null,
"active": true,
"description": null,
"position": 1
}
],
"addons": [],
"bundles": []
"addons": []
}
:param organizer: The ``slug`` field of the organizer to modify

File diff suppressed because it is too large Load Diff

View File

@@ -56,8 +56,6 @@ Endpoints
}
:query page: The page number in case of a multi-page result set, default is 1
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``slug`` and
``name``. Default: ``slug``.
:statuscode 200: no error
:statuscode 401: Authentication failure

View File

@@ -1,233 +0,0 @@
Question options
================
Resource description
--------------------
Questions of type "choice" or "multiple choice" can have different options attached.
The options resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the option
position integer An integer, used for sorting
identifier string An arbitrary string that can be used for matching with
other sources.
answer multi-lingual string The displayed value of this option
===================================== ========================== =======================================================
.. versionchanged:: 1.12
This resource has been added.
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/
Returns a list of all options for a given question.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/questions/11/options/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
},
{
"id": 2,
"identifier": "DFEMJWMJ",
"position": 2,
"answer": {"en": "M"}
},
{
"id": 3,
"identifier": "W9AH7RDE",
"position": 3,
"answer": {"en": "L"}
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query boolean active: If set to ``true`` or ``false``, only questions with this value for the field ``active`` will be
returned.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param question: The ``id`` field of the question to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/question does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/(id)/
Returns information on one option, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param question: The ``id`` field of the question to fetch
:param id: The ``id`` field of the option to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/
Creates a new option
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
}
:param organizer: The ``slug`` field of the organizer of the event/question to create a option for
:param event: The ``slug`` field of the event to create a option for
:param question: The ``id`` field of the question to create a option for
:statuscode 201: no error
:statuscode 400: The option could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/(id)/
Update an option. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"position": 3
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the question to modify
:param id: The ``id`` field of the option to modify
:statuscode 200: no error
:statuscode 400: The option could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/options/(id)/
Delete an option.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the question to modify
:param id: The ``id`` field of the option to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.

View File

@@ -1,7 +1,5 @@
.. spelling:: checkin
.. _rest-questions:
Questions
=========
@@ -30,35 +28,16 @@ type string The expected ty
* ``D`` date
* ``H`` time
* ``W`` date and time
* ``CC`` country code (ISO 3666-1 alpha-2)
required boolean If ``true``, the question needs to be filled out.
required boolean If ``True``, the question needs to be filled out.
position integer An integer, used for sorting
items list of integers List of item IDs this question is assigned to.
identifier string An arbitrary string that can be used for matching with
other sources.
ask_during_checkin boolean If ``true``, this question will not be asked while
ask_during_checkin boolean If ``True``, this question will not be asked while
buying the ticket, but will show up when redeeming
the ticket instead.
hidden boolean If ``true``, the question will only be shown in the
backend.
options list of objects In case of question type ``C`` or ``M``, this lists the
available objects. Only writable during creation,
use separate endpoint to modify this later.
available objects.
├ id integer Internal ID of the option
├ position integer An integer, used for sorting
├ identifier string An arbitrary string that can be used for matching with
other sources.
└ answer multi-lingual string The displayed value of this option
dependency_question integer Internal ID of a different question. The current
question will only be shown if the question given in
this attribute is set to the value given in
``dependency_value``. This cannot be combined with
``ask_during_checkin``.
dependency_value string The value ``dependency_question`` needs to be set to.
If ``dependency_question`` is set to a boolean
question, this should be ``"true"`` or ``"false"``.
Otherwise, it should be the ``identifier`` of a
question option.
===================================== ========================== =======================================================
.. versionchanged:: 1.12
@@ -66,23 +45,9 @@ dependency_value string The value ``dep
The values ``D``, ``H``, and ``W`` for the field ``type`` are now allowed and the ``ask_during_checkin`` field has
been added.
.. versionchanged:: 1.14
Write methods have been added. The attribute ``identifier`` has been added to both the resource itself and the
options resource. The ``position`` attribute has been added to the options resource.
.. versionchanged:: 2.7
The attribute ``hidden`` and the question type ``CC`` have been added.
Endpoints
---------
.. versionchanged:: 1.15
The questions endpoint has been extended by the filter queries ``ask_during_checkin``, ``requred``, and
``identifier``.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/
Returns a list of all questions within a given event.
@@ -115,28 +80,18 @@ Endpoints
"required": false,
"items": [1, 2],
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
{
"id": 1,
"identifier": "LVETRWVU",
"position": 0,
"answer": {"en": "S"}
},
{
"id": 2,
"identifier": "DFEMJWMJ",
"position": 1,
"answer": {"en": "M"}
},
{
"id": 3,
"identifier": "W9AH7RDE",
"position": 2,
"answer": {"en": "L"}
}
]
@@ -147,9 +102,6 @@ Endpoints
:query integer page: The page number in case of a multi-page result set, default is 1
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id`` and ``position``.
Default: ``position``
:query string identifier: Only return questions with the given identifier string
:query boolean ask_during_checkin: Only return questions that are or are not to be asked during check-in
:query boolean required: Only return questions that are or are not required to fill in
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
@@ -182,29 +134,19 @@ Endpoints
"type": "C",
"required": false,
"items": [1, 2],
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"position": 1,
"options": [
{
"id": 1,
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
},
{
"id": 2,
"identifier": "DFEMJWMJ",
"position": 2,
"answer": {"en": "M"}
},
{
"id": 3,
"identifier": "W9AH7RDE",
"position": 3,
"answer": {"en": "L"}
}
]
@@ -216,188 +158,3 @@ Endpoints
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/questions/
Creates a new question
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/questions/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"question": {"en": "T-Shirt size"},
"type": "C",
"required": false,
"items": [1, 2],
"position": 1,
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
{
"answer": {"en": "S"}
},
{
"answer": {"en": "M"}
},
{
"answer": {"en": "L"}
}
]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"question": {"en": "T-Shirt size"},
"type": "C",
"required": false,
"items": [1, 2],
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
{
"id": 1,
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
},
{
"id": 2,
"identifier": "DFEMJWMJ",
"position": 2,
"answer": {"en": "M"}
},
{
"id": 3,
"identifier": "W9AH7RDE",
"position": 3,
"answer": {"en": "L"}
}
]
}
:param organizer: The ``slug`` field of the organizer of the event to create an item for
:param event: The ``slug`` field of the event to create an item for
:statuscode 201: no error
:statuscode 400: The item could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/
Update a question. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``options`` field. If
you need to update/delete options please use the nested dedicated endpoints.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"position": 2
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"question": {"en": "T-Shirt size"},
"type": "C",
"required": false,
"items": [1, 2],
"position": 2,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"options": [
{
"id": 1,
"identifier": "LVETRWVU",
"position": 1,
"answer": {"en": "S"}
},
{
"id": 2,
"identifier": "DFEMJWMJ",
"position": 2,
"answer": {"en": "M"}
},
{
"id": 3,
"identifier": "W9AH7RDE",
"position": 3,
"answer": {"en": "L"}
}
]
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the question to modify
:statuscode 200: no error
:statuscode 400: The item could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/
Delete a question.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the item to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.

View File

@@ -1,5 +1,3 @@
.. _rest-quotas:
Quotas
======
@@ -137,7 +135,7 @@ Endpoints
.. sourcecode:: http
HTTP/1.1 201 Created
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json

View File

@@ -1,5 +1,3 @@
.. _rest-subevents:
Event series dates / Sub-events
===============================
@@ -17,11 +15,8 @@ Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the sub-event
name multi-lingual string The sub-event's full name
event string The slug of the parent event
active boolean If ``true``, the sub-event ticket shop is publicly
available.
is_public boolean If ``true``, the sub-event ticket shop is publicly
shown in lists.
date_from datetime The sub-event's start date
date_to datetime The sub-event's end date (or ``null``)
date_admission datetime The sub-event's admission date (or ``null``)
@@ -43,16 +38,6 @@ meta_data dict Values set for
The ``meta_data`` field has been added.
.. versionchanged:: 2.1
The ``event`` field has been added, together with filters on the list of dates and an organizer-level list.
.. versionchanged:: 2.6
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
.. versionchanged:: 2.7
The attribute ``is_public`` has been added.
Endpoints
---------
@@ -85,9 +70,7 @@ Endpoints
{
"id": 1,
"name": {"en": "First Sample Conference"},
"event": "sampleconf",
"active": false,
"is_public": true,
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
@@ -107,90 +90,12 @@ Endpoints
}
:query page: The page number in case of a multi-page result set, default is 1
:query active: If set to ``true``/``false``, only events with a matching value of ``active`` are returned.
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned.
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned.
:query ends_after: If set to a date and time, only events that happen during of after the given time are returned.
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the main event
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/subevents/
Creates a new subevent.
Permission required: "Can create events"
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/subevents/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"name": {"en": "First Sample Conference"},
"active": false,
"is_public": true,
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"presale_start": null,
"presale_end": null,
"location": null,
"item_price_overrides": [
{
"item": 2,
"price": "12.00"
}
],
"variation_price_overrides": [],
"meta_data": {}
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": {"en": "First Sample Conference"},
"active": false,
"is_public": true,
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"presale_start": null,
"presale_end": null,
"location": null,
"item_price_overrides": [
{
"item": 2,
"price": "12.00"
}
],
"variation_price_overrides": [],
"meta_data": {}
}
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the main event
:statuscode 201: no error
:statuscode 400: The sub-event could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/subevents/(id)/
Returns information on one sub-event, identified by its ID.
@@ -214,9 +119,7 @@ Endpoints
{
"id": 1,
"name": {"en": "First Sample Conference"},
"event": "sampleconf",
"active": false,
"is_public": true,
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
@@ -233,164 +136,9 @@ Endpoints
"meta_data": {}
}
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the main event
:param id: The ``id`` field of the sub-event to fetch
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``slug`` field of the sub-event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/subevents/(id)/
Updates a sub-event, identified by its ID. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to
provide all fields of the resource, other fields will be reset to default. With ``PATCH``, you only need to provide
the fields that you want to change.
Permission required: "Can change event settings"
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/subevents/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"name": {"en": "New Subevent Name"},
"item_price_overrides": [
{
"item": 2,
"price": "23.42"
}
],
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": {"en": "New Subevent Name"},
"event": "sampleconf",
"active": false,
"is_public": true,
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"presale_start": null,
"presale_end": null,
"location": null,
"item_price_overrides": [
{
"item": 2,
"price": "23.42"
}
],
"variation_price_overrides": [],
"meta_data": {}
}
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the main event
:param id: The ``id`` field of the sub-event to update
:statuscode 200: no error
:statuscode 400: The sub-event could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/sub-event does not exist **or** you have no permission to update this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/subevents/(id)/
Delete a sub-event. Note that events with orders cannot be deleted to ensure data integrity.
Permission required: "Can change event settings"
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/subevents/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the main event
:param id: The ``id`` field of the sub-event to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/sub-event does not exist **or** you have no permission to delete this resource.
.. http:get:: /api/v1/organizers/(organizer)/subevents/
Returns a list of all sub-events of any event series you have access to within an organizer account.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/subevents/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": {"en": "First Sample Conference"},
"event": "sampleconf",
"active": false,
"is_public": true,
"date_from": "2017-12-27T10:00:00Z",
"date_to": null,
"date_admission": null,
"presale_start": null,
"presale_end": null,
"location": null,
"item_price_overrides": [
{
"item": 2,
"price": "12.00"
}
],
"variation_price_overrides": [],
"meta_data": {}
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:query active: If set to ``true``/``false``, only events with a matching value of ``active`` are returned.
:query event__live: If set to ``true``/``false``, only events with a matching value of ``live`` on the parent event are returned.
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned.
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned.
:query ends_after: If set to a date and time, only events that happen during of after the given time are returned.
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.

View File

@@ -1,5 +1,3 @@
.. _rest-taxrules:
Tax rules
=========

View File

@@ -18,8 +18,8 @@ max_usages integer The maximum num
redeemed integer The number of times this voucher already has been
redeemed.
valid_until datetime The voucher expiration date (or ``null``).
block_quota boolean If ``true``, quota is blocked for this voucher.
allow_ignore_quota boolean If ``true``, this voucher can be redeemed even if a
block_quota boolean If ``True``, quota is blocked for this voucher.
allow_ignore_quota boolean If ``True``, this voucher can be redeemed even if a
product is sold out and even if quota is not blocked
for this voucher.
price_mode string Determines how this voucher affects product prices.
@@ -231,76 +231,6 @@ Endpoints
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/vouchers/batch_create/
Creates multiple new vouchers atomically.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/vouchers/batch_create/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 408
[
{
"code": "43K6LKM37FBVR2YG",
"max_usages": 1,
"valid_until": null,
"block_quota": false,
"allow_ignore_quota": false,
"price_mode": "set",
"value": "12.00",
"item": 1,
"variation": null,
"quota": null,
"tag": "testvoucher",
"comment": "",
"subevent": null
},
{
"code": "ASDKLJCYXCASDASD",
"max_usages": 1,
"valid_until": null,
"block_quota": false,
"allow_ignore_quota": false,
"price_mode": "set",
"value": "12.00",
"item": 1,
"variation": null,
"quota": null,
"tag": "testvoucher",
"comment": "",
"subevent": null
},
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
[
{
"id": 1,
"code": "43K6LKM37FBVR2YG",
}, …
}
:param organizer: The ``slug`` field of the organizer to create a vouchers for
:param event: The ``slug`` field of the event to create a vouchers for
:statuscode 201: no error
:statuscode 400: The vouchers could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/vouchers/(id)/
Update a voucher. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
@@ -321,7 +251,7 @@ Endpoints
{
"price_mode": "set",
"value": "24.00"
"value": "24.00",
}
**Example response**:

View File

@@ -27,12 +27,6 @@ subevent integer ID of the date
===================================== ========================== =======================================================
.. versionchanged:: 1.15
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added as well as a method to send out
vouchers.
Endpoints
---------
@@ -127,161 +121,3 @@ Endpoints
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/
Create a new entry.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 408
{
"email": "waiting@example.org",
"item": 3,
"variation": null,
"locale": "de",
"subevent": null
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"created": "2017-12-01T10:00:00Z",
"email": "waiting@example.org",
"voucher": null,
"item": 3,
"variation": null,
"locale": "de",
"subevent": null
}
:param organizer: The ``slug`` field of the organizer to create an entry for
:param event: The ``slug`` field of the event to create an entry for
:statuscode 201: no error
:statuscode 400: The voucher could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
resource **or** entries cannot be created for this item at this time.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/(id)/
Update an entry. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id``, ``voucher`` and ``created`` fields. You can only change
an entry as long as no ``voucher`` is set.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 408
{
"item": 4
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"created": "2017-12-01T10:00:00Z",
"email": "waiting@example.org",
"voucher": null,
"item": 4,
"variation": null,
"locale": "de",
"subevent": null
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the entry to modify
:statuscode 200: no error
:statuscode 400: The entry could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
resource **or** entries cannot be created for this item at this time **or** this entry already
has a voucher assigned
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/(id)/send_voucher/
Manually sends a voucher to someone on the waiting list
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/1/send_voucher/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 0
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the entry to modify
:statuscode 204: no error
:statuscode 400: The voucher could not be sent out, see body for details (e.g. voucher has already been sent or
item is not available).
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to do this
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/(id)/
Delete an entry. Note that you cannot delete an entry once it is assigned a voucher.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the entry to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this
resource **or** this entry already has a voucher assigned.

View File

@@ -1,242 +0,0 @@
.. _`rest-webhooks`:
Webhooks
========
.. note:: This page is about how to modify webhook settings themselves through the REST API. If you just want to know
how webhooks work, go here: :ref:`webhooks`
Resource description
--------------------
The webhook resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the webhook
enabled boolean If ``false``, this webhook will not receive any notifications
target_url string The URL to call
all_events boolean If ``true``, this webhook will receive notifications
on all events of this organizer
limit_events list of strings If ``all_events`` is ``false``, this is a list of
event slugs this webhook is active for
action_types list of strings A list of action type filters that limit the
notifications sent to this webhook. See below for
valid values
===================================== ========================== =======================================================
The following values for ``action_types`` are valid with pretix core:
* ``pretix.event.order.placed``
* ``pretix.event.order.paid``
* ``pretix.event.order.canceled``
* ``pretix.event.order.expired``
* ``pretix.event.order.modified``
* ``pretix.event.order.contact.changed``
* ``pretix.event.order.changed.*``
* ``pretix.event.order.refund.created.externally``
* ``pretix.event.order.approved``
* ``pretix.event.order.denied``
* ``pretix.event.checkin``
* ``pretix.event.checkin.reverted``
Installed plugins might register more valid values.
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/webhooks/
Returns a list of all webhooks within a given organizer.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/webhooks/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 2,
"enabled": true,
"target_url": "https://httpstat.us/200",
"all_events": false,
"limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/webhooks/(id)/
Returns information on one webhook, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 2,
"enabled": true,
"target_url": "https://httpstat.us/200",
"all_events": false,
"limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
}
:param organizer: The ``slug`` field of the organizer to fetch
:param id: The ``id`` field of the webhook to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/webhooks/
Creates a new webhook
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/webhooks/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"enabled": true,
"target_url": "https://httpstat.us/200",
"all_events": false,
"limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 3,
"enabled": true,
"target_url": "https://httpstat.us/200",
"all_events": false,
"limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
}
:param organizer: The ``slug`` field of the organizer to create a webhook for
:statuscode 201: no error
:statuscode 400: The webhook could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/webhooks/(id)/
Update a webhook. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"enabled": false
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"enabled": false,
"target_url": "https://httpstat.us/200",
"all_events": false,
"limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
}
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the webhook to modify
:statuscode 200: no error
:statuscode 400: The webhook could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/webhook/(id)/
Delete a webhook. Currently, this will not delete but just disable the webhook.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the webhook to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource.

View File

@@ -1,36 +0,0 @@
.. _`rest-tokenauth`:
Token-based authentication
==========================
Obtaining an API token
----------------------
To authenticate your API requests with Tokens, you need to obtain a team-level API token.
You can create a token in the pretix web interface on the level of organizer teams. Create
a new team or choose an existing team that has the level of permissions the token should
have and create a new token using the form below the list of team members:
.. image:: img/token_form.png
:class: screenshot
You can enter a description for the token to distinguish from other tokens later on.
Once you click "Add", you will be provided with an API token in the success message.
Copy this token, as you won't be able to retrieve it again.
.. image:: img/token_success.png
:class: screenshot
Using an API token
------------------
You need to include the API token with every request to pretix' API in the ``Authorization`` header
like the following:
.. sourcecode:: http
:emphasize-lines: 3
GET /api/v1/organizers/ HTTP/1.1
Host: pretix.eu
Authorization: Token e1l6gq2ye72thbwkacj7jbri7a7tvxe614ojv8ybureain92ocub46t5gab5966k

View File

@@ -1,108 +0,0 @@
.. _`webhooks`:
Webhooks
========
pretix can send webhook calls to notify your application of any changes that happen inside pretix. This is especially
useful for everything triggered by an actual user, such as a new ticket sale or the arrival of a payment.
You can register any number of webhook URLs that pretix will notify any time one of the supported events occurs inside
your organizer account. A great example use case of webhooks would be to add the buyer to your mailing list every time
a new order comes in.
Configuring webhooks
--------------------
You can find the list of your active webhooks in the "Webhook" section of your organizer account:
.. thumbnail:: ../screens/organizer/webhook_list.png
:align: center
:class: screenshot
Click "Create webhook" if you want to add a new URL. You will then be able to enter the URL pretix shall call for
notifications. You need to select any number of notification types that you want to receive and you can optionally
filter the events you want to receive notifications for.
.. thumbnail:: ../screens/organizer/webhook_edit.png
:align: center
:class: screenshot
You can also configure webhooks :ref:`through the API itself <rest-webhooks>`.
Receiving webhooks
------------------
Creating a webhook endpoint on your server is no different from creating any other page on your website. If your
website is written in PHP, you might just create a new ``.php`` file on your server; if you use a web framework like
Symfony or Django, you would just create a new route with the desired URL.
We will call your URL with a HTTP ``POST`` request with a ``JSON`` body. In PHP, you can parse this like this::
$input = @file_get_contents('php://input');
$event_json = json_decode($input);
// Do something with $event_json
In Django, you would create a view like this::
def my_webhook_view(request):
event_json = json.loads(request.body)
# Do something with event_json
return HttpResponse(status=200)
More samples for the language of your choice are easy to find online.
The exact body of the request varies by notification type, but for the main types included with pretix core, such as
those related to changes of an order, it will look like this::
{
"notification_id": 123455,
"organizer": "acmecorp",
"event": "democon",
"code": "ABC23",
"action": "pretix.event.order.placed"
}
Notifications regarding a check-in will contain more details like ``orderposition_id``
and ``checkin_list``.
.. warning:: You should not trust data supplied to your webhook, but only use it as a trigger to fetch updated data.
Anyone could send data there if they guess the correct URL and you won't be able to tell. Therefore, we
only include the minimum amount of data necessary for you to fetch the changed objects from our
:ref:`rest-api` in an authenticated way.
If you want to further prevent others from accessing your webhook URL, you can also use `Basic authentication`_ and
supply the URL to us in the format of ``https://username:password@domain.com/path/``.
We recommend that you use HTTPS for your webhook URL and might require it in the future. If HTTPS is used, we require
that a valid certificate is in use.
.. note:: If you use a web framework that makes use of automatic CSRF protection, this protection might prevent us
from calling your webhook URL. In this case, we recommend that you turn of CSRF protection selectively
for that route. In Django, you can do this by putting the ``@csrf_exempt`` decorator on your view. In
Rails, you can pass an ``except`` parameter to ``protect_from_forgery``.
Responding to a webhook
-----------------------
If you successfully received a webhook call, your endpoint should return a HTTP status code between ``200`` and ``299``.
If any other status code is returned, we will assume you did not receive the call. This does mean that any redirection
or ``304 Not Modified`` response will be treated as a failure. pretix will not follow any ``301`` or ``302`` redirect
headers and pretix will ignore all other information in your response headers or body.
If we do not receive a status code in the range of ``200`` and ``299``, pretix will retry to deliver for up to three
days with an exponential back off. Therefore, we recommend that you implement your endpoint in a way where calling it
multiple times for the same event due to a perceived error does not do any harm.
There is only one exception: If status code ``410 Gone`` is returned, we will assume the
endpoint does not exist any more and automatically disable the webhook.
.. note:: If you use a self-hosted version of pretix (i.e. not our SaaS offering at pretix.eu) and you did not
configure a background task queue, failed webhooks will not be retried.
Debugging webhooks
------------------
If you want to debug your webhooks, you can view a log of all sent notifications and the responses of your server for
30 days right next to your configuration.
.. _Basic authentication: https://en.wikipedia.org/wiki/Basic_access_authentication

View File

@@ -31,13 +31,6 @@ import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix.testutils.settings")
django.setup()
try:
import enchant
HAS_PYENCHANT = True
except:
HAS_PYENCHANT = False
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
@@ -52,9 +45,8 @@ extensions = [
'sphinx.ext.coverage',
'sphinxcontrib.httpdomain',
'sphinxcontrib.images',
'sphinxcontrib.spelling',
]
if HAS_PYENCHANT:
extensions.append('sphinxcontrib.spelling')
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -66,7 +58,7 @@ source_suffix = '.rst'
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = 'contents'
# General information about the project.
project = 'pretix'
@@ -234,7 +226,7 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'pretix.tex', 'pretix Documentation',
('contents', 'pretix.tex', 'pretix Documentation',
'Raphael Michel', 'manual'),
]
@@ -300,25 +292,21 @@ images_config = {
'default_image_width': '250px'
}
linkcheck_ignore = [
r'http://localhost.*', r'.*yourdomain.*', r'https://en.wikipedia.org', 'https://pretix.eu/',
]
# -- Options for Spelling output ------------------------------------------
if HAS_PYENCHANT:
# String specifying the language, as understood by PyEnchant and enchant.
# Defaults to en_US for US English.
spelling_lang = 'en_US'
# String specifying a file containing a list of words known to be spelled
# correctly but that do not appear in the language dictionary selected by
# spelling_lang. The file should contain one word per line.
spelling_word_list_filename='spelling_wordlist.txt'
# String specifying the language, as understood by PyEnchant and enchant.
# Defaults to en_US for US English.
spelling_lang = 'en_US'
# Boolean controlling whether suggestions for misspelled words are printed.
# Defaults to False.
spelling_show_suggestions=True
# String specifying a file containing a list of words known to be spelled
# correctly but that do not appear in the language dictionary selected by
# spelling_lang. The file should contain one word per line.
spelling_word_list_filename='spelling_wordlist.txt'
# List of filter classes to be added to the tokenizer that produces words to be checked.
from checkin_filter import CheckinFilter
spelling_filters=[CheckinFilter]
# Boolean controlling whether suggestions for misspelled words are printed.
# Defaults to False.
spelling_show_suggestions=True
# List of filter classes to be added to the tokenizer that produces words to be checked.
from checkin_filter import CheckinFilter
spelling_filters=[CheckinFilter]

View File

@@ -64,7 +64,7 @@ Similarly, there is ``organizer_permission_required`` and ``OrganizerPermissionR
event-related views, there is also a signal that allows you to add the view to the event navigation like this::
from django.urls import resolve, reverse
from django.core.urlresolvers import resolve, reverse
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from pretix.control.signals import nav_event

View File

@@ -1,109 +0,0 @@
.. highlight:: python
:linenothreshold: 5
Writing an HTML e-mail renderer plugin
======================================
An email renderer class controls how the HTML part of e-mails sent by pretix is built.
The creation of such a plugin is very similar to creating an export output.
Please read :ref:`Creating a plugin <pluginsetup>` first, if you haven't already.
Output registration
-------------------
The email HTML renderer API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available email renderers. Your plugin
should listen for this signal and return the subclass of ``pretix.base.email.BaseHTMLMailRenderer``
that we'll provide in this plugin::
from django.dispatch import receiver
from pretix.base.signals import register_html_mail_renderers
@receiver(register_html_mail_renderers, dispatch_uid="renderer_custom")
def register_mail_renderers(sender, **kwargs):
from .email import MyMailRenderer
return MyMailRenderer
The renderer class
------------------
.. class:: pretix.base.email.BaseHTMLMailRenderer
The central object of each email renderer is the subclass of ``BaseHTMLMailRenderer``.
.. py:attribute:: BaseHTMLMailRenderer.event
The default constructor sets this property to the event we are currently
working for.
.. autoattribute:: identifier
This is an abstract attribute, you **must** override this!
.. autoattribute:: verbose_name
This is an abstract attribute, you **must** override this!
.. autoattribute:: thumbnail_filename
This is an abstract attribute, you **must** override this!
.. autoattribute:: is_available
.. automethod:: render
This is an abstract method, you **must** implement this!
Helper class for template-base renderers
----------------------------------------
The email renderer that ships with pretix is based on Django templates to generate HTML.
In case you also want to render emails based on a template, we provided a ready-made base
class ``TemplateBasedMailRenderer`` that you can re-use to perform the following steps:
* Convert the body text and the signature to HTML using our markdown renderer
* Render the template
* Call `inlinestyler`_ to convert all ``<style>`` style sheets to inline ``style=""``
attributes for better compatibility
To use it, you just need to implement some variables::
class ClassicMailRenderer(TemplateBasedMailRenderer):
verbose_name = _('pretix default')
identifier = 'classic'
thumbnail_filename = 'pretixbase/email/thumb.png'
template_name = 'pretixbase/email/plainwrapper.html'
The template is passed the following context variables:
``site``
Name of the pretix installation (``settings.PRETIX_INSTANCE_NAME``)
``site_url``
Root URL of the pretix installation (``settings.SITE_URL``)
``body``
The body as markdown (render with ``{{ body|safe }}``)
``subject``
The email subject
``color``
The primary color of the event
``event``
The ``Event`` object
``signature`` (optional, only if configured)
The body as markdown (render with ``{{ signature|safe }}``)
``order`` (optional, only if applicable)
The ``Order`` object
.. _inlinestyler: https://pypi.org/project/inlinestyler/

View File

@@ -11,8 +11,7 @@ Core
----
.. automodule:: pretix.base.signals
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types,
item_copy_data, register_sales_channels, register_global_settings
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types
Order events
""""""""""""
@@ -20,13 +19,17 @@ Order events
There are multiple signals that will be sent out in the ordering cycle:
.. automodule:: pretix.base.signals
:members: validate_cart, validate_order, order_fee_calculation, order_paid, order_placed, order_canceled, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download
:members: validate_cart, order_fee_calculation, order_paid, order_placed, order_fee_type_name, allow_ticket_download
Frontend
--------
.. automodule:: pretix.presale.signals
:members: html_head, html_footer, footer_link, front_page_top, front_page_bottom, fee_calculation_for_cart, contact_form_fields, question_form_fields, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, sass_preamble, sass_postamble, checkout_flow_steps, order_info, order_meta_from_request, position_info
:members: html_head, html_footer, footer_links, front_page_top, front_page_bottom, fee_calculation_for_cart, contact_form_fields, question_form_fields, checkout_confirm_messages, checkout_confirm_page_content
.. automodule:: pretix.presale.signals
:members: order_info, order_meta_from_request
Request flow
""""""""""""
@@ -44,18 +47,11 @@ Backend
-------
.. automodule:: pretix.control.signals
:members: nav_event, html_head, html_page_start, quota_detail_html, nav_topbar, nav_global, nav_organizer, nav_event_settings,
order_info, event_settings_widget, oauth_application_registered, order_position_buttons, nav_item
:members: nav_event, html_head, quota_detail_html, nav_topbar, nav_global, nav_organizer, nav_event_settings, order_info, event_settings_widget
.. automodule:: pretix.base.signals
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events
Vouchers
""""""""
.. automodule:: pretix.control.signals
:members: item_forms
:members: logentry_display, logentry_object_link, requiredaction_display
Vouchers
""""""""
@@ -72,5 +68,5 @@ Dashboards
Ticket designs
""""""""""""""
.. automodule:: pretix.base.signals
.. automodule:: pretix.plugins.ticketoutputpdf.signals
:members: layout_text_variables

View File

@@ -10,10 +10,6 @@ Contents:
exporter
ticketoutput
payment
payment_2.0
email
invoice
shredder
customview
general
quality

View File

@@ -13,7 +13,7 @@ Output registration
-------------------
The invoice renderer API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available invoice renderers. Your plugin
does use a signal to get a list of all available ticket outputs. Your plugin
should listen for this signal and return the subclass of ``pretix.base.invoice.BaseInvoiceRenderer``
that we'll provide in this plugin::
@@ -23,7 +23,7 @@ that we'll provide in this plugin::
@receiver(register_invoice_renderers, dispatch_uid="output_custom")
def register_invoice_renderers(sender, **kwargs):
def register_infoice_renderers(sender, **kwargs):
from .invoice import MyInvoiceRenderer
return MyInvoiceRenderer

View File

@@ -9,10 +9,6 @@ is very similar to creating an export output.
Please read :ref:`Creating a plugin <pluginsetup>` first, if you haven't already.
.. warning:: We changed our payment provider API a lot in pretix 2.x. Our documentation page on :ref:`payment2.0`
might be insightful even if you do not have a payment provider to port, as it outlines the rationale
behind the current design.
Provider registration
---------------------
@@ -35,7 +31,7 @@ that the plugin will provide::
The provider class
------------------
.. py:class:: pretix.base.payment.BasePaymentProvider
.. class:: pretix.base.payment.BasePaymentProvider
The central object of each payment provider is the subclass of ``BasePaymentProvider``.
@@ -58,63 +54,55 @@ The provider class
This is an abstract attribute, you **must** override this!
.. autoattribute:: public_name
.. autoattribute:: is_enabled
.. automethod:: calculate_fee
.. autoattribute:: settings_form_fields
.. automethod:: settings_form_clean
.. automethod:: settings_content_render
.. automethod:: is_allowed
.. automethod:: render_invoice_text
.. automethod:: payment_form_render
.. automethod:: payment_form
.. automethod:: is_allowed
.. autoattribute:: payment_form_fields
.. automethod:: payment_is_valid_session
.. automethod:: checkout_prepare
.. automethod:: payment_is_valid_session
.. automethod:: checkout_confirm_render
This is an abstract method, you **must** override this!
.. automethod:: execute_payment
.. automethod:: calculate_fee
.. automethod:: payment_perform
.. automethod:: order_pending_mail_render
.. automethod:: payment_pending_render
.. automethod:: order_pending_render
.. autoattribute:: abort_pending_allowed
.. automethod:: render_invoice_text
This is an abstract method, you **must** override this!
.. automethod:: order_change_allowed
.. automethod:: payment_prepare
.. automethod:: order_can_retry
.. automethod:: payment_control_render
.. automethod:: order_prepare
.. automethod:: payment_refund_supported
.. automethod:: order_paid_render
.. automethod:: payment_partial_refund_supported
.. automethod:: order_control_render
.. automethod:: execute_refund
.. automethod:: order_control_refund_render
.. automethod:: shred_payment_info
.. automethod:: order_control_refund_perform
.. autoattribute:: is_implicit
.. autoattribute:: is_meta
.. autoattribute:: test_mode_message
.. automethod:: is_implicit
Additional views

View File

@@ -1,129 +0,0 @@
.. highlight:: python
:linenothreshold: 5
.. _`payment2.0`:
Porting a payment provider from pretix 1.x to pretix 2.x
========================================================
In pretix 2.x, we changed large parts of the payment provider API. This documentation details the changes we made
and shows you how you can make an existing pretix 1.x payment provider compatible with pretix 2.x
Conceptual overview
-------------------
In pretix 1.x, an order was always directly connected to a payment provider for the full life of an order. As long as
an order was unpaid, this could still be changed in some cases, but once an order was paid, no changes to the payment
provider were possible any more. Additionally, the internal state of orders allowed orders only to be fully paid or
not paid at all. This leads to a couple of consequences:
* Payment-related functions (like "execute payment" or "do a refund") always operated on full orders.
* Changing the total of an order was basically impossible once an order was paid, since there was no concept of
partial payments or partial refunds.
* Payment provider plugins needed to take complicated steps to detect cases that require human intervention, like e.g.
* An order has expired, no quota is left to revive it, but a payment has been received
* A payment has been received for a canceled order
* A payment has been received for an order that has already been paid with a different payment method
* An external payment service notified us of a refund/dispute
We noticed that we copied and repeated large portions of code in all our official payment provider plugins, just
to deal with some of these cases.
* Sometimes, there is the need to mark an order as refunded within pretix, without automatically triggering a refund
with an external API. Every payment method needed to implement a user interface for this independently.
* If a refund was not possible automatically, there was no way user to track which payments actually have been refunded
manually and which are still left to do.
* When the payment with one payment provider failed and the user changed to a different payment provider, all
information about the first payment was lost from the order object and could only be retrieved from order log data,
which also made it hard to design a data shredder API to get rid of this data.
In pretix 2.x, we introduced two new models, :py:class:`OrderPayment <pretix.base.models.OrderPayment>` and
:py:class:`OrderRefund <pretix.base.models.OrderRefund>`. Each instance of these is connected to an order and
represents one single attempt to pay or refund a specific amount of money. Each one of these has an individual state,
can individually fail or succeed, and carries an amount variable that can differ from the order total.
This has the following advantages:
* The system can now detect orders that are over- or underpaid, independent of the payment providers in use.
* Therefore, we can now allow partial payments, partial refunds, and changing paid orders, and automatically detect
the cases listed above and notify the user.
Payment providers now interact with those payment and refund objects more than with orders.
Your to-do list
---------------
Payment processing
""""""""""""""""""
* The method ``BasePaymentProvider.order_pending_render`` has been removed and replaced by a new
``BasePaymentProvider.payment_pending_render(request, payment)`` method that is passed an ``OrderPayment``
object instead of an ``Order``.
* The method ``BasePaymentProvider.payment_form_render`` now receives a new ``total`` parameter.
* The method ``BasePaymentProvider.payment_perform`` has been removed and replaced by a new method
``BasePaymentProvider.execute_payment(request, payment)`` that is passed an ``OrderPayment``
object instead of an ``Order``.
* The function ``pretix.base.services.mark_order_paid`` has been removed, instead call ``payment.confirm()``
on a pending ``OrderPayment`` object. If no further payments are required for this order, this will also
mark the order as paid automatically. Note that ``payment.confirm()`` can still throw a ``QuotaExceededException``,
however it will still mark the payment as complete (not the order!), so you should catch this exception and
inform the user, but not abort the transaction.
* A new property ``BasePaymentProvider.abort_pending_allowed`` has been introduced. Only if set, the user will
be able to retry a payment or switch the payment method when the order currently has a payment object in
state ``"pending"``. This replaces ``BasePaymentProvider.order_can_retry``, which no longer exists.
* The methods ``BasePaymentProvider.retry_prepare`` and ``BasePaymentProvider.order_prepare`` have both been
replaced by a new method ``BasePaymentProvider.payment_prepare(request, payment)`` that is passed an ``OrderPayment``
object instead of an ``Order``. **Keep in mind that this payment object might have an amount property that
differs from the order total, if the order is already partially paid.**
* The method ``BasePaymentProvider.order_paid_render`` has been removed.
* The method ``BasePaymentProvider.order_control_render`` has been removed and replaced by a new method
``BasePaymentProvider.payment_control_render(request, payment)`` that is passed an ``OrderPayment``
object instead of an ``Order``.
* There's no need to manually deal with excess payments or duplicate payments anymore, just setting the ``OrderPayment``
methods to the correct state will do the job.
Creating refunds
""""""""""""""""
* The methods ``BasePaymentProvider.order_control_refund_render`` and ``BasePaymentProvider.order_control_refund_perform``
have been removed.
* Two new boolean methods ``BasePaymentProvider.payment_refund_supported(payment)`` and ``BasePaymentProvider.payment_partial_refund_supported(payment)``
have been introduced. They should be set to return ``True`` if and only if the payment API allows to *automatically*
transfer the money back to the customer.
* A new method ``BasePaymentProvider.execute_refund(refund)`` has been introduced. This method is called using a
``OrderRefund`` object in ``"created"`` state and is expected to transfer the money back and confirm success with
calling ``refund.done()``. This will only ever be called if either ``BasePaymentProvider.payment_refund_supported(payment)``
or ``BasePaymentProvider.payment_partial_refund_supported(payment)`` return ``True``.
Processing external refunds
"""""""""""""""""""""""""""
* If e.g. a webhook API notifies you that a payment has been disputed or refunded with the external API, you are
expected to call ``OrderPayment.create_external_refund(self, amount, execution_date, info='{}')`` on this payment.
This will create and return an appropriate ``OrderRefund`` object and send out a notification. However, it will not
mark the order as refunded, but will ask the event organizer for a decision.
Data shredders
""""""""""""""
* The method ``BasePaymentProvider.shred_payment_info`` is no longer passed an order, but instead **either**
an ``OrderPayment`` **or** an ``OrderRefund``.

View File

@@ -49,19 +49,15 @@ description string A more verbose description of what your
visible boolean (optional) ``True`` by default, can hide a plugin so it cannot be normally activated.
restricted boolean (optional) ``False`` by default, restricts a plugin such that it can only be enabled
for an event by system administrators / superusers.
compatibility string Specifier for compatible pretix versions.
================== ==================== ===========================================================
A working example would be::
try:
from pretix.base.plugins import PluginConfig
except ImportError:
raise RuntimeError("Please use pretix 2.7 or above to run this plugin!")
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class PaypalApp(PluginConfig):
class PaypalApp(AppConfig):
name = 'pretix_paypal'
verbose_name = _("PayPal")
@@ -72,7 +68,6 @@ A working example would be::
visible = True
restricted = False
description = _("This plugin allows you to receive payments via PayPal")
compatibility = "pretix>=2.7.0"
default_app_config = 'pretix_paypal.PaypalApp'
@@ -84,9 +79,6 @@ human-readable error messages. We recommend using the ``django.utils.functional.
decorator, as it might get called a lot. You can also implement ``compatibility_warnings``,
those will be displayed but not block the plugin execution.
The ``AppConfig`` class may implement a method ``is_available(event)`` that checks if a plugin
is available for a specific event. If not, it will not be shown in the plugin list of that event.
Plugin registration
-------------------
@@ -150,5 +142,5 @@ your Django app label.
.. _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/
.. _entry point: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#locating-plugins
.. _entry point: https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
.. _cookiecutter: https://cookiecutter.readthedocs.io/en/latest/

View File

@@ -1,125 +0,0 @@
.. highlight:: python
:linenothreshold: 5
.. _`pluginquality`:
Plugin quality checklist
========================
If you want to write a high-quality pretix plugin, this is a list of things you should check before
you publish it. This is also a list of things that we check, if we consider installing an externally
developed plugin on our hosted infrastructure.
A. Meta
-------
#. The plugin is clearly licensed under an appropriate license.
#. The plugin has an unambiguous name, description, and author metadata.
#. The plugin has a clear versioning scheme and the latest version of the plugin is kept compatible to the latest
stable version of pretix.
#. The plugin is properly packaged using standard Python packaging tools.
#. The plugin correctly declares its external dependencies.
#. A contact address is provided in case of security issues.
B. Isolation
------------
#. If any signal receivers use the `dispatch_uid`_ feature, the UIDs are prefixed by the plugin's name and do not
clash with other plugins.
#. If any templates or static files are shipped, they are located in subdirectories with the name of the plugin and do
not clash with other plugins or core files.
#. Any keys stored to the settings store are prefixed with the plugin's name and do not clash with other plugins or
core.
#. Any keys stored to the user session are prefixed with the plugin's name and do not clash with other plugins or
core.
#. Any registered URLs are unlikely to clash with other plugins or future core URLs.
C. Security
-----------
#. All important actions are logged to the :ref:`shared log storage <logging>` and a signal receiver is registered to
provide a human-readable representation of the log entry.
#. All views require appropriate permissions and use the ``event_urls`` mechanism if appropriate.
:ref:`Read more <customview>`
#. Any session data for customers is stored in the cart session system if appropriate.
#. If the plugin is a payment provider:
#. No credit card numbers may be stored within pretix.
#. A notification/webhook system is implemented to notify pretix of any refunds.
#. If such a webhook system is implemented, contents of incoming webhooks are either verified using a cryptographic
signature or are not being trusted and all data is fetched from an API instead.
D. Privacy
----------
#. No personal data is stored that is not required for the plugin's functionality.
#. For any personal data that is saved to the database, an appropriate :ref:`data shredder <shredder>` is provided
that offers the data for download and then removes it from the database (including log entries).
E. Internationalization
-----------------------
#. All user-facing strings in templates, Python code, and templates are wrapped in `gettext calls`_.
#. No languages, time zones, date formats, or time formats are hardcoded.
#. Installing the plugin automatically compiles ``.po`` files to ``.mo`` files. This is fulfilled automatically if
you use the ``setup.py`` file form our plugin cookiecutter.
F. Functionality
----------------
#. If the plugin adds any database models or relationships from the settings storage to database models, it registers
a receiver to the :py:attr:`pretix.base.signals.event_copy_data` or :py:attr:`pretix.base.signals.item_copy_data`
signals.
#. If the plugin is a payment provider:
#. A webhook-like system is implemented if payment confirmations are not sent instantly.
#. Refunds are implemented, if possible.
#. In case of overpayment or external refunds, a "required action" is created to notify the event organizer.
#. If the plugin adds steps to the checkout process, it has been tested in combination with the pretix widget.
G. Code quality
---------------
#. `isort`_ and `flake8`_ are used to ensure consistent code styling.
#. Unit tests are provided for important pieces of business logic.
#. Functional tests are provided for important interface parts.
#. Tests are provided to check that permission checks are working.
#. Continuous Integration is set up to check that tests are passing and styling is consistent.
H. Specific to pretix.eu
------------------------
#. pretix.eu integrates the data stored by this plugin with its data report features.
#. pretix.eu integrates this plugin in its generated privacy statements, if necessary.
.. _isort: https://www.google.de/search?q=isort&oq=isort&aqs=chrome..69i57j0j69i59j69i60l2j69i59.599j0j4&sourceid=chrome&ie=UTF-8
.. _flake8: http://flake8.pycqa.org/en/latest/
.. _gettext calls: https://docs.djangoproject.com/en/2.0/topics/i18n/translation/
.. _dispatch_uid: https://docs.djangoproject.com/en/2.0/topics/signals/#django.dispatch.Signal.connect

View File

@@ -1,94 +0,0 @@
.. highlight:: python
:linenothreshold: 5
.. _`shredder`:
Writing a data shredder
=======================
If your plugin adds the ability to store personal data within pretix, you should also implement a "data shredder"
to anonymize or pseudonymize the data later.
Shredder registration
---------------------
The data shredder API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available data shredders. Your plugin
should listen for this signal and return the subclass of ``pretix.base.shredder.BaseDataShredder``
that we'll provide in this plugin:
.. sourcecode:: python
from django.dispatch import receiver
from pretix.base.signals import register_data_shredders
@receiver(register_data_shredders, dispatch_uid="custom_data_shredders")
def register_shredder(sender, **kwargs):
return [
PluginDataShredder,
]
The shredder class
------------------
.. class:: pretix.base.shredder.BaseDataShredder
The central object of each invoice renderer is the subclass of ``BaseInvoiceRenderer``.
.. py:attribute:: BaseInvoiceRenderer.event
The default constructor sets this property to the event we are currently
working for.
.. autoattribute:: identifier
This is an abstract attribute, you **must** override this!
.. autoattribute:: verbose_name
This is an abstract attribute, you **must** override this!
.. autoattribute:: description
This is an abstract attribute, you **must** override this!
.. automethod:: generate_files
.. automethod:: shred_data
Example
-------
For example, the core data shredder responsible for removing invoice address information including their history
looks like this:
.. sourcecode:: python
class InvoiceAddressShredder(BaseDataShredder):
verbose_name = _('Invoice addresses')
identifier = 'invoice_addresses'
description = _('This will remove all invoice addresses from orders, '
'as well as logged changes to them.')
def generate_files(self) -> List[Tuple[str, str, str]]:
yield 'invoice-addresses.json', 'application/json', json.dumps({
ia.order.code: InvoiceAdddressSerializer(ia).data
for ia in InvoiceAddress.objects.filter(order__event=self.event)
}, indent=4)
@transaction.atomic
def shred_data(self):
InvoiceAddress.objects.filter(order__event=self.event).delete()
for le in self.event.logentry_set.filter(action_type="pretix.event.order.modified"):
d = le.parsed_data
if 'invoice_data' in d and not isinstance(d['invoice_data'], bool):
for field in d['invoice_data']:
if d['invoice_data'][field]:
d['invoice_data'][field] = ''
le.data = json.dumps(d)
le.shredded = True
le.save(update_fields=['data', 'shredded'])

View File

@@ -82,12 +82,6 @@ Orders
^^^^^^
If a customer completes the checkout process, an **Order** will be created containing all the entered information.
An order can be in one of currently four states that are listed in the diagram below:
An order can be in one of currently five states that are listed in the diagram below:
.. image:: /images/order_states.png
There are additional "fake" states that are displayed like states but not represented as states in the system:
* An order is considered **canceled (with paid fee)** if it is in **paid** status but does not include any non-cancelled positions.
* An order is considered **requiring approval** if it is in **pending** status with the ``require_approval`` attribute set to ``True``.

View File

@@ -77,6 +77,6 @@ Attribution
-----------
This Code of Conduct is adapted from the `Contributor Covenant`_, version 1.4,
available at https://www.contributor-covenant.org/version/1/4/
available at http://contributor-covenant.org/version/1/4/
.. _Contributor Covenant: https://www.contributor-covenant.org
.. _Contributor Covenant: http://contributor-covenant.org

View File

@@ -24,7 +24,7 @@ Coding style and quality
``Fix #123 -- Problems with order creation`` or ``Refs #123 -- Fix this part of that bug``.
.. _PEP 8: https://legacy.python.org/dev/peps/pep-0008/
.. _PEP 8: http://legacy.python.org/dev/peps/pep-0008/
.. _flake8: https://pypi.python.org/pypi/flake8
.. _Django Coding Style: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/
.. _translation: https://docs.djangoproject.com/en/1.11/topics/i18n/translation/

View File

@@ -16,5 +16,4 @@ Contents:
settings
background
email
permissions
logging

View File

@@ -4,8 +4,6 @@ Logging and notifications
As pretix is handling monetary transactions, we are very careful to make it possible to review all changes
in the system that lead to the current state.
.. _`logging`:
Logging changes
---------------

View File

@@ -23,7 +23,7 @@ Organizers and events
:members:
.. autoclass:: pretix.base.models.Event
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running, cache, lock, get_plugins, get_mail_backend, payment_term_last, get_payment_providers, get_invoice_renderers, invoice_renderer, settings
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running, cache, lock, get_plugins, get_mail_backend, payment_term_last, get_payment_providers, get_invoice_renderers, active_subevents, invoice_renderer, settings
.. autoclass:: pretix.base.models.SubEvent
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running
@@ -31,9 +31,6 @@ Organizers and events
.. autoclass:: pretix.base.models.Team
:members:
.. autoclass:: pretix.base.models.TeamAPIToken
:members:
.. autoclass:: pretix.base.models.RequiredAction
:members:
@@ -86,15 +83,6 @@ Carts and Orders
.. autoclass:: pretix.base.models.OrderPosition
:members:
.. autoclass:: pretix.base.models.OrderFee
:members:
.. autoclass:: pretix.base.models.OrderPayment
:members:
.. autoclass:: pretix.base.models.OrderRefund
:members:
.. autoclass:: pretix.base.models.CartPosition
:members:

View File

@@ -1,194 +0,0 @@
Permissions
===========
pretix uses a fine-grained permission system to control who is allowed to control what parts of the system.
The central concept here is the concept of *Teams*. You can read more on `configuring teams and permissions <user-teams>`_
and the :class:`pretix.base.models.Team` model in the respective parts of the documentation. The basic digest is:
An organizer account can have any number of teams, and any number of users can be part of a team. A team can be
assigned a set of permissions and connected to some or all of the events of the organizer.
A second way to access pretix is via the REST API, which allows authentication via tokens that are bound to a team,
but not to a user. You can read more at :class:`pretix.base.models.TeamAPIToken`. This page will show you how to
work with permissions in plugins and within the pretix code base.
Requiring permissions for a view
--------------------------------
pretix provides a number of useful mixins and decorators that allow you to specify that a user needs a certain
permission level to access a view::
from pretix.control.permissions import (
OrganizerPermissionRequiredMixin, organizer_permission_required
)
class MyOrgaView(OrganizerPermissionRequiredMixin, View):
permission = 'can_change_organizer_settings'
# Only users with the permission ``can_change_organizer_settings`` on
# this organizer can access this
class MyOtherOrgaView(OrganizerPermissionRequiredMixin, View):
permission = None
# Only users with *any* permission on this organizer can access this
@organizer_permission_required('can_change_organizer_settings')
def my_orga_view(request, organizer, **kwargs):
# Only users with the permission ``can_change_organizer_settings`` on
# this organizer can access this
@organizer_permission_required()
def my_other_orga_view(request, organizer, **kwargs):
# Only users with *any* permission on this organizer can access this
Of course, the same is available on event level::
from pretix.control.permissions import (
EventPermissionRequiredMixin, event_permission_required
)
class MyEventView(EventPermissionRequiredMixin, View):
permission = 'can_change_event_settings'
# Only users with the permission ``can_change_event_settings`` on
# this event can access this
class MyOtherEventView(EventPermissionRequiredMixin, View):
permission = None
# Only users with *any* permission on this event can access this
@event_permission_required('can_change_event_settings')
def my_event_view(request, organizer, **kwargs):
# Only users with the permission ``can_change_event_settings`` on
# this event can access this
@event_permission_required()
def my_other_event_view(request, organizer, **kwargs):
# Only users with *any* permission on this event can access this
You can also require that this view is only accessible by system administrators with an active "admin session"
(see below for what this means)::
from pretix.control.permissions import (
AdministratorPermissionRequiredMixin, administrator_permission_required
)
class MyGlobalView(AdministratorPermissionRequiredMixin, View):
# ...
@administrator_permission_required
def my_global_view(request, organizer, **kwargs):
# ...
In rare cases it might also be useful to expose a feature only to people who have a staff account but do not
necessarily have an active admin session::
from pretix.control.permissions import (
StaffMemberRequiredMixin, staff_member_required
)
class MyGlobalView(StaffMemberRequiredMixin, View):
# ...
@staff_member_required
def my_global_view(request, organizer, **kwargs):
# ...
Requiring permissions in the REST API
-------------------------------------
When creating your own ``viewset`` using Django REST framework, you just need to set the ``permission`` attribute
and pretix will check it automatically for you::
class MyModelViewSet(viewsets.ReadOnlyModelViewSet):
permission = 'can_view_orders'
Checking permission in code
---------------------------
If you need to work with permissions manually, there are a couple of useful helper methods on the :class:`pretix.base.models.Event`,
:class:`pretix.base.models.User` and :class:`pretix.base.models.TeamAPIToken` classes. Here's a quick overview.
Return all users that are in any team that is connected to this event::
>>> event.get_users_with_any_permission()
<QuerySet: …>
Return all users that are in a team with a specific permission for this event::
>>> event.get_users_with_permission('can_change_event_settings')
<QuerySet: …>
Determine if a user has a certain permission for a specific event::
>>> user.has_event_permission(organizer, event, 'can_change_event_settings', request=request)
True
Determine if a user has any permission for a specific event::
>>> user.has_event_permission(organizer, event, request=request)
True
In the two previous commands, the ``request`` argument is optional, but required to support staff sessions (see below).
The same method exists for organizer-level permissions::
>>> user.has_organizer_permission(organizer, 'can_change_event_settings', request=request)
True
Sometimes, it might be more useful to get the set of permissions at once::
>>> user.get_event_permission_set(organizer, event)
{'can_change_event_settings', 'can_view_orders', 'can_change_orders'}
>>> user.get_organizer_permission_set(organizer, event)
{'can_change_organizer_settings', 'can_create_events'}
Within a view on the ``/control`` subpath, the results of these two methods are already available in the
``request.eventpermset`` and ``request.orgapermset`` properties. This makes it convenient to query them in templates::
{% if "can_change_orders" in request.eventpermset %}
{% endif %}
You can also do the reverse to get any events a user has access to::
>>> user.get_events_with_permission('can_change_event_settings', request=request)
<QuerySet: …>
>>> user.get_events_with_any_permission(request=request)
<QuerySet: …>
Most of these methods work identically on :class:`pretix.base.models.TeamAPIToken`.
Staff sessions
--------------
.. versionchanged:: 1.14
In 1.14, the ``User.is_superuser`` attribute has been deprecated and statically set to return ``False``. Staff
sessions have been newly introduced.
System administrators of a pretix instance are identified by the ``is_staff`` attribute on the user model. By default,
the regular permission rules apply for users with ``is_staff = True``. The only difference is that such users can
temporarily turn on "staff mode" via a button in the user interface that grants them **all permissions** as long as
staff mode is active. You can check if a user is in staff mode using their session key:
>>> user.has_active_staff_session(request.session.session_key)
False
Staff mode has a hard time limit and during staff mode, a middleware will log all requests made by that user. Later,
the user is able to also save a message to comment on what they did in their administrative session. This feature is
intended to help compliance with data protection rules as imposed e.g. by GDPR.

View File

@@ -8,6 +8,5 @@ Developer documentation
setup
contribution/index
implementation/index
translation/index
api/index
structure

View File

@@ -18,15 +18,13 @@ External Dependencies
---------------------
Your should install the following on your system:
* Python 3.5 or newer
* Python 3.4 or newer
* ``pip`` for Python 3 (Debian package: ``python3-pip``)
* ``python-dev`` for Python 3 (Debian package: ``python3-dev``)
* On Debian/Ubuntu: ``python-venv`` for Python 3 (Debian package: ``python3-venv``)
* ``libffi`` (Debian package: ``libffi-dev``)
* ``libssl`` (Debian package: ``libssl-dev``)
* ``libxml2`` (Debian package ``libxml2-dev``)
* ``libxslt`` (Debian package ``libxslt1-dev``)
* ``libenchant1c2a`` (Debian package ``libenchant1c2a``)
* ``msgfmt`` (Debian package ``gettext``)
* ``git``
@@ -56,6 +54,10 @@ The first thing you need are all the main application's dependencies::
cd src/
pip3 install -r requirements.txt -r requirements/dev.txt
If you are working with Python 3.4, you will also need (you can skip this for Python 3.5+)::
pip3 install -r requirements/py34.txt
Next, you need to copy the SCSS files from the source folder to the STATIC_ROOT directory::
python manage.py collectstatic --noinput
@@ -113,21 +115,12 @@ Execute the following command to run pretix' test suite (might take a couple of
``NUM`` being the number of threads you want to use.
It is a good idea to put this command into your git hook ``.git/hooks/pre-commit``,
for example, to check for any errors in any staged files when committing::
for example::
#!/bin/bash
#!/bin/sh
cd $GIT_DIR/../src
export GIT_WORK_TREE=../
export GIT_DIR=../.git
source ../env/bin/activate # Adjust to however you activate your virtual environment
for file in $(git diff --cached --name-only | grep -E '\.py$' | grep -Ev "migrations|mt940\.py|pretix/settings\.py|make_testdata\.py|testutils/settings\.py|tests/settings\.py|pretix/base/models/__init__\.py")
do
echo $file
git show ":$file" | flake8 - --stdin-display-name="$file" || exit 1 # we only want to lint the staged changes, not any un-staged changes
git show ":$file" | isort -df --check-only - | grep ERROR && exit 1 || true
done
flake8 . || exit 1
isort -q -rc -c . || exit 1
This keeps you from accidentally creating commits violating the style guide.
@@ -152,10 +145,6 @@ and update the ``*.po`` files accordingly::
make localegen
However, most of the time you don't need to care about this. Just create your pull request
with functionality and English strings only, and we'll push the new translation strings
to our translation platform after the merge.
To actually see pretix in your language, you have to compile the ``*.po`` files to their
optimized binary ``*.mo`` counterparts::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -1,88 +0,0 @@
Translating pretix
==================
pretix has been designed for multi-language capabilities from its start. Organizers can enter their event information
in multiple languages at the same time. However, the software interface of pretix also needs to be translated for
this to be useful.
Since we (the developers of pretix) only speak a very limited number of languages, we need help from the community
to achieve this goal. To make translating pretix easy not only for software developers, we set up a translation
platform at `translate.pretix.eu`_.
Official and inofficial languages
---------------------------------
In the pretix project, there are three types of languages:
Official languages
are translated and maintained by the core team behind pretix or as part of long-term partnerships. We are
committed to keeping these translations up-to-date with new features or changes in pretix and try to offer
support in this language.
Inofficial languages
are contributed and maintained by the Community. We ship them with pretix so you can use them, but we can not
guarantee that new or changed features in pretix will be translated in time.
Incubating languages
are currently in the process of being translated. They can not yet be selected in pretix by end users on
production installations and are only available in development mode for testing.
Please contact translate@pretix.eu if you think an incubated language should be promoted to an inofficial one or if
you are interested in a partnership to make your language official.
The current translation status of various languages is:
.. image:: https://translate.pretix.eu/widgets/pretix/-/multi-blue.svg
:target: https://translate.pretix.eu/engage/pretix/?utm_source=widget
Using our translation platform
------------------------------
If you visit `translate.pretix.eu`_ for the first time, it admittedly looks pretty bare.
.. image:: img/weblate1.png
:class: screenshot
It gets better if you create an account, which you will need to contribute translations. Click on "Register" in the
top-right corner to get started:
.. image:: img/weblate2.png
:class: screenshot
You can either create an account or choose to log in with your GitHub account, whichever you like more.
After creating and activating your account, we recommend that you change your profile and select which languages you
can translate to and which languages you understand. You can find your profile settings by clicking on your name in
the top-right corner.
.. image:: img/weblate3.png
:class: screenshot
Going back to the dashboard by clicking on the logo in the top-left corner, you can select between different lists
of translation projects. You can either filter by projects that already have a translation in your language, or you
go to the `pretix project page`_ where you can select specific components.
.. note::
If you want to translate pretix to a new language that is not yet listed here, you are very welcome to do so!
While you technically can add the language to the portal yourself, we ask you to drop us a short mail to
translate@pretix.eu so we can add it to all components at once and also make it selectable in pretix itself.
.. image:: img/weblate4.png
:class: screenshot
Once you selected a component of a language, you can start going through strings to translate. You can start of by
clicking the "Strings needing action" line in this view:
.. image:: img/weblate5.png
:class: screenshot
In the translate view, you can input your translation for a given source string. If you're unsure about your
translation, you can also just "Suggest" it or mark it as "Needs editing". If you have no idea, just "Skip". If you
scroll down, there is also a "Comments" section to discuss any questions with fellow translators or us developers.
.. image:: img/weblate6.png
:class: screenshot
.. _translate.pretix.eu: https://translate.pretix.eu
.. _pretix project page: https://translate.pretix.eu/projects/pretix/

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -4,6 +4,7 @@ Pending: Order is expecting payment\nOrder reduces quotas
Expired: Payment period is over\nOrder does not affect quotas
Paid: Order was successful\nOrder reduces quotas
Canceled: Order has been canceled\nOrder does not affect quotas
Refunded: Order has been refunded\nOrder does not affect quotas
[*] --> Pending: customer\nplaces order
Pending --> Paid: successful payment
@@ -11,9 +12,8 @@ Pending --> Expired: automatically\nor manually\non admin action
Expired --> Paid: if payment is received\nonly if quota left
Expired --> Canceled
Expired --> Pending: manually\non admin action
Paid --> Canceled: manually on\nadmin action\nor if an external\npayment provider\nnotifies about a\npayment refund
Paid --> Refunded: manually on\nadmin action\nor if an external\npayment provider\nnotifies about a\npayment refund
Pending --> Canceled: on admin or\ncustomer action
Paid -> Pending: manually on admin action
[*] --> Paid: customer\nplaces free order
@enduml

View File

@@ -1,150 +0,0 @@
Badges
======
The badges plugin provides a HTTP API that exposes the various layouts used to generate PDF badges.
Resource description
--------------------
The badge layout resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal layout ID
name string Internal layout description
default boolean ``true`` if this is the default layout
layout object Layout specification for libpretixprint
background URL Background PDF file
item_assignments list of objects Products this layout is assigned to
└ item integer Item ID
===================================== ========================== =======================================================
.. versionchanged:: 1.16
This resource has been added.
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/badgelayouts/
Returns a list of all badge layouts
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/democon/badgelayouts/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "Default layout",
"default": true,
"layout": {…},
"background": {},
"item_assignments": []
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of a valid event
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/badgelayouts/(id)/
Returns information on layout.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/democon/layoutsbadge/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"name": "Default layout",
"default": true,
"layout": {…},
"background": {},
"item_assignments": []
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the layout to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/badgeitems/
Returns a list of all assignments of items to layouts
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/democon/badgeitems/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"layout": 2,
"item": 3,
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of a valid event
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.

View File

@@ -12,5 +12,3 @@ If you want to **create** a plugin, please go to the
list
pretixdroid
banktransfer
ticketoutputpdf
badges

View File

@@ -4,10 +4,10 @@ pretixdroid HTTP API
The pretixdroid plugin provides a HTTP API that the `pretixdroid Android app`_
uses to communicate with the pretix server.
.. warning:: This API is **DEPRECATED** and will probably go away soon. It is used **only** to serve the pretixdroid
Android app. There are no backwards compatibility guarantees on this API. We will not add features that
are not required for the Android App. There is a general-purpose :ref:`rest-api` that provides all
features that you need to check in.
.. warning:: This API is intended **only** to serve the pretixdroid Android app. There are no backwards compatibility
guarantees on this API. We will not add features that are not required for the Android App. There is a
general-purpose :ref:`rest-api` that not yet provides all features that this API provides, but will do
so in the future.
.. versionchanged:: 1.12
@@ -81,7 +81,6 @@ uses to communicate with the pretix server.
"attention": false,
"redeemed": true,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
}
}
@@ -107,7 +106,6 @@ uses to communicate with the pretix server.
"attention": false,
"redeemed": true,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
},
"questions": [
@@ -154,7 +152,6 @@ uses to communicate with the pretix server.
"attention": false,
"redeemed": true,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
}
}
@@ -215,7 +212,6 @@ uses to communicate with the pretix server.
"redeemed": false,
"attention": false,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
},
...
@@ -316,7 +312,7 @@ uses to communicate with the pretix server.
"total": 42,
"version": 3,
"event": {
"name": "Demo Conference",
"name": "Demo Converence",
"slug": "democon",
"date_from": "2016-12-27T17:00:00Z",
"date_to": "2016-12-30T18:00:00Z",

View File

@@ -1,157 +0,0 @@
PDF ticket output
=================
The PDF ticket output plugin provides a HTTP API that exposes the various layouts used
to generate PDF tickets.
Resource description
--------------------
The ticket layout resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal layout ID
name string Internal layout description
default boolean ``true`` if this is the default layout
layout object Layout specification for libpretixprint
background URL Background PDF file
item_assignments list of objects Products this layout is assigned to
├ sales_channel string Sales channel (defaults to ``web``).
└ item integer Item ID
===================================== ========================== =======================================================
.. versionchanged:: 1.16
This resource has been added.
.. versionchanged:: 2.3
The ``item_assignments.sales_channel`` field has been added.
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/ticketlayouts/
Returns a list of all ticket layouts
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/democon/ticketlayouts/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "Default layout",
"default": true,
"layout": {…},
"background": {},
"item_assignments": []
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of a valid event
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/ticketlayouts/(id)/
Returns information on layout.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/democon/ticketlayouts/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"name": "Default layout",
"default": true,
"layout": {…},
"background": {},
"item_assignments": []
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the layout to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/ticketlayoutitems/
Returns a list of all assignments of items to layouts
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/democon/ticketlayoutitems/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"layout": 2,
"item": 3,
"sales_channel": web
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of a valid event
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.

View File

@@ -4,5 +4,4 @@ sphinx-rtd-theme
sphinxcontrib-httpdomain
sphinxcontrib-images
sphinxcontrib-spelling
# See https://github.com/rfk/pyenchant/pull/130
git+https://github.com/raphaelm/pyenchant.git@patch-1#egg=pyenchant
pyenchant

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,33 +1,24 @@
addon
addons
Analytics
anonymize
api
auditability
auth
autobuild
availabilities
backend
backends
banktransfer
Bcc
boolean
booleans
cancelled
casted
Ceph
checkbox
checksum
config
contenttypes
contextmanager
cookiecutter
cron
cronjob
cryptographic
debian
deduplication
deprovision
discoverable
django
dockerfile
@@ -42,19 +33,13 @@ gettext
gunicorn
hardcoded
hostname
idempotency
incrementing
inofficial
invalidations
iterable
Jimdo
libpretixprint
libsass
linters
memcached
metadata
middleware
Minio
mixin
mixins
multi
@@ -68,9 +53,7 @@ nginx
NotificationType
ons
optimizations
overpayment
param
passphrase
percental
positionid
pre
@@ -85,8 +68,6 @@ pretixpresale
prometheus
proxied
proxying
pseudonymize
pseudonymization
queryset
redemptions
redis
@@ -95,12 +76,7 @@ regex
renderer
renderers
reportlab
SaaS
scalability
screenshot
scss
searchable
selectable
serializers
serializers
sexualized
@@ -114,24 +90,18 @@ subdomains
subevent
subevents
submodule
subnet
subpath
Symfony
systemd
testmode
testutils
timestamp
tuples
un
unconfigured
unix
unprefixed
untrusted
uptime
username
url
versa
versioning
viewset
viewsets
webhook

View File

@@ -21,18 +21,11 @@ Frontpage text
your product types, give more information on the event or for other general notices.
You can use :ref:`Markdown syntax <markdown-guide>` in this field.
Voucher explanation
This text will be shown above the voucher input box. You can use it to explain how to obtain a voucher and use it.
Show variations of a product expanded by default
If this is not checked, a product with variations will be shown as one row in the show by default and will expand
into multiple rows once it is clicked on. With this box checked, the variations will be shown as multiple rows
right from the beginning.
Ask search engines not to index the ticket shop
If this is checked, we will set a HTML meta attribute asking search engines by Google not to put this ticket shop
into their searchable index.
The lower part of the page contains settings that you can **either** set on organizer-level for all your events **or**
override for this single event individually. Those are:
@@ -42,12 +35,6 @@ Primary color
customers. We suggest not choosing something to light, since text in that color should be readable on a white
background and white text should be readable on a background of this color.
Accent color for success
This color will be used for success messages. We suggest to choose a dark shade of green.
Accent color for errors
This color will be used for error messages. We suggest to choose a dark shade of red.
Font
Choose one of multiple fonts to use for your web shop.

View File

@@ -8,8 +8,8 @@ event.
:align: center
:class: screenshot
The page is separated into four parts: "E-mail settings", "E-mail design", "E-mail content" and "SMTP settings".
We will explain all of them in detail on this page.
The page is separated into three parts: "E-mail settings", "E-mail content" and "SMTP settings". We will explain all
of them in detail on this page.
E-mail settings
---------------
@@ -30,18 +30,10 @@ Signature
This text will be appended to all e-mails in form of a signature. This might be useful e.g. to add your contact
details or any legal information that needs to be included with the e-mails.
Bcc address
This email address will receive a copy of every event-related email.
E-mail design
-------------
In this part, you can choose and preview the layout of your emails. More layouts can be added by pretix plugins.
E-mail content
--------------
The next part of the page allows you to customize the exact texts of all e-mails sent by the system automatically.
The middle part of the page allows you to customize the exact texts of all e-mails sent by the system automatically.
You can click on the different boxes to expand them and see the texts.
Within the texts, you can use placeholders that will later by replaced by values depending on the event or order. Below
@@ -53,7 +45,6 @@ Placeholder Description
============================== ===============================================================================
event The event name
total The order's total value
total_with_currency The order's total value with a localized currency sign
currency The currency used for the event (three-letter code)
payment_info Information text specific to the payment method (e.g. banking details)
url An URL pointing to the download/status page of the order
@@ -121,22 +112,6 @@ Reminder to download tickets
attendees to download their tickets. The e-mail should include a link to the ticket download. This e-mail will only
ever be sent if you specify a number of days.
Order approval process
If you configure one of your products to "require approval", orders of that product will not immediately be confirmed
but only after you approved them manually. In this case, the following e-mail templates will be sent out.
Received order
After an order has been received, this e-mail will be sent automatically instead of the "order placed" e-mail from
above.
Approved order
This e-mail will be sent after you manually approved an order. This should include instructions to pay for the order,
which is why this will only be used for a paid order. For a free order, the "free order" e-mail from above will
be sent.
Denied order
This e-mail will be sent out to customers when their order has been denied.
SMTP settings
-------------
@@ -151,29 +126,4 @@ With the checkbox "Use custom SMTP server" you can turn using your SMTP server o
button "Save and test custom SMTP connection", you can test if the connection and authentication to your SMTP server
succeeds, even before turning that checkbox on.
Spam issues
-----------
If you use an email address of your own domain as a sender address and do not use a custom SMTP server, it is very
likely that at least some of your emails will go to the spam folders of their recipients. We **strongly recommend**
to use your organization's SMTP server in this case, making your email really come from your organization. If you don't
want that or cannot do that, you should add the pretix application server to your SPF record.
If you are using our hosted service at pretix.eu, you can add the following to your SPF record::
include:_spf.pretix.eu
A complete record could look like this::
v=spf1 a mx include:_spf.pretix.eu ~all
Make sure to read up on the `SPF specification`_. If you want to authenticate your emails with DKIM, set up a DNS TXT
record for the subdomain ``pretix._domainkey`` with the following contents::
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXrDk6lwOWX00e2MbiiJac6huI+gnzLf9N4G1FnBv3PXq8fz3i2q1szH72OF5mAlKm3zXO4cl/uxx+lfidS1ERbX6Bn9BRstBTQUKWC4JFj8Yk9+fwT7LWehDURazLdTzfsIjJFudLLvxtOKSaOCtMhbPX05DIhziaqVCBqgz/NQIDAQAB
Then, please contact support@pretix.eu and we will enable DKIM for your domain on our mail servers.
.. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework
.. _SPF specification: http://www.openspf.org/SPF_Record_Syntax

View File

@@ -11,6 +11,18 @@ The settings at "Settings" → "Invoice" allow you to specify if and how pretix
In particular, you can configure the following things:
Ask for invoice address
If this checkbox is enabled, customers will be able to enter an invoice address during checkout. If you only enable
this box, the invoice address will be optional to fill in.
Require invoice address
If this checkbox is enabled, entering an invoice address will be obligatory for all customers and it will not be
able to create an order without entering an address.
Require customer name
If this checkbox is enabled, the street, city, and country fields of the invoice address will still be optional but
the name field will be obligatory.
Generate invoices
This field controls whether pretix should generate an invoice for an order. You have the following options:
@@ -39,51 +51,6 @@ Attach invoices to emails
"Automatically for all created orders" or to the payment confirmation e-mails if it is set to "Automatically on
payment".
Invoice number prefix
This is the prefix that will be prepended to all your invoice numbers. For example, if you set this to "Inv", your
invoices will be numbered Inv00001, Inv00002, etc. If you leave this field empty, your event slug will be used,
followed by a dash, e.g. DEMOCON-00001.
Within one organizer account, events with the same number prefix will share their number range. For example, if you
set this to "Inv" for all of your events, there will be only one invoice numbered Inv00007 across all your events
and the numbers will have gaps within one event.
Generate invoices with consecutive numbers
If enabled, invoices will be created with numerical invoice numbers in the order of their creation, i.e.
PREFIX-00001, PREFIX-00002, and so on. If disabled, invoice numbers will instead be generated from the order code,
i.e. PREFIX-YHASD-1. When in doubt, keep this option enabled since it might be legally required in your country,
but disabling it has the advantage that your customers can not estimate the number of tickets sold by looking at
the invoice numbers.
Invoice language
This setting allows you to configure the language of all invoices. You can either set it to one of your event
language or dynamically to the language used by the customer.
Show free products on invoices
If enabled, products that do not cost anything will still show up on invoices. Note that the order needs to contain
at least one non-free product in order to generate an invoice.
Show attendee names on invoices
If enabled, the attendee name will be printed on the invoice for admission tickets.
Ask for invoice address
If this checkbox is enabled, customers will be able to enter an invoice address during checkout. If you only enable
this box, the invoice address will be optional to fill in.
Require invoice address
If this checkbox is enabled, entering an invoice address will be obligatory for all customers and it will not be
able to create an order without entering an address.
Require customer name
If this checkbox is enabled, the street, city, and country fields of the invoice address will still be optional but
the name field will be obligatory.
Require a business address
If enabled, the invoice address form will require a company name and do not allow personal addresses.
Ask for beneficiary
If enabled, the invoice address form will contain an additional field to input the beneficiary of the transaction.
Ask for VAT ID
If enabled, the invoice address form will not only ask for a postal address, but also for a VAT ID. The VAT ID will
always be an optional field.
@@ -95,12 +62,25 @@ Generate invoices with consecutive numbers
but disabling it has the advantage that your customers can not estimate the number of tickets sold by looking at
the invoice numbers.
Your invoice details
These fields should be set to the address of the entity issuing the invoice (read: you) and will be printed inside
the header of the invoice.
Invoice number prefix
This is the prefix that will be prepended to all your invoice numbers. For example, if you set this to "Inv", your
invoices will be numbered Inv00001, Inv00002, etc. If you leave this field empty, your event slug will be used,
followed by a dash, e.g. DEMOCON-00001.
Invoice style
This setting allows you to choose the design of your invoice. Additional designs can be added by pretix plugins.
Within one organizer account, events with the same number prefix will share their number range. For example, if you
set this to "Inv" for all of your events, there will be only one invoice numbered Inv00007 across all your events
and the numbers will have gaps within one event.
Show free products on invoices
If enabled, products that do not cost anything will still show up on invoices. Note that the order needs to contain
at least one non-free product in order to generate an invoice.
Show attendee names on invoices
If enabled, the attendee name will be printed on the invoice for admission tickets.
Your address
This should be set to the address of the entity issuing the invoice (read: you) and will be printed inside
the header of the invoice.
Introductory text
A free custom text that will be printed above the list of products on the invoice.

View File

@@ -1,260 +0,0 @@
Product structure guide
=======================
Between products, categories, variations, add-ons, bundles, and quotas, pretix provides a wide range of features that allow you to set up your event in the way you want it.
However, it is easy to get lost in the process or to get started with building your event right.
Often times, there are multiple ways to do something that come with different advantages and disadvantages.
This guide will walk you through a number of typical examples of pretix event structures and will explain how to set them up feel free to just skip ahead to a section relevant for you.
Terminology
-----------
Products
A product is a basic entity that can be bought. You can think of it as a ticket type, but it can be more things than just a ticket, it can also be a piece of merchandise, a parking slot, etc.
You might find some places where they are called "items" instead, but we're trying to get rid of that.
Product categories
Products can be sorted into categories. Each product can only be in one category. Category are mostly used for grouping related products together to make your event page easier to read for buyers. However, we'll need categories as well to set up some of the structures outlined below.
Product variations
During creation of a product, you can decide that your product should have multiple variations. Variations of a product can differ in price, description, and availability, but are otherwise the same. You could use this e.g. for differentiating between a regular ticket and a discounted ticket for students, or when selling merchandise to differentiate the different sizes of a t-shirt.
Product add-ons
Add-ons are products that are sold together with another product (which we will call the base product in this case). For example, you could have a base product "Conference ticket" and then define multiple workshops that can be chosen as an add-on.
Product bundles
Bundles work very similarly to add-ons, but are different in the way that they are always automatically included with the base product and cannot be optional. In contrast to add-on products, the same product can be included multiple times in a bundle.
Quotas
Quotas define the availability of products. A quota has a size (i.e. the number of products in the inventory) and is mapped to one or multiple products or variations.
Questions
Questions are user-defined form fields that buyers will need to fill out when purchasing a product.
Use case: Multiple price levels
-------------------------------
Imagine you're running a concert with general admission that sells a total of 200 tickets for two prices:
* Regular: € 25.00
* Students: € 19.00
You can either set up two different products called e.g. "Regular ticket" and "Student ticket" with the respective prices, or two variations within the same product. In this simple case, it really doesn't matter.
In addition, you will need quotas. If you do not care how many of your tickets are sold to students, you should set up just **one quota** of 200 called e.g. "General admission" that you link to **both products**.
If you want to limit the number of student tickets to 50 to ensure a certain minimum revenue, but do not want to limit the number of regular tickets artificially, we suggest you to create the same quota of 200 that is linked to both products, and then create a **second quota** of 50 that is only linked to the student ticket. This way, the system will reduce both quotas whenever a student ticket is sold and only the larger quota when a regular ticket is sold.
Use case: Early-bird tiers
--------------------------
Let's say you run a conference that has the following pricing scheme:
* 12 to 6 months before the event: € 450
* 6 to 3 months before the event: € 550
* closer than 3 months to the event: € 650
Of course, you could just set up one product and change its price at the given dates manually, but if you want to set this up automatically, here's how:
Create three products (e.g. "super early bird", "early bird", "regular ticket") with the respective prices and one shared quota of your total event capacity. Then, set the **available from** and **available until** configuration fields of the products to automatically turn them on and off based on the current date.
.. note::
pretix currently can't do early-bird tiers based on **ticket number** instead of time. We're planning this feature for later in 2019. For now, you'll need to monitor that manually.
Use case: Up-selling of ticket extras
-------------------------------------
Let's assume you're putting up a great music festival, and to save trouble with handling payments on-site, you want to sell parking spaces together with your ticket. By using our add-on feature, you can prompt all users to book the parking space (to make sure they see it) and ensure that only people with a ticket can book a parking space. You can set it up like this:
* Create a base product "Festival admission"
* Create a quota for the base product
* Create a category "Ticket extras" and check "Products in this category are add-on products"
* Create a product "Parking space" within that category
* Create a quota for the parking space product
* Go to the base product and select the tab "Add-Ons" at the top. Click "Add a new add-on" and choose the "Ticket extras" category. You can keep the numbers at 0 and 1.
During checkout, all buyers of the base product will now be prompted if they want to add the parking space.
.. tip::
You can also use add-on products for free things, just to keep tabs on capacity.
Use case: Conference with workshops
-----------------------------------
When running a conference, you might also organize a number of workshops with smaller capacity. To be able to plan, it would be great to know which workshops an attendee plans to attend.
Your first and simplest option is to just create a multiple-choice question. This has the upside of making it easy for users to change their mind later on, but will not allow you to restrict the number of attendees signing up for a given workshop or even charge extra for a given workshop.
The usually better option is to go with add-on products. Let's take for example the following conference schedule, in which the lecture can be attended by anyone, but the workshops only have space for 20 persons each:
==================== =================================== ===================================
Time Room A Room B
==================== =================================== ===================================
Wednesday morning Lecture
Wednesday afternoon Workshop A Workshop B
Thursday morning Workshop C Workshop D (20 € extra charge)
==================== =================================== ===================================
Assuming you already created one or more products for your general conference admission, we suggest that you additionally create:
* A category called "Workshops" with the checkbox "Products in this category are add-on products" activated
* A free product called "Wednesday afternoon" within the category "Workshops" and with two variations:
* Workshop A
* Workshop B
* A free product called "Thursday morning" within the category "Workshops" and with two variations:
* Workshop C
* Workshop D with a price of 20 €
* Four quotas for each of the workshops
* One add-on configuration on your base product that allows users to choose between 0 and 2 products from the category "Workshops"
Use case: Discounted packages
-----------------------------
Imagine you run a trade show that opens on three consecutive days and you want to have the following pricing:
* Single day: € 10
* Any two days: € 17
* All three days: € 25
In this case, there are multiple different ways you could set this up with pretix.
Option A: Combination products
""""""""""""""""""""""""""""""
With this option, you just set up all the different combinations someone could by as a separate product. In this case, you would need 7 products:
* Day 1 pass
* Day 2 pass
* Day 3 pass
* Day 1+2 pass
* Day 2+3 pass
* Day 1+3 pass
* All-day pass
Then, you create three quotas, each one with the maximum capacity of your venue on any given day:
* Day 1 quota, linked to "Day 1 pass", "Day 1+2 pass", "Day 1+3 pass", and "All-day pass"
* Day 2 quota, linked to "Day 2 pass", "Day 1+2 pass", "Day 2+3 pass", and "All-day pass"
* Day 3 quota, linked to "Day 3 pass", "Day 2+3 pass", "Day 1+3 pass", and "All-day pass"
This way, every person gets exactly one ticket that they can use for all days that they attend. You can later set up check-in lists appropriately to make sure only tickets valid for a certain day can be scanned on that day.
The benefit of this option is that your product structure and order structure stays very simple. However, the two-day packages scale badly when you need many products.
We recommend this setup for most setups in which the number of possible combinations does not exceed the number of parts (here: number of days) by much.
Option B: Add-ons and bundles
"""""""""""""""""""""""""""""
We can combine the two features "product add-ons" and "product bundles" to set this up in a different way. Here, you would create the following five products:
* Day 1 pass in a category called "Day passes"
* Day 2 pass in a category called "Day passes"
* Day 3 pass in a category called "Day passes"
* Two-day pass
* All-day pass
This time, you will need five quotas:
* Day 1 quota, linked to "Day 1 pass"
* Day 2 quota, linked to "Day 2 pass"
* Day 3 quota, linked to "Day 3 pass"
* Two-day pass quota, linked to "Two-day pass" (can be unlimited)
* All-day pass quota, linked to "All-day pass" (can be unlimited)
Then, you open the "Add-On" tab in the settings of the **Two-day pass** product and create a new add-on configuration specifying the following options:
* Category: "Day passes"
* Minimum number: 2
* Maximum number: 2
* Add-Ons are included in the price: Yes
This way, when buying a two-day pass, the user will be able to select *exactly* two days for free, which will then be added to the cart. Depending on your specific configuration, the user will now receive *two separate* tickets, one for each day.
For the all-day pass, you open the "Bundled products" tab in the settings of the **All-day pass** product and add **three** new bundled items with the following options:
* Bundled product: "Day 1/2/3"
* Bundled variation: None
* Count: 1
* Designated price: 0
This way, when buying an all-day pass, three free day passes will *automatically* be added to the cart. Depending on your specific configuration, the user will now receive *three separate* tickets, one for each day.
This approach makes your order data more complicated, since e.g. someone who buys an all-day pass now technically bought **four products**. However, this option allows for more flexibility when you have lots of options to choose from.
.. tip::
Depending on the packages you offer, you **might not need both the add-on and the bundle feature**, i.e. you only need the add-on feature for the two-day pass and only the bundle feature for the all-day pass. You could also set up the two-day pass like we showed here, but the all-day pass like in option A!
Use case: Group discounts
-------------------------
Often times, you want to give discounts for whole groups attending your event. pretix can't automatically discount based on volume, but there's still some ways you can set up group tickets.
Flexible group sizes
""""""""""""""""""""
If you want to give out discounted tickets to groups starting at a given size, but still billed per person, you can do so by creating a special **Group ticket** at the per-person price and set the **Minimum amount per order** option of the ticket to the minimal group size.
This way, your ticket can be bought an arbitrary number of times but no less than the given minimal amount per order.
Fixed group sizes
"""""""""""""""""
If you want to sell group tickets in fixed sizes, e.g. a table of eight at your gala dinner, you can use product bundles. Assuming you already set up a ticket for admission of single persons, you then set up a second product **Table (8 persons)** with a discounted full price. Then, head to the **Bundled products** tab of that product and add one bundle configuration to include the single admission product **eight times**. Next, create an unlimited quota mapped to the new product.
This way, the purchase of a table will automatically create eight tickets, leading to a correct calculation of your total quota and, as expected, eight persons on your check-in list. You can even ask for the individual names of the persons during checkout.
Use case: Restricted audience
-----------------------------
Not all events are for everyone. Sometimes, there is a good reason to restrict access to your event or parts of your event only to a specific, invited group. There's two ways to implement this with pretix:
Option A: Required voucher codes
""""""""""""""""""""""""""""""""
If you check the option "**This product can only be bought using a voucher**" of one or multiple products, only people holding an applicable voucher code will be able to buy the product.
You can then generate voucher codes for the respective product and send them out to the group of possible attendees. If the recipients should still be able to choose between different products, you can create an additional quota and map the voucher to that quota instead of the products themselves.
There's also the second option "**This product will only be shown if a voucher matching the product is redeemed**". In this case, the existence of the product won't even be shown before a voucher code is entered useful for a VIP option in a shop where you also sell other products to the general public. Please note that this option does **not** work with vouchers assigned to a quota, only with vouchers assigned directly to the product.
This option is appropriate if you know the group of people beforehand, e.g. members of a club, and you can mail them their access codes.
Option B: Order approvals
"""""""""""""""""""""""""
If you do not know your audience already, but still want to restrict it to a certain group, e.g. people with a given profession, you can check the "**Buying this product requires approval**" in the settings of your product. If a customer tries to buy such a product, they will be able to place their order but can not proceed to payment. Instead, you will be asked to approve or deny the order and only if you approve it, we will send a payment link to the customer.
This requires the customer to interact with the ticket shop twice (once for the order, once for the payment) which adds a little more friction, but gives you full control over who attends the event.
Use case: Mixed taxation
------------------------
Let's say you are a charitable organization in Germany and are allowed to charge a reduced tax rate of 7% for your educational event. However, your event includes a significant amount of food, you might need to charge a 19% tax rate on that portion. For example, your desired tax structure might then look like this:
* Conference ticket price: € 450 (incl. € 150 for food)
* incl. € 19.63 VAT at 7%
* incl. € 23.95 VAT at 19%
You can implement this in pretix using product bundles. In order to do so, you should create the following two products:
* Conference ticket at € 450 with a 7% tax rule
* Conference food at € 150 with a 19% tax rule and the option "**Only sell this product as part of a bundle**" set
In addition to your normal conference quota, you need to create an unlimited quota for the food product.
Then, head to the **Bundled products** tab of the "conference ticket" and add the "conference food" as a bundled product with a **designated price** of € 150.
Once a customer tries to buy the € 450 conference ticket, a sub-product will be added and the price will automatically be split into the two components, leading to a correct computation of taxes.

View File

@@ -25,10 +25,6 @@ Generate tickets for non-admission products
By default, tickets will only be generated for products that are marked as admission products. Enable this option to
generate tickets for all products instead.
Offer to download tickets even before an order is paid
By default, ticket download is only possible for paid orders. If you run an event where people usually pay only after
the event, you can check this box to enable ticket download even before.
Below these settings, the detail settings for the various ticket file formats are offered. They differ from format to
format and only share the common "Enable" setting that can be used to turn them on. By default, pretix ships with
a PDF output plugin that you can configure through a visual design editor.

View File

@@ -36,12 +36,6 @@ The second snippet should be embedded at the position where the widget should sh
You can of course embed multiple widgets of multiple events on your page. In this case, please add the first
snippet only *once* and the second snippets once *for each event*.
.. note::
Some website builders like Jimdo have trouble with our custom HTML tag. In that case, you can use
``<div class="pretix-widget-compat" …></div>`` instead of ``<pretix-widget …></pretix-widget>`` starting with
pretix 1.14.
Example
-------
@@ -107,42 +101,6 @@ voucher's settings.
</div>
</noscript>
Disabling the voucher input
---------------------------
If you want to disable voucher input in the widget, you can pass the ``disable-vouchers`` attribute::
<pretix-widget event="https://pretix.eu/demo/democon/" disable-vouchers></pretix-widget>
Multi-event selection
---------------------
If you want to embed multiple events in a single widget, you can do so. If it's multiple dates of an event series, just leave off the ``series`` attribute::
<pretix-widget event="https://pretix.eu/demo/series/"></pretix-widget>
If you want to include all your public events, you can just reference your organizer::
<pretix-widget event="https://pretix.eu/demo/"></pretix-widget>
There is an optional ``style`` parameter that let's you choose between a calendar view and a list view. If you do not set it, the choice will be taken from your organizer settings::
<pretix-widget event="https://pretix.eu/demo/series/" style="list"></pretix-widget>
<pretix-widget event="https://pretix.eu/demo/series/" style="calendar"></pretix-widget>
You can see an example here:
.. raw:: html
<pretix-widget event="https://pretix.eu/demo/series/" style="calendar"></pretix-widget>
<noscript>
<div class="pretix-widget">
<div class="pretix-widget-info-message">
JavaScript is disabled in your browser. To access our ticket shop without javascript, please <a target="_blank" href="https://pretix.eu/demo/series/">click here</a>.
</div>
</div>
</noscript>
pretix Button
-------------
@@ -172,107 +130,14 @@ resources. Then, instead of the ``pretix-widget`` tag, use the ``pretix-button``
As you can see, the ``pretix-button`` element takes an additional ``items`` attribute that specifies the items that
should be added to the cart. The syntax of this attribute is ``item_ITEMID=1,item_ITEMID=2,variation_ITEMID_VARID=4``
where ``ITEMID`` are the internal IDs of items to be added and ``VARID`` are the internal IDs of variations of those
items, if the items have variations. If you omit the ``items`` attribute, the general start page will be presented.
items, if the items have variations.
Just as the widget, the button supports the optional attributes ``voucher`` and ``skip-ssl-check``.
You can style the button using the ``pretix-button`` CSS class.
Dynamically loading the widget
------------------------------
.. versionchanged:: 1.13
If you need to control the way or timing the widget loads, for example because you want to modify user data (see
below) dynamically via JavaScript, you can register a listener that we will call before creating the widget::
<script type="text/javascript">
window.pretixWidgetCallback = function () {
// Will be run before we create the widget.
}
</script>
If you want, you can suppress us loading the widget and/or modify the user data passed to the widget::
<script type="text/javascript">
window.pretixWidgetCallback = function () {
window.PretixWidget.build_widgets = false;
window.PretixWidget.widget_data["email"] = "test@example.org";
}
</script>
If you then later want to trigger loading the widgets, just call ``window.PretixWidget.buildWidgets()``.
Passing user data to the widget
-------------------------------
If you display the widget in a restricted area of your website and you want to pre-fill fields in the checkout process
with known user data to save your users some typing and increase conversions, you can pass additional data attributes
with that information::
<pretix-widget event="https://pretix.eu/demo/democon/"
data-attendee-name-given-name="John"
data-attendee-name-family-name="Doe"
data-invoice-address-name-given-name="John"
data-invoice-address-name-family-name="Doe"
data-email="test@example.org"
data-question-L9G8NG9M="Foobar">
</pretix-widget>
This works for the pretix Button as well. Currently, the following attributes are understood by pretix itself:
* ``data-email`` will pre-fill the order email field as well as the attendee email field (if enabled).
* ``data-question-IDENTIFIER`` will pre-fill the answer for the question with the given identifier. You can view and set
identifiers in the *Questions* section of the backend.
* Depending on the person name scheme configured in your event settings, you can pass one or more of
``data-attendee-name-full-name``, ``data-attendee-name-given-name``, ``data-attendee-name-family-name``,
``data-attendee-name-middle-name``, ``data-attendee-name-title``, ``data-attendee-name-calling-name``,
``data-attendee-name-latin-transcription``. If you don't know or don't care, you can also just pass a string as
``data-attendee-name``, which will pre-fill the last part of the name, whatever that is.
* ``data-invoice-address-FIELD`` will pre-fill the corresponding field of the invoice address. Possible values for
``FIELD`` are ``company``, ``street``, ``zipcode``, ``city`` and ``country``, as well as fields specified by the
naming scheme such as ``name-title`` or ``name-given-name`` (see above). ``country`` expects a two-character
country code.
Any configured pretix plugins might understand more data fields. For example, if the appropriate plugins on pretix
Hosted or pretix Enterprise are active, you can pass the following fields:
* If you use the campaigns plugin, you can pass a campaign ID as a value to ``data-campaign``. This way, all orders
made through this widget will be counted towards this campaign.
* If you use the tracking plugin, you can pass a Google Analytics User ID to enable cross-domain tracking. This will
require you to dynamically load the widget, like this::
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXXX-1', 'auto');
ga('send', 'pageview');
window.pretixWidgetCallback = function () {
window.PretixWidget.build_widgets = false;
window.addEventListener('load', function() { // Wait for GA to be loaded
if(window.ga && ga.create) {
ga(function(tracker) {
window.PretixWidget.widget_data["tracking-ga-id"] = tracker.get('clientId');
window.PretixWidget.buildWidgets()
});
} else { // Tracking is probably blocked
window.PretixWidget.buildWidgets()
}
});
};
</script>
.. versionchanged:: 2.3
Data passing options have been added in pretix 2.3. If you use a self-hosted version of pretix, they only work
fully if you configured a redis server.
The pretix Button has been added in version 1.13.
.. _Let's Encrypt: https://letsencrypt.org/

View File

@@ -4,10 +4,22 @@ FAQ and Troubleshooting
How can I test my shop before taking it live?
---------------------------------------------
On your event dashboard, click on the first tile that shows your shop status. On the lower part of this page, you can
place your event into "test mode". In "test mode", everything behaves the same, but orders created during test mode can
later be fully deleted. Be sure to actually delete them when or after you turn off test mode, since test mode orders
still count toward your quotas and are included in your reports.
There are multiple ways to do this.
First, you could just create some orders in your real shop and cancel/refund them later. If you don't want to process
real payments for the tests, you can either use a "manual" payment method like bank transfer and just mark the orders
as paid with the button in the backend, or if you want to use e.g. Stripe, you can configure pretix to use your keys
for the Stripe test system and use their test credit cars. Read our :ref:`Stripe documentation <stripe>` for more
information.
Second, you could create a separate event, just for testing. In the last step of the :ref:`event creation process <event_create>`,
you can specify that you want to copy all settings from your real event, so you don't have to do all of it twice.
We are planning to add a dedicated test mode in a later version of pretix.
If you are using the hosted service at pretix.eu and want to get rid of the test orders completely, contact us at
support@pretix.eu and we can remove them for you. Please note that we only are able to do that *before* you have
received any real orders (i.e. taken the shop public). We won't charge any fees for test orders or test events.
How do I delete an event?
-------------------------
@@ -39,25 +51,3 @@ If you created a product and it doesn't show up, please follow the following ste
quota that is assigned to the series date that you access the shop for.
6. If the sale period has not started yet or is already over, check the "Show items outside presale period" setting of
your event.
How can I revert a check-in?
----------------------------
Neither our apps nor our web interface can currently undo the check-in of a tickets. We know that this is
inconvenient for some of you, but we have a good reason for it:
Our Desktop and Android apps both support an asynchronous mode in which they can scan tickets while staying
independent of their internet connection. When scanning with multiple devices, it can of course happen that two
devices scan the same ticket without knowing of the other scan. As soon as one of the devices regains connectivity, it
will upload its activity and the server marks the ticket as checked in -- regardless of the order in which the two
scans were made and uploaded (which could be two different orders).
If we'd provide a "check out" feature, it would not only be used to fix an accidental scan, but scan at entry and
exit to count the current number of people inside etc. In this case, the order of operations matters very much for them
to make sense and provide useful results. This makes implementing an asynchronous mode much more complicated.
In this trade off, we chose offline-capabilities over the check out feature. We plan on solving this problem in the
future, but we're not there yet.
If you're just *testing* the check-in capabilities and want to clean out everything for the real process, you can just
delete and re-create the check-in list.

View File

@@ -10,7 +10,6 @@ wanting to use pretix to sell tickets.
organizers/index
events/create
events/settings
events/structureguide
events/widget
faq
markdown

View File

@@ -1,5 +1,3 @@
.. _user-teams:
Teams
=====

View File

@@ -3,13 +3,6 @@
PayPal
======
.. note::
If you use pretix Hosted, you do not longer need to go through this tedious process! You can
just open the PayPal settings in the payment section of your event, click "Connect to PayPal"
and log in to your PayPal account. The following guide is only required for self-hosted
versions of pretix.
To integrate PayPal with pretix, you first need to have an active PayPal merchant account. If you do not already have a
PayPal account, you can create one on `paypal.com`_.
If you look into pretix' settings, you are required to fill in two keys:

View File

@@ -1,7 +1,7 @@
General settings
================
At "Settings" → "Payment", you can configure every aspect related to the payments you want to accept. The upper part
At "Settings" → "Pages", you can configure every aspect related to the payments you want to accept. The upper part
of the page shows a number of general settings that affect all payment methods:
.. thumbnail:: ../../screens/event/settings_payment.png

View File

@@ -3,10 +3,6 @@
Stripe
======
.. note:: If you use the Hosted version of pretix at pretix.eu, you do not need to copy API keys and create webhooks
any more. Instead, you can just click "Connect with Stripe" in pretix and everything will connect
automatically.
To integrate Stripe with pretix, you first need to have an active Stripe merchant account. If you do not already have a
Stripe account, you can create one on `stripe.com`_. Then, click on "API" in the left navigation of the Stripe
Dashboard. As you can see in the following screenshot, you will be presented with two sets of API keys, one for test

View File

@@ -1,6 +0,0 @@
build:
image: latest
python:
version: 3.6

12
src/.coveragerc Normal file
View File

@@ -0,0 +1,12 @@
[run]
source = pretix
omit = */migrations/*,*/urls.py,*/tests/*,*/testdummy/*,*/admin.py,pretix/wsgi.py,pretix/settings.py
[report]
exclude_lines =
pragma: no cover
def __str__
der __repr__
if settings.DEBUG
NOQA
NotImplementedError

View File

@@ -1,38 +0,0 @@
#!/bin/sh
COMPONENTS="pretix/pretix pretix/pretix-js"
DIR=pretix/locale
# Renerates .po files used for translating the plugin
set -e
set -x
# Lock Weblate
for c in $COMPONENTS; do
wlc lock $c;
done
# Push changes from Weblate to GitHub
for c in $COMPONENTS; do
wlc commit $c;
done
# Pull changes from GitHub
git pull --rebase
# Update po files itself
make localegen
# Commit changes
git add $DIR/*/*/*.po
git add $DIR/*.pot
git commit -s -m "Update po files
[CI skip]"
# Push changes
git push
# Unlock Weblate
for c in $COMPONENTS; do
wlc unlock $c;
wlc pull $c;
done

Some files were not shown because too many files have changed in this diff Show More