From b3e3d427cb2acc0ab3c9dc346a3c877b684a5021 Mon Sep 17 00:00:00 2001
From: Martin Gross
Date: Wed, 5 Feb 2020 09:50:29 +0100
Subject: [PATCH] Add more Stripe Payment Methods, simplify forms (#1571)
* Add more Stripe Payment Methods, simplify forms
* Revert accidential commit of boxoffice control renderer...
* Use existing QR-Code encoding in presale
---
src/pretix/plugins/stripe/payment.py | 297 +++++++++++++++++-
src/pretix/plugins/stripe/signals.py | 7 +-
...orm.html => checkout_payment_form_cc.html} | 0
.../stripe/checkout_payment_form_giropay.html | 7 -
.../stripe/checkout_payment_form_simple.html | 2 +
... checkout_payment_form_simple_noform.html} | 2 -
.../stripe/checkout_payment_form_sofort.html | 7 -
.../pretixplugins/stripe/pending.html | 9 +-
8 files changed, 295 insertions(+), 36 deletions(-)
rename src/pretix/plugins/stripe/templates/pretixplugins/stripe/{checkout_payment_form.html => checkout_payment_form_cc.html} (100%)
delete mode 100644 src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_giropay.html
rename src/pretix/plugins/stripe/templates/pretixplugins/stripe/{checkout_payment_form_bancontact.html => checkout_payment_form_simple_noform.html} (78%)
delete mode 100644 src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sofort.html
diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py
index 24fff0f7a..30344f3ab 100644
--- a/src/pretix/plugins/stripe/payment.py
+++ b/src/pretix/plugins/stripe/payment.py
@@ -28,7 +28,7 @@ from pretix.base.payment import BasePaymentProvider, PaymentException
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
+from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.plugins.stripe.forms import StripeKeyValidator
from pretix.plugins.stripe.models import (
ReferencedStripeObject, RegisteredApplePayDomain,
@@ -200,6 +200,34 @@ class StripeSettingsHolder(BasePaymentProvider):
),
required=False,
)),
+ ('method_eps',
+ forms.BooleanField(
+ label=_('EPS'),
+ disabled=self.event.currency != 'EUR',
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
+ ('method_multibanco',
+ forms.BooleanField(
+ label=_('Multibanco'),
+ disabled=self.event.currency != 'EUR',
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
+ ('method_przelewy24',
+ forms.BooleanField(
+ label=_('Przelewy24'),
+ disabled=self.event.currency not in ['EUR', 'PLN'],
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
+ ('method_wechatpay',
+ forms.BooleanField(
+ label=_('WeChat Pay'),
+ disabled=self.event.currency not in ['AUD', 'CAD', 'EUR', 'GBP', 'HKD', 'JPY', 'SGD', 'USD'],
+ help_text=_('Needs to be enabled in your Stripe account first.'),
+ required=False,
+ )),
] + list(super().settings_form_fields.items())
)
d.move_to_end('_enabled', last=False)
@@ -603,7 +631,7 @@ class StripeCC(StripeMethod):
if not RegisteredApplePayDomain.objects.filter(account=account, domain=request.host).exists():
stripe_verify_domain.apply_async(args=(self.event.pk, request.host))
- template = get_template('pretixplugins/stripe/checkout_payment_form.html')
+ template = get_template('pretixplugins/stripe/checkout_payment_form_cc.html')
ctx = {
'request': request,
'event': self.event,
@@ -843,7 +871,7 @@ class StripeGiropay(StripeMethod):
method = 'giropay'
def payment_form_render(self, request) -> str:
- template = get_template('pretixplugins/stripe/checkout_payment_form_giropay.html')
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple.html')
ctx = {
'request': request,
'event': self.event,
@@ -872,9 +900,7 @@ class StripeGiropay(StripeMethod):
owner={
'name': request.session.get('payment_stripe_giropay_account') or ugettext('unknown name')
},
- giropay={
- 'statement_descriptor': self.statement_descriptor(payment, 35),
- },
+ statement_descriptor=self.statement_descriptor(payment, 35),
redirect={
'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
'order': payment.order.code,
@@ -909,7 +935,7 @@ class StripeIdeal(StripeMethod):
method = 'ideal'
def payment_form_render(self, request) -> str:
- template = get_template('pretixplugins/stripe/checkout_payment_form_simple.html')
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple_noform.html')
ctx = {
'request': request,
'event': self.event,
@@ -927,9 +953,7 @@ class StripeIdeal(StripeMethod):
'event': self.event.id,
'code': payment.order.code
},
- ideal={
- 'statement_descriptor': self.statement_descriptor(payment)
- },
+ statement_descriptor=self.statement_descriptor(payment),
redirect={
'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
'order': payment.order.code,
@@ -955,7 +979,7 @@ class StripeAlipay(StripeMethod):
method = 'alipay'
def payment_form_render(self, request) -> str:
- template = get_template('pretixplugins/stripe/checkout_payment_form_simple.html')
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple_noform.html')
ctx = {
'request': request,
'event': self.event,
@@ -998,7 +1022,7 @@ class StripeBancontact(StripeMethod):
method = 'bancontact'
def payment_form_render(self, request) -> str:
- template = get_template('pretixplugins/stripe/checkout_payment_form_bancontact.html')
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple.html')
ctx = {
'request': request,
'event': self.event,
@@ -1027,9 +1051,7 @@ class StripeBancontact(StripeMethod):
owner={
'name': request.session.get('payment_stripe_bancontact_account') or ugettext('unknown name')
},
- bancontact={
- 'statement_descriptor': self.statement_descriptor(payment, 35)
- },
+ statement_descriptor=self.statement_descriptor(payment, 35),
redirect={
'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
'order': payment.order.code,
@@ -1064,7 +1086,7 @@ class StripeSofort(StripeMethod):
method = 'sofort'
def payment_form_render(self, request) -> str:
- template = get_template('pretixplugins/stripe/checkout_payment_form_sofort.html')
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple.html')
ctx = {
'request': request,
'event': self.event,
@@ -1095,9 +1117,9 @@ class StripeSofort(StripeMethod):
'event': self.event.id,
'code': payment.order.code
},
+ statement_descriptor=self.statement_descriptor(payment, 35),
sofort={
'country': request.session.get('payment_stripe_sofort_bank_country'),
- 'statement_descriptor': self.statement_descriptor(payment, 35)
},
redirect={
'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
@@ -1124,3 +1146,244 @@ class StripeSofort(StripeMethod):
def payment_can_retry(self, payment):
return payment.state != OrderPayment.PAYMENT_STATE_PENDING and self._is_still_available(order=payment.order)
+
+
+class StripeEPS(StripeMethod):
+ identifier = 'stripe_eps'
+ verbose_name = _('EPS via Stripe')
+ public_name = _('EPS')
+ method = 'eps'
+
+ 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,
+ '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, payment):
+ try:
+ source = stripe.Source.create(
+ type='eps',
+ amount=self._get_amount(payment),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(payment.order.id),
+ 'event': self.event.id,
+ 'code': payment.order.code
+ },
+ owner={
+ 'name': request.session.get('payment_stripe_eps_account') or ugettext('unknown name')
+ },
+ statement_descriptor=self.statement_descriptor(payment),
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': payment.order.code,
+ 'payment': payment.pk,
+ 'hash': hashlib.sha1(payment.order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ **self.api_kwargs
+ )
+ return source
+ finally:
+ if 'payment_stripe_eps_account' in request.session:
+ del request.session['payment_stripe_eps_account']
+
+ def payment_is_valid_session(self, request):
+ return (
+ request.session.get('payment_stripe_eps_account', '') != ''
+ )
+
+ def checkout_prepare(self, request, cart):
+ form = self.payment_form(request)
+ if form.is_valid():
+ request.session['payment_stripe_eps_account'] = form.cleaned_data['account']
+ return True
+ return False
+
+
+class StripeMultibanco(StripeMethod):
+ identifier = 'stripe_multibanco'
+ verbose_name = _('Multibanco via Stripe')
+ public_name = _('Multibanco')
+ method = 'multibanco'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple_noform.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'form': self.payment_form(request)
+ }
+ return template.render(ctx)
+
+ def _create_source(self, request, payment):
+ source = stripe.Source.create(
+ type='multibanco',
+ amount=self._get_amount(payment),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(payment.order.id),
+ 'event': self.event.id,
+ 'code': payment.order.code
+ },
+ owner={
+ 'email': payment.order.email
+ },
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': payment.order.code,
+ 'payment': payment.pk,
+ 'hash': hashlib.sha1(payment.order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ **self.api_kwargs
+ )
+ return source
+
+ def payment_is_valid_session(self, request):
+ return True
+
+ def checkout_prepare(self, request, cart):
+ return True
+
+
+class StripePrzelewy24(StripeMethod):
+ identifier = 'stripe_przelewy24'
+ verbose_name = _('Przelewy24 via Stripe')
+ public_name = _('Przelewy24')
+ method = 'przelewy24'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple_noform.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'form': self.payment_form(request)
+ }
+ return template.render(ctx)
+
+ def _create_source(self, request, payment):
+ source = stripe.Source.create(
+ type='p24',
+ amount=self._get_amount(payment),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(payment.order.id),
+ 'event': self.event.id,
+ 'code': payment.order.code
+ },
+ owner={
+ 'email': payment.order.email
+ },
+ statement_descriptor=self.statement_descriptor(payment, 35),
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': payment.order.code,
+ 'payment': payment.pk,
+ 'hash': hashlib.sha1(payment.order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ **self.api_kwargs
+ )
+ return source
+
+ def payment_is_valid_session(self, request):
+ return True
+
+ def checkout_prepare(self, request, cart):
+ return True
+
+
+class StripeWeChatPay(StripeMethod):
+ identifier = 'stripe_wechatpay'
+ verbose_name = _('WeChat Pay via Stripe')
+ public_name = _('WeChat Pay')
+ method = 'wechatpay'
+
+ def payment_form_render(self, request) -> str:
+ template = get_template('pretixplugins/stripe/checkout_payment_form_simple_noform.html')
+ ctx = {
+ 'request': request,
+ 'event': self.event,
+ 'settings': self.settings,
+ 'form': self.payment_form(request)
+ }
+ return template.render(ctx)
+
+ def _create_source(self, request, payment):
+ source = stripe.Source.create(
+ type='wechat',
+ amount=self._get_amount(payment),
+ currency=self.event.currency.lower(),
+ metadata={
+ 'order': str(payment.order.id),
+ 'event': self.event.id,
+ 'code': payment.order.code
+ },
+ statement_descriptor=self.statement_descriptor(payment, 32),
+ redirect={
+ 'return_url': build_absolute_uri(self.event, 'plugins:stripe:return', kwargs={
+ 'order': payment.order.code,
+ 'payment': payment.pk,
+ 'hash': hashlib.sha1(payment.order.secret.lower().encode()).hexdigest(),
+ })
+ },
+ **self.api_kwargs
+ )
+ return source
+
+ def payment_is_valid_session(self, request):
+ return True
+
+ def checkout_prepare(self, request, cart):
+ return True
+
+ def execute_payment(self, request: HttpRequest, payment: OrderPayment):
+ self._init_api()
+ try:
+ source = self._create_source(request, payment)
+ except stripe.error.StripeError as e:
+ if e.json_body and 'err' in 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))
+ payment.info_data = {
+ 'error': True,
+ 'message': err['message'],
+ }
+ payment.state = OrderPayment.PAYMENT_STATE_FAILED
+ payment.save()
+ payment.order.log_action('pretix.event.order.payment.failed', {
+ 'local_id': payment.local_id,
+ 'provider': payment.provider,
+ 'message': err['message']
+ })
+ raise PaymentException(_('We had trouble communicating with Stripe. Please try again and get in touch '
+ 'with us if this problem persists.'))
+
+ ReferencedStripeObject.objects.get_or_create(
+ reference=source.id,
+ defaults={'order': payment.order, 'payment': payment}
+ )
+ payment.info = str(source)
+ payment.save()
+
+ return eventreverse(request.event, 'presale:event.order', kwargs={
+ 'order': payment.order.code,
+ 'secret': payment.order.secret
+ })
diff --git a/src/pretix/plugins/stripe/signals.py b/src/pretix/plugins/stripe/signals.py
index 200de8a52..7a55c2d8d 100644
--- a/src/pretix/plugins/stripe/signals.py
+++ b/src/pretix/plugins/stripe/signals.py
@@ -21,10 +21,13 @@ from pretix.presale.signals import html_head
def register_payment_provider(sender, **kwargs):
from .payment import (
StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact,
- StripeSofort
+ StripeSofort, StripeEPS, StripeMultibanco, StripePrzelewy24, StripeWeChatPay
)
- return [StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact, StripeSofort]
+ return [
+ StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact,
+ StripeSofort, StripeEPS, StripeMultibanco, StripePrzelewy24, StripeWeChatPay
+ ]
@receiver(html_head, dispatch_uid="payment_stripe_html_head")
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_cc.html
similarity index 100%
rename from src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form.html
rename to src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_cc.html
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
deleted file mode 100644
index b1a340292..000000000
--- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_giropay.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% 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
index e54c7699d..b1a340292 100644
--- 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
@@ -1,4 +1,6 @@
{% 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.
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_simple_noform.html
similarity index 78%
rename from src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_bancontact.html
rename to src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_simple_noform.html
index b1a340292..e54c7699d 100644
--- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_bancontact.html
+++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_simple_noform.html
@@ -1,6 +1,4 @@
{% 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.
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
deleted file mode 100644
index b1a340292..000000000
--- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sofort.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% 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/pending.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/pending.html
index 53324e69f..67d7a941e 100644
--- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/pending.html
+++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/pending.html
@@ -17,7 +17,14 @@
-
+{% elif payment.state == "created" and payment.provider == "stripe_wechatpay" %}
+ {% 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: