diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py
index 3dd5e3d4ad..dd9fa1e424 100644
--- a/src/pretix/base/settings.py
+++ b/src/pretix/base/settings.py
@@ -463,6 +463,9 @@ class SettingsSandbox:
self._type = typestr
self._key = key
+ def get_prefix(self):
+ return '%s_%s_' % (self._type, self._key)
+
def _convert_key(self, key: str) -> str:
return '%s_%s_%s' % (self._type, self._key, key)
diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py
index 0ca1b8a345..71b04b989b 100644
--- a/src/pretix/control/views/event.py
+++ b/src/pretix/control/views/event.py
@@ -186,12 +186,12 @@ class PaymentSettings(EventPermissionRequiredMixin, TemplateView, SingleObjectMi
for provider in self.request.event.get_payment_providers().values():
provider.form = ProviderForm(
obj=self.request.event,
- settingspref='payment_%s_' % provider.identifier,
+ settingspref=provider.settings.get_prefix(),
data=(self.request.POST if self.request.method == 'POST' else None)
)
provider.form.fields = OrderedDict(
[
- ('payment_%s_%s' % (provider.identifier, k), v)
+ ('%s%s' % (provider.settings.get_prefix(), k), v)
for k, v in provider.settings_form_fields.items()
]
)
diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py
index 8e2425f268..55a625d95f 100644
--- a/src/pretix/plugins/stripe/payment.py
+++ b/src/pretix/plugins/stripe/payment.py
@@ -7,20 +7,34 @@ import stripe
from django import forms
from django.contrib import messages
from django.template.loader import get_template
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext, ugettext_lazy as _
-from pretix.base.models import Quota, RequiredAction
+from pretix.base.models import Event, Quota, RequiredAction
from pretix.base.payment import BasePaymentProvider, PaymentException
from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
+from pretix.base.settings import SettingsSandbox
from pretix.multidomain.urlreverse import build_absolute_uri
logger = logging.getLogger('pretix.plugins.stripe')
-class Stripe(BasePaymentProvider):
- identifier = 'stripe'
- verbose_name = _('Credit Card via Stripe')
+class StripeSettingsHolder(BasePaymentProvider):
+ identifier = 'stripe_settings'
+ verbose_name = _('Stripe')
+ is_enabled = False
+
+ def __init__(self, event: Event):
+ super().__init__(event)
+ self.settings = SettingsSandbox('payment', 'stripe', event)
+
+ def settings_content_render(self, request):
+ 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_absolute_uri(self.event, 'plugins:stripe:webhook')
+ )
@property
def settings_form_fields(self):
@@ -44,56 +58,87 @@ class Stripe(BasePaymentProvider):
choices=(
('pretix', _('Simple (pretix design)')),
('checkout', _('Stripe Checkout')),
- )
- ))
+ ),
+ help_text=_('Only relevant for credit card payments.')
+ )),
+ ('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. Note that, despite the name, '
+ 'payments are not immediately confirmed but might take some time.'),
+ required=False,
+ )),
]
)
- def settings_content_render(self, request):
- return "%s
%s
" % (
- _('Please configure a Stripe Webhook to '
- 'the following endpoint in order to automatically cancel orders when charges are refunded externally.'),
- build_absolute_uri(self.event, 'plugins:stripe:webhook')
- )
- def payment_is_valid_session(self, request):
- return request.session.get('payment_stripe_token', '') != ''
+class StripeMethod(BasePaymentProvider):
+ identifier = ''
+ method = ''
+
+ def __init__(self, event: Event):
+ super().__init__(event)
+ self.settings = SettingsSandbox('payment', 'stripe', event)
+
+ @property
+ def settings_form_fields(self):
+ return {}
+
+ @property
+ def is_enabled(self) -> bool:
+ return self.settings.get('_enabled', as_type=bool) and self.settings.get('method_{}'.format(self.method),
+ as_type=bool)
def order_prepare(self, request, order):
return self.checkout_prepare(request, None)
- def checkout_prepare(self, request, cart):
- token = request.POST.get('stripe_token', '')
- request.session['payment_stripe_token'] = token
- request.session['payment_stripe_brand'] = request.POST.get('stripe_card_brand', '')
- request.session['payment_stripe_last4'] = request.POST.get('stripe_card_last4', '')
- if token == '':
- messages.error(request, _('You may need to enable JavaScript for Stripe payments.'))
- return False
- return True
-
- def payment_form_render(self, request) -> str:
- ui = self.settings.get('ui', default='pretix')
- if ui == 'checkout':
- template = get_template('pretixplugins/stripe/checkout_payment_form_stripe_checkout.html')
- else:
- template = get_template('pretixplugins/stripe/checkout_payment_form.html')
- ctx = {'request': request, 'event': self.event, 'settings': self.settings}
- return template.render(ctx)
-
def _init_api(self):
- stripe.api_version = '2015-04-07'
+ stripe.api_version = '2017-06-05'
stripe.api_key = self.settings.get('secret_key')
def checkout_confirm_render(self, request) -> str:
template = get_template('pretixplugins/stripe/checkout_payment_confirm.html')
- ctx = {'request': request, 'event': self.event, 'settings': self.settings}
+ ctx = {'request': request, 'event': self.event, 'settings': self.settings, 'provider': self}
return template.render(ctx)
def order_can_retry(self, order):
return self._is_still_available(order=order)
- def _charge_source(self, source, order):
+ def _charge_source(self, request, source, order):
try:
charge = stripe.Charge.create(
amount=int(order.total * 100),
@@ -139,7 +184,7 @@ class Stripe(BasePaymentProvider):
else:
if charge.status == 'succeeded' and charge.paid:
try:
- mark_order_paid(order, 'stripe', str(charge))
+ mark_order_paid(order, self.identifier, str(charge))
except Quota.QuotaExceededException as e:
RequiredAction.objects.create(
event=self.event, action_type='pretix.plugins.stripe.overpaid', data=json.dumps({
@@ -151,56 +196,32 @@ class Stripe(BasePaymentProvider):
except SendMailException:
raise PaymentException(_('There was an error sending the confirmation mail.'))
+ elif charge.status == 'pending':
+ messages.warning(request, _('Your payment is pending completion. We will inform you as soon as the '
+ 'payment completed.'))
+ order.payment_info = str(charge)
+ order.save(update_fields=['payment_info'])
+ return
else:
logger.info('Charge failed: %s' % str(charge))
order.payment_info = str(charge)
order.save(update_fields=['payment_info'])
raise PaymentException(_('Stripe reported an error: %s') % charge.failure_message)
- def payment_perform(self, request, order) -> str:
- self._init_api()
-
- if request.session['payment_stripe_token'].startswith('src_'):
- src = stripe.Source.retrieve(request.session['payment_stripe_token'])
- if src.type == 'card' and src.card and src.card.three_d_secure == 'required':
- request.session['payment_stripe_order_secret'] = order.secret
- source = stripe.Source.create(
- type='three_d_secure',
- amount=int(order.total * 100),
- currency=self.event.currency.lower(),
- three_d_secure={
- 'card': src.id
- },
- metadata={
- 'order': str(order.id),
- 'event': self.event.id,
- 'code': order.code
- },
- redirect={
- 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
- 'order': order.code,
- 'hash': hashlib.sha1(order.secret.lower().encode()).hexdigest(),
- })
- },
- )
- if source.status == "pending":
- order.payment_info = str(source)
- order.save(update_fields=['payment_info'])
- return source.redirect.url
-
- try:
- self._charge_source(request.session['payment_stripe_token'], order)
- finally:
- del request.session['payment_stripe_token']
-
def order_pending_render(self, request, order) -> str:
if order.payment_info:
payment_info = json.loads(order.payment_info)
else:
payment_info = None
template = get_template('pretixplugins/stripe/pending.html')
- ctx = {'request': request, 'event': self.event, 'settings': self.settings,
- 'order': order, 'payment_info': payment_info}
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'provider': self,
+ 'order': order,
+ 'payment_info': payment_info,
+ }
return template.render(ctx)
def order_control_render(self, request, order) -> str:
@@ -211,8 +232,15 @@ class Stripe(BasePaymentProvider):
else:
payment_info = None
template = get_template('pretixplugins/stripe/control.html')
- ctx = {'request': request, 'event': self.event, 'settings': self.settings,
- 'payment_info': payment_info, 'order': order}
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'payment_info': payment_info,
+ 'order': order,
+ 'method': self.method,
+ 'provider': self,
+ }
return template.render(ctx)
def order_control_refund_render(self, order) -> str:
@@ -255,3 +283,409 @@ class Stripe(BasePaymentProvider):
order = mark_order_refunded(order, user=request.user)
order.payment_info = str(ch)
order.save()
+
+ def payment_perform(self, request, order) -> str:
+ self._init_api()
+ try:
+ source = self._create_source(request, order)
+ except stripe.error.StripeError as e:
+ if e.json_body:
+ err = e.json_body['error']
+ logger.exception('Stripe error: %s' % str(err))
+ else:
+ err = {'message': str(e)}
+ logger.exception('Stripe error: %s' % str(e))
+ order.payment_info = json.dumps({
+ 'error': True,
+ 'message': err['message'],
+ })
+ order.save(update_fields=['payment_info'])
+ raise PaymentException(_('We had trouble communicating with Stripe. Please try again and get in touch '
+ 'with us if this problem persists.'))
+
+ order.payment_info = str(source)
+ order.save(update_fields=['payment_info'])
+ request.session['payment_stripe_order_secret'] = order.secret
+ return source.redirect.url
+
+
+class StripeCC(StripeMethod):
+ identifier = 'stripe'
+ verbose_name = _('Credit card via Stripe')
+ public_name = _('Credit card')
+ method = 'cc'
+
+ def payment_form_render(self, request) -> str:
+ ui = self.settings.get('ui', default='pretix')
+ if ui == 'checkout':
+ template = get_template('pretixplugins/stripe/checkout_payment_form_stripe_checkout.html')
+ else:
+ template = get_template('pretixplugins/stripe/checkout_payment_form.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ }
+ return template.render(ctx)
+
+ def payment_is_valid_session(self, request):
+ return request.session.get('payment_stripe_token', '') != ''
+
+ def checkout_prepare(self, request, cart):
+ token = request.POST.get('stripe_token', '')
+ request.session['payment_stripe_token'] = token
+ request.session['payment_stripe_brand'] = request.POST.get('stripe_card_brand', '')
+ request.session['payment_stripe_last4'] = request.POST.get('stripe_card_last4', '')
+ if token == '':
+ messages.error(request, _('You may need to enable JavaScript for Stripe payments.'))
+ return False
+ return True
+
+ def payment_perform(self, request, order) -> str:
+ self._init_api()
+
+ if request.session['payment_stripe_token'].startswith('src_'):
+ try:
+ src = stripe.Source.retrieve(request.session['payment_stripe_token'])
+ if src.type == 'card' and src.card and src.card.three_d_secure == 'required':
+ request.session['payment_stripe_order_secret'] = order.secret
+ source = stripe.Source.create(
+ type='three_d_secure',
+ amount=int(order.total * 100),
+ currency=self.event.currency.lower(),
+ three_d_secure={
+ 'card': src.id
+ },
+ metadata={
+ 'order': str(order.id),
+ 'event': self.event.id,
+ 'code': order.code
+ },
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': order.code,
+ 'hash': hashlib.sha1(order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ )
+ if source.status == "pending":
+ order.payment_info = str(source)
+ order.save(update_fields=['payment_info'])
+ return source.redirect.url
+ except stripe.error.StripeError as e:
+ if e.json_body:
+ err = e.json_body['error']
+ logger.exception('Stripe error: %s' % str(err))
+ else:
+ err = {'message': str(e)}
+ logger.exception('Stripe error: %s' % str(e))
+ order.payment_info = json.dumps({
+ 'error': True,
+ 'message': err['message'],
+ })
+ order.save(update_fields=['payment_info'])
+ raise PaymentException(_('We had trouble communicating with Stripe. Please try again and get in touch '
+ 'with us if this problem persists.'))
+
+ try:
+ self._charge_source(request, request.session['payment_stripe_token'], order)
+ finally:
+ del request.session['payment_stripe_token']
+
+
+class StripeGiropay(StripeMethod):
+ identifier = 'stripe_giropay'
+ verbose_name = _('giropay via Stripe')
+ public_name = _('giropay')
+ method = 'giropay'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_giropay.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'form': self.payment_form(request)
+ }
+ return template.render(ctx)
+
+ @property
+ def payment_form_fields(self):
+ return OrderedDict([
+ ('account', forms.CharField(label=_('Account holder'))),
+ ])
+
+ def _create_source(self, request, order):
+ try:
+ source = stripe.Source.create(
+ type='giropay',
+ amount=int(order.total * 100),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(order.id),
+ 'event': self.event.id,
+ 'code': order.code
+ },
+ owner={
+ 'name': request.session.get('payment_stripe_giropay_account') or ugettext('unknown name')
+ },
+ giropay={
+ 'statement_descriptor': ugettext('{event}-{code}').format(
+ event=self.event.slug.upper(),
+ code=order.code
+ )[:35]
+ },
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': order.code,
+ 'hash': hashlib.sha1(order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ )
+ return source
+ finally:
+ if 'payment_stripe_giropay_account' in request.session:
+ del request.session['payment_stripe_giropay_account']
+
+ def payment_is_valid_session(self, request):
+ return (
+ request.session.get('payment_stripe_giropay_account', '') != ''
+ )
+
+ def checkout_prepare(self, request, cart):
+ form = self.payment_form(request)
+ if form.is_valid():
+ request.session['payment_stripe_giropay_account'] = form.cleaned_data['account']
+ return True
+ return False
+
+
+class StripeIdeal(StripeMethod):
+ identifier = 'stripe_ideal'
+ verbose_name = _('iDEAL via Stripe')
+ public_name = _('iDEAL')
+ method = 'ideal'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ }
+ return template.render(ctx)
+
+ def _create_source(self, request, order):
+ source = stripe.Source.create(
+ type='ideal',
+ amount=int(order.total * 100),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(order.id),
+ 'event': self.event.id,
+ 'code': order.code
+ },
+ ideal={
+ 'statement_descriptor': ugettext('{event}-{code}').format(
+ event=self.event.slug.upper(),
+ code=order.code
+ )
+ },
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': order.code,
+ 'hash': hashlib.sha1(order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ )
+ return source
+
+ def payment_is_valid_session(self, request):
+ return True
+
+ def checkout_prepare(self, request, cart):
+ return True
+
+
+class StripeAlipay(StripeMethod):
+ identifier = 'stripe_alipay'
+ verbose_name = _('Alipay via Stripe')
+ public_name = _('Alipay')
+ method = 'alipay'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ }
+ return template.render(ctx)
+
+ def _create_source(self, request, order):
+ source = stripe.Source.create(
+ type='alipay',
+ amount=int(order.total * 100),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(order.id),
+ 'event': self.event.id,
+ 'code': order.code
+ },
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': order.code,
+ 'hash': hashlib.sha1(order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ )
+ return source
+
+ def payment_is_valid_session(self, request):
+ return True
+
+ def checkout_prepare(self, request, cart):
+ return True
+
+
+class StripeBancontact(StripeMethod):
+ identifier = 'stripe_bancontact'
+ verbose_name = _('Bancontact via Stripe')
+ public_name = _('Bancontact')
+ method = 'bancontact'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_bancontact.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'form': self.payment_form(request)
+ }
+ return template.render(ctx)
+
+ @property
+ def payment_form_fields(self):
+ return OrderedDict([
+ ('account', forms.CharField(label=_('Account holder'), min_length=3)),
+ ])
+
+ def _create_source(self, request, order):
+ try:
+ source = stripe.Source.create(
+ type='bancontact',
+ amount=int(order.total * 100),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(order.id),
+ 'event': self.event.id,
+ 'code': order.code
+ },
+ owner={
+ 'name': request.session.get('payment_stripe_bancontact_account') or ugettext('unknown name')
+ },
+ bancontact={
+ 'statement_descriptor': ugettext('{event}-{code}').format(
+ event=self.event.slug.upper(),
+ code=order.code
+ )[:35]
+ },
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': order.code,
+ 'hash': hashlib.sha1(order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ )
+ return source
+ finally:
+ if 'payment_stripe_bancontact_account' in request.session:
+ del request.session['payment_stripe_bancontact_account']
+
+ def payment_is_valid_session(self, request):
+ return (
+ request.session.get('payment_stripe_bancontact_account', '') != ''
+ )
+
+ def checkout_prepare(self, request, cart):
+ form = self.payment_form(request)
+ if form.is_valid():
+ request.session['payment_stripe_bancontact_account'] = form.cleaned_data['account']
+ return True
+ return False
+
+
+class StripeSofort(StripeMethod):
+ identifier = 'stripe_sofort'
+ verbose_name = _('SOFORT via Stripe')
+ public_name = _('SOFORT')
+ method = 'sofort'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_sofort.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'form': self.payment_form(request)
+ }
+ return template.render(ctx)
+
+ @property
+ def payment_form_fields(self):
+ return OrderedDict([
+ ('bank_country', forms.ChoiceField(label=_('Country of your bank'), choices=(
+ ('de', _('Germany')),
+ ('at', _('Austria')),
+ ('be', _('Belgium')),
+ ('nl', _('Netherlands')),
+ ('es', _('Spain'))
+ ))),
+ ])
+
+ def _create_source(self, request, order):
+ source = stripe.Source.create(
+ type='sofort',
+ amount=int(order.total * 100),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(order.id),
+ 'event': self.event.id,
+ 'code': order.code
+ },
+ sofort={
+ 'country': request.session.get('payment_stripe_sofort_bank_country'),
+ 'statement_descriptor': ugettext('{event}-{code}').format(
+ event=self.event.slug.upper(),
+ code=order.code
+ )[:35]
+ },
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': order.code,
+ 'hash': hashlib.sha1(order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ )
+ return source
+
+ def payment_is_valid_session(self, request):
+ return (
+ request.session.get('payment_stripe_sofort_bank_country', '') != ''
+ )
+
+ def checkout_prepare(self, request, cart):
+ form = self.payment_form(request)
+ if form.is_valid():
+ request.session['payment_stripe_sofort_bank_country'] = form.cleaned_data['bank_country']
+ return True
+ return False
+
+ def order_can_retry(self, order):
+ try:
+ d = json.loads(order.payment_info)
+ except ValueError:
+ return self._is_still_available(order=order)
+ return not (
+ d.get('object') == 'charge' and d.get('status') == 'pending'
+ )
diff --git a/src/pretix/plugins/stripe/signals.py b/src/pretix/plugins/stripe/signals.py
index 3540c55eec..5d7951a187 100644
--- a/src/pretix/plugins/stripe/signals.py
+++ b/src/pretix/plugins/stripe/signals.py
@@ -5,6 +5,7 @@ from django.dispatch import receiver
from django.template.loader import get_template
from django.utils.translation import ugettext_lazy as _
+from pretix.base.settings import settings_hierarkey
from pretix.base.signals import (
logentry_display, register_payment_providers, requiredaction_display,
)
@@ -13,18 +14,21 @@ from pretix.presale.signals import html_head
@receiver(register_payment_providers, dispatch_uid="payment_stripe")
def register_payment_provider(sender, **kwargs):
- from .payment import Stripe
+ from .payment import (
+ StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact,
+ StripeSofort
+ )
- return Stripe
+ return [StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact, StripeSofort]
@receiver(html_head, dispatch_uid="payment_stripe_html_head")
def html_head_presale(sender, request=None, **kwargs):
- from .payment import Stripe
+ from .payment import StripeSettingsHolder
- provider = Stripe(sender)
+ provider = StripeSettingsHolder(sender)
url = resolve(request.path_info)
- if provider.is_enabled and ("checkout" in url.url_name or "order.pay" in url.url_name):
+ if provider.settings.get('_enabled', as_type=bool) and ("checkout" in url.url_name or "order.pay" in url.url_name):
template = get_template('pretixplugins/stripe/presale_head.html')
ctx = {'event': sender, 'settings': provider.settings}
return template.render(ctx)
@@ -81,3 +85,6 @@ def pretixcontrol_action_display(sender, action, request, **kwargs):
ctx = {'data': data, 'event': sender, 'action': action}
return template.render(ctx, request)
+
+
+settings_hierarkey.add_default('payment_stripe_method_cc', True, bool)
diff --git a/src/pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js b/src/pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js
index ace10e5ff4..18f28d4358 100644
--- a/src/pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js
+++ b/src/pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js
@@ -6,7 +6,7 @@ var pretixstripe = {
elements: null,
card: null,
- 'request': function () {
+ 'cc_request': function () {
waitingDialog.show(gettext("Contacting Stripe …"));
$(".stripe-errors").hide();
@@ -35,42 +35,91 @@ var pretixstripe = {
success: function () {
pretixstripe.stripe = Stripe($.trim($("#stripe_pubkey").html()));
pretixstripe.elements = pretixstripe.stripe.elements();
- pretixstripe.card = pretixstripe.elements.create('card', {
- 'style': {
- 'base': {
- 'fontFamily': '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif',
- 'fontSize': '14px',
- 'color': '#555555',
- 'lineHeight': '1.42857',
- 'border': '1px solid #ccc',
- '::placeholder': {
- color: 'rgba(0,0,0,0.4)',
+ if ($("#stripe-card").length) {
+ pretixstripe.card = pretixstripe.elements.create('card', {
+ 'style': {
+ 'base': {
+ 'fontFamily': '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif',
+ 'fontSize': '14px',
+ 'color': '#555555',
+ 'lineHeight': '1.42857',
+ 'border': '1px solid #ccc',
+ '::placeholder': {
+ color: 'rgba(0,0,0,0.4)',
+ },
+ },
+ 'invalid': {
+ 'color': 'red',
},
},
- 'invalid': {
- 'color': 'red',
- },
- },
- classes: {
- focus: 'is-focused',
- invalid: 'has-error',
- }
- });
- pretixstripe.card.mount("#stripe-card");
+ classes: {
+ focus: 'is-focused',
+ invalid: 'has-error',
+ }
+ });
+ pretixstripe.card.mount("#stripe-card");
+ }
}
}
);
- }
+ },
+ 'load_checkout': function () {
+ $.ajax(
+ {
+ url: 'https://checkout.stripe.com/checkout.js',
+ dataType: 'script',
+ success: function () {
+ pretixstripe.checkout_handler = StripeCheckout.configure({
+ key: $.trim($("#stripe_pubkey").html()),
+ locale: 'auto',
+ token: function (token) {
+ var $form = $("#stripe-checkout").parents("form");
+ $("#stripe_token").val(token.id);
+ $("#stripe_card_brand").val(token.card.brand);
+ $("#stripe_card_last4").val(token.card.last4);
+ $("#stripe_card_brand_display").text(token.card.brand);
+ $("#stripe_card_last4_display").text(token.card.last4);
+ $($form.get(0)).submit();
+ },
+ shippingAddress: false,
+ allowRememberMe: false,
+ billingAddress: false
+ });
+ }
+ }
+ );
+ },
+ 'show_checkout': function () {
+ var amount = Math.round(
+ parseFloat(
+ $("#stripe-checkout").parents("[data-total]").attr("data-total").replace(",", ".")
+ ) * 100
+ );
+ pretixstripe.checkout_handler.open({
+ name: $("#organizer_name").val(),
+ description: $("#event_name").val(),
+ currency: $("#stripe_currency").val(),
+ email: $("#stripe_email").val(),
+ amount: amount
+ });
+ },
+ 'checkout_handler': null
};
$(function () {
- if (!$("#stripe-card").length) // Not on the checkout page
+ if (!$(".stripe-container").length) // Not on the checkout page
return;
if ($("input[name=payment][value=stripe]").is(':checked') || $(".payment-redo-form").length) {
+ if ($("#stripe-checkout").length) {
+ pretixstripe.load_checkout();
+ }
pretixstripe.load();
} else {
$("input[name=payment]").change(function () {
- if ($(this).val() == 'stripe') {
+ if ($(this).val() === 'stripe') {
+ if ($("#stripe-checkout").length) {
+ pretixstripe.load_checkout();
+ }
pretixstripe.load();
}
})
@@ -79,9 +128,12 @@ $(function () {
$("#stripe_other_card").click(
function (e) {
$("#stripe_token").val("");
- $("#stripe-current-card").slideUp();
- $("#stripe-card").slideDown();
- pretixstripe.start();
+ if ($("#stripe-checkout").length) {
+ pretixstripe.show_checkout();
+ } else {
+ $("#stripe-current-card").slideUp();
+ $("#stripe-card").slideDown();
+ }
e.preventDefault();
return false;
}
@@ -91,13 +143,23 @@ $(function () {
$("#stripe-card").hide();
}
- $("#stripe-card").parents("form").submit(
+ $('.stripe-container').closest("form").submit(
function () {
if (($("input[name=payment][value=stripe]").prop('checked') || $("input[name=payment]").length === 0)
&& $("#stripe_token").val() == "") {
- pretixstripe.request();
+ console.log("foo");
+ if ($("#stripe-checkout").length) {
+ pretixstripe.show_checkout();
+ } else {
+ pretixstripe.cc_request();
+ }
return false;
}
}
);
+ $(window).on('popstate', function () {
+ if (pretixstripe.checkout_handler) {
+ pretixstripe.checkout_handler.close();
+ }
+ });
});
diff --git a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html
index 824383ee48..97f0e12332 100644
--- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html
+++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html
@@ -1,12 +1,29 @@
{% load i18n %}
-{% blocktrans trimmed %}
- The total amount will be withdrawn from your credit card.
-{% endblocktrans %}
-
- - {% trans "Card type" %}
- - {{ request.session.payment_stripe_brand }}
- - {% trans "Card number" %}
- - **** **** **** {{ request.session.payment_stripe_last4 }}
-
-
+{% if provider.method == "cc" %}
+ {% blocktrans trimmed %}
+ The total amount will be withdrawn from your credit card.
+ {% endblocktrans %}
+
+ - {% trans "Card type" %}
+ - {{ request.session.payment_stripe_brand }}
+ - {% trans "Card number" %}
+ - **** **** **** {{ request.session.payment_stripe_last4 }}
+
+{% else %}
+ {% 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 %}
+
+ - {% trans "Payment method" %}
+ - {{ provider.public_name }}
+ {% if provider.method == "giropay" %}
+ - {% trans "Account holder" %}
+ - {{ request.session.payment_stripe_giropay_account }}
+ {% elif provider.method == "bancontact" %}
+ - {% trans "Account holder" %}
+ - {{ request.session.payment_stripe_bancontact_account }}
+ {% endif %}
+
+{% endif %}
diff --git a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form.html
index 1f8ca615c3..dff1ecf4ab 100644
--- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form.html
+++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form.html
@@ -1,6 +1,6 @@
{% load i18n %}
-