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 %} -
+
diff --git a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_bancontact.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_bancontact.html new file mode 100644 index 0000000000..b1a340292f --- /dev/null +++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_bancontact.html @@ -0,0 +1,7 @@ +{% load i18n %} +{% load bootstrap3 %} +{% bootstrap_form form layout='horizontal' %} +

{% 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/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_giropay.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_giropay.html new file mode 100644 index 0000000000..b1a340292f --- /dev/null +++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_giropay.html @@ -0,0 +1,7 @@ +{% load i18n %} +{% load bootstrap3 %} +{% bootstrap_form form layout='horizontal' %} +

{% 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/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_simple.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_simple.html new file mode 100644 index 0000000000..e54c7699d3 --- /dev/null +++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_simple.html @@ -0,0 +1,5 @@ +{% load i18n %} +

{% 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/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sofort.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sofort.html new file mode 100644 index 0000000000..b1a340292f --- /dev/null +++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sofort.html @@ -0,0 +1,7 @@ +{% load i18n %} +{% load bootstrap3 %} +{% bootstrap_form form layout='horizontal' %} +

{% 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/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_stripe_checkout.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_stripe_checkout.html index 7d44f72d48..bdc1b368be 100644 --- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_stripe_checkout.html +++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_stripe_checkout.html @@ -1,6 +1,6 @@ {% load i18n %} -
+