commit 5bd64741d98a03f96a4aa7df79ae9d4b68195300
Author: RunasSudo
Date: Fri Sep 4 17:56:10 2020 +1000
Per Pretix 3.10
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8d35cb3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+*.pyc
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..8dc880f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,32 @@
+Copyright 2014-2016 Raphael Michel
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+This project includes the work of others, namely:
+
+* Django, (c) Django Software Foundation and contributors, BSD License
+* Font Awesome, (c) Dave Gandy, SIL Open Font License and MIT License
+* Bootstrap, (c) Twitter, Inc., MIT License
+* jQuery, (c) jQuery Foundation and contributors, MIT License
+* django-formset-js, (c) Ionata Web Solutions, BSD License
+* CleanerVersion, (c) Jean-Christophe Zulian, Brian King, Andrea Marcacci, Manuel Jeckelmann, Apache License
+* django-bootstrap3, (c) Dylan Verheul, Apache License
+* pytz, (c) Stuart Bishop, MIT License
+* python-dateutil, (c) Yaron de Leeuw, BSD License
+* startbootstrap-sb-admin-2, (c) Iron Summit Media Strategies, LLC, Apache License
+* metismenu, (c) Osman Nuri Okumus, MIT License
+* easy-thumbnails, (c) Chris Beaven and contributors
+* reportlab, (c) ReportLab Europe Ltd, BSD License
+* django-compressor, (c) Jannis Leidel and contributors, MIT License
+* static3, (c) Roman Mohr and contributors, LGPL License
+* Lightbox, (c) Lokesh Dhakar, MIT License
diff --git a/stripe/__init__.py b/stripe/__init__.py
new file mode 100644
index 0000000..ac7218a
--- /dev/null
+++ b/stripe/__init__.py
@@ -0,0 +1,33 @@
+from django.apps import AppConfig
+from django.utils.functional import cached_property
+from django.utils.translation import gettext_lazy as _
+
+from pretix import __version__ as version
+
+
+class StripeApp(AppConfig):
+ name = 'pretix.plugins.stripe'
+ verbose_name = _("Stripe")
+
+ class PretixPluginMeta:
+ name = _("Stripe")
+ author = _("the pretix team")
+ version = version
+ category = 'PAYMENT'
+ description = _("This plugin allows you to receive credit card payments " +
+ "via Stripe")
+
+ def ready(self):
+ from . import signals, tasks # NOQA
+
+ @cached_property
+ def compatibility_errors(self):
+ errs = []
+ try:
+ import stripe # NOQA
+ except ImportError:
+ errs.append("Python package 'stripe' is not installed.")
+ return errs
+
+
+default_app_config = 'pretix.plugins.stripe.StripeApp'
diff --git a/stripe/forms.py b/stripe/forms.py
new file mode 100644
index 0000000..e94db52
--- /dev/null
+++ b/stripe/forms.py
@@ -0,0 +1,40 @@
+from django import forms
+from django.utils.translation import gettext_lazy as _
+
+from pretix.base.forms import SettingsForm
+
+
+class StripeKeyValidator:
+ def __init__(self, prefix):
+ assert len(prefix) > 0
+ if isinstance(prefix, list):
+ self._prefixes = prefix
+ else:
+ self._prefixes = [prefix]
+ assert isinstance(prefix, str)
+
+ def __call__(self, value):
+ if not any(value.startswith(p) for p in self._prefixes):
+ raise forms.ValidationError(
+ _('The provided key "%(value)s" does not look valid. It should start with "%(prefix)s".'),
+ code='invalid-stripe-key',
+ params={
+ 'value': value,
+ 'prefix': self._prefixes[0],
+ },
+ )
+
+
+class OrganizerStripeSettingsForm(SettingsForm):
+ payment_stripe_connect_app_fee_percent = forms.DecimalField(
+ label=_('Stripe Connect: App fee (percent)'),
+ required=False,
+ )
+ payment_stripe_connect_app_fee_max = forms.DecimalField(
+ label=_('Stripe Connect: App fee (max)'),
+ required=False,
+ )
+ payment_stripe_connect_app_fee_min = forms.DecimalField(
+ label=_('Stripe Connect: App fee (min)'),
+ required=False,
+ )
diff --git a/stripe/management/__init__.py b/stripe/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/stripe/management/commands/__init__.py b/stripe/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/stripe/management/commands/stripe_connect_fill_countries.py b/stripe/management/commands/stripe_connect_fill_countries.py
new file mode 100644
index 0000000..c60b554
--- /dev/null
+++ b/stripe/management/commands/stripe_connect_fill_countries.py
@@ -0,0 +1,35 @@
+import stripe
+from django.core.management.base import BaseCommand
+from django_scopes import scopes_disabled
+
+from pretix.base.models import Event
+from pretix.base.settings import GlobalSettingsObject
+
+
+class Command(BaseCommand):
+ help = "Detect country for Stripe Connect accounts connected with pretix 2.0 (required for payment request buttons)"
+
+ @scopes_disabled()
+ def handle(self, *args, **options):
+ cache = {}
+ gs = GlobalSettingsObject()
+ api_key = gs.settings.payment_stripe_connect_secret_key or gs.settings.payment_stripe_connect_test_secret_key
+ if not api_key:
+ self.stderr.write(self.style.ERROR("Stripe Connect is not set up!"))
+ return
+
+ for e in Event.objects.filter(plugins__icontains="pretix.plugins.stripe"):
+ uid = e.settings.payment_stripe_connect_user_id
+ if uid and not e.settings.payment_stripe_merchant_country:
+ if uid in cache:
+ e.settings.payment_stripe_merchant_country = cache[uid]
+ else:
+ try:
+ account = stripe.Account.retrieve(
+ uid,
+ api_key=api_key
+ )
+ except Exception as e:
+ print(e)
+ else:
+ e.settings.payment_stripe_merchant_country = cache[uid] = account.get('country')
diff --git a/stripe/migrations/0001_initial.py b/stripe/migrations/0001_initial.py
new file mode 100644
index 0000000..b3dcdc6
--- /dev/null
+++ b/stripe/migrations/0001_initial.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.3 on 2017-07-23 09:37
+from __future__ import unicode_literals
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('pretixbase', '0070_auto_20170719_0910'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ReferencedStripeObject',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('reference', models.CharField(db_index=True, max_length=190, unique=True)),
+ ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Order')),
+ ],
+ ),
+ ]
diff --git a/stripe/migrations/0002_referencedstripeobject_payment.py b/stripe/migrations/0002_referencedstripeobject_payment.py
new file mode 100644
index 0000000..049d056
--- /dev/null
+++ b/stripe/migrations/0002_referencedstripeobject_payment.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.13 on 2018-07-22 08:01
+from __future__ import unicode_literals
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pretixbase', '0096_auto_20180722_0801'),
+ ('stripe', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='referencedstripeobject',
+ name='payment',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.OrderPayment'),
+ ),
+ ]
diff --git a/stripe/migrations/0003_registeredapplepaydomain.py b/stripe/migrations/0003_registeredapplepaydomain.py
new file mode 100644
index 0000000..c64fc01
--- /dev/null
+++ b/stripe/migrations/0003_registeredapplepaydomain.py
@@ -0,0 +1,21 @@
+# Generated by Django 2.1 on 2018-08-12 14:51
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('stripe', '0002_referencedstripeobject_payment'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='RegisteredApplePayDomain',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('domain', models.CharField(max_length=190)),
+ ('account', models.CharField(max_length=190)),
+ ],
+ ),
+ ]
diff --git a/stripe/migrations/__init__.py b/stripe/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/stripe/models.py b/stripe/models.py
new file mode 100644
index 0000000..fea980b
--- /dev/null
+++ b/stripe/models.py
@@ -0,0 +1,12 @@
+from django.db import models
+
+
+class ReferencedStripeObject(models.Model):
+ reference = models.CharField(max_length=190, db_index=True, unique=True)
+ order = models.ForeignKey('pretixbase.Order', on_delete=models.CASCADE)
+ payment = models.ForeignKey('pretixbase.OrderPayment', null=True, blank=True, on_delete=models.CASCADE)
+
+
+class RegisteredApplePayDomain(models.Model):
+ domain = models.CharField(max_length=190)
+ account = models.CharField(max_length=190)
diff --git a/stripe/payment.py b/stripe/payment.py
new file mode 100644
index 0000000..c2616d0
--- /dev/null
+++ b/stripe/payment.py
@@ -0,0 +1,1369 @@
+import hashlib
+import json
+import logging
+import re
+import urllib.parse
+from collections import OrderedDict
+from decimal import Decimal
+
+import stripe
+from django import forms
+from django.conf import settings
+from django.contrib import messages
+from django.core import signing
+from django.db import transaction
+from django.http import HttpRequest
+from django.template.loader import get_template
+from django.urls import reverse
+from django.utils.crypto import get_random_string
+from django.utils.safestring import mark_safe
+from django.utils.timezone import now
+from django.utils.translation import gettext, gettext_lazy as _, pgettext
+from django_countries import countries
+
+from pretix import __version__
+from pretix.base.decimal import round_decimal
+from pretix.base.models import Event, OrderPayment, OrderRefund, Quota
+from pretix.base.payment import BasePaymentProvider, PaymentException
+from pretix.base.plugins import get_all_plugins
+from pretix.base.services.mail import SendMailException
+from pretix.base.settings import SettingsSandbox
+from pretix.helpers.urls import build_absolute_uri as build_global_uri
+from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
+from pretix.plugins.stripe.forms import StripeKeyValidator
+from pretix.plugins.stripe.models import (
+ ReferencedStripeObject, RegisteredApplePayDomain,
+)
+from pretix.plugins.stripe.tasks import (
+ get_stripe_account_key, stripe_verify_domain,
+)
+
+logger = logging.getLogger('pretix.plugins.stripe')
+
+
+class StripeSettingsHolder(BasePaymentProvider):
+ identifier = 'stripe_settings'
+ verbose_name = _('Stripe')
+ is_enabled = False
+ is_meta = True
+
+ def __init__(self, event: Event):
+ super().__init__(event)
+ self.settings = SettingsSandbox('payment', 'stripe', event)
+
+ def get_connect_url(self, request):
+ request.session['payment_stripe_oauth_event'] = request.event.pk
+ if 'payment_stripe_oauth_token' not in request.session:
+ request.session['payment_stripe_oauth_token'] = get_random_string(32)
+ return (
+ "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={}&state={}"
+ "&scope=read_write&redirect_uri={}"
+ ).format(
+ self.settings.connect_client_id,
+ request.session['payment_stripe_oauth_token'],
+ urllib.parse.quote(build_global_uri('plugins:stripe:oauth.return')),
+ )
+
+ def settings_content_render(self, request):
+ if self.settings.connect_client_id and not self.settings.secret_key:
+ # Use Stripe connect
+ if not self.settings.connect_user_id:
+ return (
+ "
{}
"
+ "{}"
+ ).format(
+ _('To accept payments via Stripe, you will need an account at Stripe. By clicking on the '
+ 'following button, you can either create a new Stripe account connect pretix to an existing '
+ 'one.'),
+ self.get_connect_url(request),
+ _('Connect with Stripe')
+ )
+ else:
+ return (
+ ""
+ ).format(
+ reverse('plugins:stripe:oauth.disconnect', kwargs={
+ 'organizer': self.event.organizer.slug,
+ 'event': self.event.slug,
+ }),
+ _('Disconnect from Stripe')
+ )
+ else:
+ return "
%s %s
" % (
+ _('Please configure a Stripe Webhook to '
+ 'the following endpoint in order to automatically cancel orders when charges are refunded externally '
+ 'and to process asynchronous payment methods like SOFORT.'),
+ build_global_uri('plugins:stripe:webhook')
+ )
+
+ @property
+ def settings_form_fields(self):
+ if 'pretix_resellers' in [p.module for p in get_all_plugins()]:
+ moto_settings = [
+ ('reseller_moto',
+ forms.BooleanField(
+ label=_('Enable MOTO payments for resellers'),
+ help_text=(
+ _('Gated feature (needs to be enabled for your account by Stripe support first)') +
+ '
%s
' % _(
+ 'We can flag the credit card transaction you make through the reseller interface as MOTO '
+ '(Mail Order / Telephone Order), which will exempt them from Strong Customer '
+ 'Authentication (SCA) requirements. However: By enabling this feature, you will need to '
+ 'fill out yearly PCI-DSS self-assessment forms like the 40 page SAQ D. Please consult the '
+ '%s for further information on this subject.' %
+ '{}'.format(
+ _('Stripe Integration security guide')
+ )
+ )
+ ),
+ required=False,
+ ))
+ ]
+ else:
+ moto_settings = []
+
+ if self.settings.connect_client_id and not self.settings.secret_key:
+ # Stripe connect
+ if self.settings.connect_user_id:
+ fields = [
+ ('connect_user_name',
+ forms.CharField(
+ label=_('Stripe account'),
+ disabled=True
+ )),
+ ('endpoint',
+ forms.ChoiceField(
+ label=_('Endpoint'),
+ initial='live',
+ choices=(
+ ('live', pgettext('stripe', 'Live')),
+ ('test', pgettext('stripe', 'Testing')),
+ ),
+ help_text=_('If your event is in test mode, we will always use Stripe\'s test API, '
+ 'regardless of this setting.')
+ )),
+ ]
+ else:
+ return {}
+ else:
+ allcountries = list(countries)
+ allcountries.insert(0, ('', _('Select country')))
+
+ fields = [
+ ('publishable_key',
+ forms.CharField(
+ label=_('Publishable key'),
+ help_text=_('{text}').format(
+ text=_('Click here for a tutorial on how to obtain the required keys'),
+ docs_url='https://docs.pretix.eu/en/latest/user/payments/stripe.html'
+ ),
+ validators=(
+ StripeKeyValidator('pk_'),
+ ),
+ )),
+ ('secret_key',
+ forms.CharField(
+ label=_('Secret key'),
+ validators=(
+ StripeKeyValidator(['sk_', 'rk_']),
+ ),
+ )),
+ ('merchant_country',
+ forms.ChoiceField(
+ choices=allcountries,
+ label=_('Merchant country'),
+ help_text=_('The country in which your Stripe-account is registered in. Usually, this is your '
+ 'country of residence.'),
+ )),
+ ]
+ d = OrderedDict(
+ fields + [
+ ('method_cc',
+ forms.BooleanField(
+ label=_('Credit card payments'),
+ required=False,
+ )),
+ ('method_giropay',
+ forms.BooleanField(
+ label=_('giropay'),
+ disabled=self.event.currency != 'EUR',
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
+ ('method_ideal',
+ forms.BooleanField(
+ label=_('iDEAL'),
+ disabled=self.event.currency != 'EUR',
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
+ ('method_alipay',
+ forms.BooleanField(
+ label=_('Alipay'),
+ disabled=self.event.currency not in ('EUR', 'AUD', 'CAD', 'GBP', 'HKD', 'JPY', 'NZD', 'SGD', 'USD'),
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
+ ('method_bancontact',
+ forms.BooleanField(
+ label=_('Bancontact'),
+ disabled=self.event.currency != 'EUR',
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
+ ('method_sofort',
+ forms.BooleanField(
+ label=_('SOFORT'),
+ disabled=self.event.currency != 'EUR',
+ help_text=(
+ _('Needs to be enabled in your Stripe account first.') +
+ '
+ {% url "control:event.order" organizer=event.organizer.slug event=event.slug code=data.order as ourl %}
+ {% blocktrans trimmed with charge=data.charge stripe_href="href='https://dashboard.stripe.com/payments/"|add:data.charge|add:"' target='_blank'"|safe order=""|add:data.order|add:""|safe %}
+ The Stripe transaction {{ charge }} has succeeded, but the order {{ order }} has
+ already been paid by other means. Please double-check and refund the money via Stripe's interface.
+ {% endblocktrans %}
+
+ {% url "control:event.order" organizer=event.organizer.slug event=event.slug code=data.order as ourl %}
+ {% blocktrans trimmed with charge=data.charge stripe_href="href='https://dashboard.stripe.com/payments/"|add:data.charge|add:"' target='_blank'"|safe order=""|add:data.order|add:""|safe %}
+ The Stripe transaction {{ charge }} has succeeded, but the order {{ order }} is
+ expired and the product was sold out in the meantime. Therefore, the payment could not be accepted. Please
+ contact the user and refund the money via Stripe's interface.
+ {% endblocktrans %}
+
+ {% url "control:event.order" organizer=event.organizer.slug event=event.slug code=data.order as ourl %}
+ {% blocktrans trimmed with charge=data.charge stripe_href="href='https://dashboard.stripe.com/payments/"|add:data.charge|add:"' target='_blank'"|safe order=""|add:data.order|add:""|safe %}
+ Stripe reported that the transaction {{ charge }} has been refunded.
+ Do you want to refund mark the matching order ({{ order }}) as refunded?
+ {% endblocktrans %}
+
diff --git a/stripe/templates/pretixplugins/stripe/apple-developer-merchantid-domain-association b/stripe/templates/pretixplugins/stripe/apple-developer-merchantid-domain-association
new file mode 100644
index 0000000..2ff95c9
--- /dev/null
+++ b/stripe/templates/pretixplugins/stripe/apple-developer-merchantid-domain-association
@@ -0,0 +1 @@
+7B227073704964223A2239373943394538343346343131343044463144313834343232393232313734313034353044314339464446394437384337313531303944334643463542433731222C2276657273696F6E223A312C22637265617465644F6E223A313536363233343735303036312C227369676E6174757265223A22333038303036303932613836343838366637306430313037303261303830333038303032303130313331306633303064303630393630383634383031363530333034303230313035303033303830303630393261383634383836663730643031303730313030303061303830333038323033653333303832303338386130303330323031303230323038346333303431343935313964353433363330306130363038326138363438636533643034303330323330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313339333033353331333833303331333333323335333735613137306433323334333033353331333633303331333333323335333735613330356633313235333032333036303335353034303330633163363536333633326437333664373032643632373236663662363537323264373336393637366535663535343333343264353035323466343433313134333031323036303335353034306230633062363934663533323035333739373337343635366437333331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034633231353737656465626436633762323231386636386464373039306131323138646337623062643666326332383364383436303935643934616634613534313162383334323065643831316633343037653833333331663163353463336637656233323230643662616435643465666634393238393839336537633066313361333832303231313330383230323064333030633036303335353164313330313031666630343032333030303330316630363033353531643233303431383330313638303134323366323439633434663933653465663237653663346636323836633366613262626664326534623330343530363038326230363031303530353037303130313034333933303337333033353036303832623036303130353035303733303031383632393638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635363136393633363133333330333233303832303131643036303335353164323030343832303131343330383230313130333038323031306330363039326138363438383666373633363430353031333038316665333038316333303630383262303630313035303530373032303233303831623630633831623335323635366336393631366536333635323036663665323037343638363937333230363336353732373436393636363936333631373436353230363237393230363136653739323037303631373237343739323036313733373337353664363537333230363136333633363537303734363136653633363532303666363632303734363836353230373436383635366532303631373037303663363936333631363236633635323037333734363136653634363137323634323037343635373236643733323036313665363432303633366636653634363937343639366636653733323036663636323037353733363532633230363336353732373436393636363936333631373436353230373036663663363936333739323036313665363432303633363537323734363936363639363336313734363936663665323037303732363136333734363936333635323037333734363137343635366436353665373437333265333033363036303832623036303130353035303730323031313632613638373437343730336132663266373737373737326536313730373036633635326536333666366432663633363537323734363936363639363336313734363536313735373436383666373236393734373932663330333430363033353531643166303432643330326233303239613032376130323538363233363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353631363936333631333332653633373236633330316430363033353531643065303431363034313439343537646236666435373438313836383938393736326637653537383530376537396235383234333030653036303335353164306630313031666630343034303330323037383033303066303630393261383634383836663736333634303631643034303230353030333030613036303832613836343863653364303430333032303334393030333034363032323130306265303935373166653731653165373335623535653561666163623463373266656234343566333031383532323263373235313030326236316562643666353530323231303064313862333530613564643664643665623137343630333562313165623263653837636661336536616636636264383338303839306463383263646461613633333038323032656533303832303237356130303330323031303230323038343936643266626633613938646139373330306130363038326138363438636533643034303330323330363733313162333031393036303335353034303330633132343137303730366336353230353236663666373432303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133343330333533303336333233333334333633333330356131373064333233393330333533303336333233333334333633333330356133303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346630313731313834313964373634383564353161356532353831303737366538383061326566646537626165346465303864666334623933653133333536643536363562333561653232643039373736306432323465376262613038666437363137636538386362373662623636373062656338653832393834666635343435613338316637333038316634333034363036303832623036303130353035303730313031303433613330333833303336303630383262303630313035303530373330303138363261363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363537323666366637343633363136373333333031643036303335353164306530343136303431343233663234396334346639336534656632376536633466363238366333666132626266643265346233303066303630333535316431333031303166663034303533303033303130316666333031663036303335353164323330343138333031363830313462626230646561313538333338383961613438613939646562656264656261666461636232346162333033373036303335353164316630343330333032653330326361303261613032383836323636383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635373236663666373436333631363733333265363337323663333030653036303335353164306630313031666630343034303330323031303633303130303630613261383634383836663736333634303630323065303430323035303033303061303630383261383634386365336430343033303230333637303033303634303233303361636637323833353131363939623138366662333563333536636136326266663431376564643930663735346461323865626566313963383135653432623738396638393866373962353939663938643534313064386639646539633266653032333033323264643534343231623061333035373736633564663333383362393036376664313737633263323136643936346663363732363938323132366635346638376137643162393963623962303938393231363130363939306630393932316430303030333138323031386233303832303138373032303130313330383138363330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533303230383463333034313439353139643534333633303064303630393630383634383031363530333034303230313035303061303831393533303138303630393261383634383836663730643031303930333331306230363039326138363438383666373064303130373031333031633036303932613836343838366637306430313039303533313066313730643331333933303338333133393331333733313332333333303561333032613036303932613836343838366637306430313039333433313164333031623330306430363039363038363438303136353033303430323031303530306131306130363038326138363438636533643034303330323330326630363039326138363438383666373064303130393034333132323034323062303731303365313430613462386231376262613230316130336163643036396234653431366232613263383066383661383338313435633239373566633131333030613036303832613836343863653364303430333032303434363330343430323230343639306264636637626461663833636466343934396534633035313039656463663334373665303564373261313264376335666538633033303033343464663032323032363764353863393365626233353031333836363062353730373938613064643731313734316262353864626436613138363633353038353431656565393035303030303030303030303030227D
\ No newline at end of file
diff --git a/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html b/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html
new file mode 100644
index 0000000..97f0e12
--- /dev/null
+++ b/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html
@@ -0,0 +1,29 @@
+{% load i18n %}
+
+{% if provider.method == "cc" %}
+
{% blocktrans trimmed %}
+ The total amount will be withdrawn from your credit card.
+ {% endblocktrans %}
{% blocktrans trimmed %}
+ After you submitted your order, we will redirect you to the payment service provider to complete your payment.
+ You will then be redirected back here to get your tickets.
+ {% endblocktrans %}
+ {% blocktrans trimmed %}
+ Your payment will be processed by Stripe, Inc. Your credit card data will be transmitted directly to
+ Stripe and never touches our servers.
+ {% endblocktrans %}
+
+
+
+
+
+
{% blocktrans trimmed %}
+ After you submitted your order, we will redirect you to the payment service provider to complete your payment.
+ You will then be redirected back here to get your tickets.
+{% endblocktrans %}
{% blocktrans trimmed %}
+ After you submitted your order, we will redirect you to the payment service provider to complete your payment.
+ You will then be redirected back here to get your tickets.
+{% endblocktrans %}
diff --git a/stripe/templates/pretixplugins/stripe/control.html b/stripe/templates/pretixplugins/stripe/control.html
new file mode 100644
index 0000000..0711c73
--- /dev/null
+++ b/stripe/templates/pretixplugins/stripe/control.html
@@ -0,0 +1,69 @@
+{% load i18n %}
+
+{% if payment_info %}
+
+ {% if "id" in payment_info %}
+
{% trans "Charge ID" %}
+
{{ payment_info.id }}
+ {% endif %}
+ {% if "source" in payment_info %}
+ {% if payment_info.source.card %}
+
{% blocktrans trimmed %}
+ We're waiting for an answer from the payment provider regarding your payment. Please contact us if this
+ takes more than a few days.
+ {% endblocktrans %}
{% blocktrans trimmed %}
+ Please scan the barcode below to complete your WeChat payment.
+ Once you have completed your payment, you can refresh this page.
+ {% endblocktrans %}
+
+
+
+{% else %}
+
{% blocktrans trimmed %}
+ The payment transaction could not be completed for the following reason:
+ {% endblocktrans %}
+
+ {% if payment_info and payment_info.error %}
+ {{ payment_info.message }}
+ {% else %}
+ {% trans "Unknown reason" %}
+ {% endif %}
+