forked from CGM_Public/pretix_original
Fix #407 -- Integrate more Stripe payment methods
This commit is contained in:
@@ -463,6 +463,9 @@ class SettingsSandbox:
|
|||||||
self._type = typestr
|
self._type = typestr
|
||||||
self._key = key
|
self._key = key
|
||||||
|
|
||||||
|
def get_prefix(self):
|
||||||
|
return '%s_%s_' % (self._type, self._key)
|
||||||
|
|
||||||
def _convert_key(self, key: str) -> str:
|
def _convert_key(self, key: str) -> str:
|
||||||
return '%s_%s_%s' % (self._type, self._key, key)
|
return '%s_%s_%s' % (self._type, self._key, key)
|
||||||
|
|
||||||
|
|||||||
@@ -186,12 +186,12 @@ class PaymentSettings(EventPermissionRequiredMixin, TemplateView, SingleObjectMi
|
|||||||
for provider in self.request.event.get_payment_providers().values():
|
for provider in self.request.event.get_payment_providers().values():
|
||||||
provider.form = ProviderForm(
|
provider.form = ProviderForm(
|
||||||
obj=self.request.event,
|
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)
|
data=(self.request.POST if self.request.method == 'POST' else None)
|
||||||
)
|
)
|
||||||
provider.form.fields = OrderedDict(
|
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()
|
for k, v in provider.settings_form_fields.items()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,20 +7,34 @@ import stripe
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.template.loader import get_template
|
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.payment import BasePaymentProvider, PaymentException
|
||||||
from pretix.base.services.mail import SendMailException
|
from pretix.base.services.mail import SendMailException
|
||||||
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
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
|
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.plugins.stripe')
|
logger = logging.getLogger('pretix.plugins.stripe')
|
||||||
|
|
||||||
|
|
||||||
class Stripe(BasePaymentProvider):
|
class StripeSettingsHolder(BasePaymentProvider):
|
||||||
identifier = 'stripe'
|
identifier = 'stripe_settings'
|
||||||
verbose_name = _('Credit Card via Stripe')
|
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 "<div class='alert alert-info'>%s<br /><code>%s</code></div>" % (
|
||||||
|
_('Please configure a <a href="https://dashboard.stripe.com/account/webhooks">Stripe Webhook</a> 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
|
@property
|
||||||
def settings_form_fields(self):
|
def settings_form_fields(self):
|
||||||
@@ -44,56 +58,87 @@ class Stripe(BasePaymentProvider):
|
|||||||
choices=(
|
choices=(
|
||||||
('pretix', _('Simple (pretix design)')),
|
('pretix', _('Simple (pretix design)')),
|
||||||
('checkout', _('Stripe Checkout')),
|
('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 "<div class='alert alert-info'>%s<br /><code>%s</code></div>" % (
|
|
||||||
_('Please configure a <a href="https://dashboard.stripe.com/account/webhooks">Stripe Webhook</a> 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):
|
class StripeMethod(BasePaymentProvider):
|
||||||
return request.session.get('payment_stripe_token', '') != ''
|
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):
|
def order_prepare(self, request, order):
|
||||||
return self.checkout_prepare(request, None)
|
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):
|
def _init_api(self):
|
||||||
stripe.api_version = '2015-04-07'
|
stripe.api_version = '2017-06-05'
|
||||||
stripe.api_key = self.settings.get('secret_key')
|
stripe.api_key = self.settings.get('secret_key')
|
||||||
|
|
||||||
def checkout_confirm_render(self, request) -> str:
|
def checkout_confirm_render(self, request) -> str:
|
||||||
template = get_template('pretixplugins/stripe/checkout_payment_confirm.html')
|
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)
|
return template.render(ctx)
|
||||||
|
|
||||||
def order_can_retry(self, order):
|
def order_can_retry(self, order):
|
||||||
return self._is_still_available(order=order)
|
return self._is_still_available(order=order)
|
||||||
|
|
||||||
def _charge_source(self, source, order):
|
def _charge_source(self, request, source, order):
|
||||||
try:
|
try:
|
||||||
charge = stripe.Charge.create(
|
charge = stripe.Charge.create(
|
||||||
amount=int(order.total * 100),
|
amount=int(order.total * 100),
|
||||||
@@ -139,7 +184,7 @@ class Stripe(BasePaymentProvider):
|
|||||||
else:
|
else:
|
||||||
if charge.status == 'succeeded' and charge.paid:
|
if charge.status == 'succeeded' and charge.paid:
|
||||||
try:
|
try:
|
||||||
mark_order_paid(order, 'stripe', str(charge))
|
mark_order_paid(order, self.identifier, str(charge))
|
||||||
except Quota.QuotaExceededException as e:
|
except Quota.QuotaExceededException as e:
|
||||||
RequiredAction.objects.create(
|
RequiredAction.objects.create(
|
||||||
event=self.event, action_type='pretix.plugins.stripe.overpaid', data=json.dumps({
|
event=self.event, action_type='pretix.plugins.stripe.overpaid', data=json.dumps({
|
||||||
@@ -151,56 +196,32 @@ class Stripe(BasePaymentProvider):
|
|||||||
|
|
||||||
except SendMailException:
|
except SendMailException:
|
||||||
raise PaymentException(_('There was an error sending the confirmation mail.'))
|
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:
|
else:
|
||||||
logger.info('Charge failed: %s' % str(charge))
|
logger.info('Charge failed: %s' % str(charge))
|
||||||
order.payment_info = str(charge)
|
order.payment_info = str(charge)
|
||||||
order.save(update_fields=['payment_info'])
|
order.save(update_fields=['payment_info'])
|
||||||
raise PaymentException(_('Stripe reported an error: %s') % charge.failure_message)
|
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:
|
def order_pending_render(self, request, order) -> str:
|
||||||
if order.payment_info:
|
if order.payment_info:
|
||||||
payment_info = json.loads(order.payment_info)
|
payment_info = json.loads(order.payment_info)
|
||||||
else:
|
else:
|
||||||
payment_info = None
|
payment_info = None
|
||||||
template = get_template('pretixplugins/stripe/pending.html')
|
template = get_template('pretixplugins/stripe/pending.html')
|
||||||
ctx = {'request': request, 'event': self.event, 'settings': self.settings,
|
ctx = {
|
||||||
'order': order, 'payment_info': payment_info}
|
'request': request,
|
||||||
|
'event': self.event,
|
||||||
|
'settings': self.settings,
|
||||||
|
'provider': self,
|
||||||
|
'order': order,
|
||||||
|
'payment_info': payment_info,
|
||||||
|
}
|
||||||
return template.render(ctx)
|
return template.render(ctx)
|
||||||
|
|
||||||
def order_control_render(self, request, order) -> str:
|
def order_control_render(self, request, order) -> str:
|
||||||
@@ -211,8 +232,15 @@ class Stripe(BasePaymentProvider):
|
|||||||
else:
|
else:
|
||||||
payment_info = None
|
payment_info = None
|
||||||
template = get_template('pretixplugins/stripe/control.html')
|
template = get_template('pretixplugins/stripe/control.html')
|
||||||
ctx = {'request': request, 'event': self.event, 'settings': self.settings,
|
ctx = {
|
||||||
'payment_info': payment_info, 'order': order}
|
'request': request,
|
||||||
|
'event': self.event,
|
||||||
|
'settings': self.settings,
|
||||||
|
'payment_info': payment_info,
|
||||||
|
'order': order,
|
||||||
|
'method': self.method,
|
||||||
|
'provider': self,
|
||||||
|
}
|
||||||
return template.render(ctx)
|
return template.render(ctx)
|
||||||
|
|
||||||
def order_control_refund_render(self, order) -> str:
|
def order_control_refund_render(self, order) -> str:
|
||||||
@@ -255,3 +283,409 @@ class Stripe(BasePaymentProvider):
|
|||||||
order = mark_order_refunded(order, user=request.user)
|
order = mark_order_refunded(order, user=request.user)
|
||||||
order.payment_info = str(ch)
|
order.payment_info = str(ch)
|
||||||
order.save()
|
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'
|
||||||
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from django.dispatch import receiver
|
|||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from pretix.base.settings import settings_hierarkey
|
||||||
from pretix.base.signals import (
|
from pretix.base.signals import (
|
||||||
logentry_display, register_payment_providers, requiredaction_display,
|
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")
|
@receiver(register_payment_providers, dispatch_uid="payment_stripe")
|
||||||
def register_payment_provider(sender, **kwargs):
|
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")
|
@receiver(html_head, dispatch_uid="payment_stripe_html_head")
|
||||||
def html_head_presale(sender, request=None, **kwargs):
|
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)
|
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')
|
template = get_template('pretixplugins/stripe/presale_head.html')
|
||||||
ctx = {'event': sender, 'settings': provider.settings}
|
ctx = {'event': sender, 'settings': provider.settings}
|
||||||
return template.render(ctx)
|
return template.render(ctx)
|
||||||
@@ -81,3 +85,6 @@ def pretixcontrol_action_display(sender, action, request, **kwargs):
|
|||||||
|
|
||||||
ctx = {'data': data, 'event': sender, 'action': action}
|
ctx = {'data': data, 'event': sender, 'action': action}
|
||||||
return template.render(ctx, request)
|
return template.render(ctx, request)
|
||||||
|
|
||||||
|
|
||||||
|
settings_hierarkey.add_default('payment_stripe_method_cc', True, bool)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ var pretixstripe = {
|
|||||||
elements: null,
|
elements: null,
|
||||||
card: null,
|
card: null,
|
||||||
|
|
||||||
'request': function () {
|
'cc_request': function () {
|
||||||
waitingDialog.show(gettext("Contacting Stripe …"));
|
waitingDialog.show(gettext("Contacting Stripe …"));
|
||||||
$(".stripe-errors").hide();
|
$(".stripe-errors").hide();
|
||||||
|
|
||||||
@@ -35,42 +35,91 @@ var pretixstripe = {
|
|||||||
success: function () {
|
success: function () {
|
||||||
pretixstripe.stripe = Stripe($.trim($("#stripe_pubkey").html()));
|
pretixstripe.stripe = Stripe($.trim($("#stripe_pubkey").html()));
|
||||||
pretixstripe.elements = pretixstripe.stripe.elements();
|
pretixstripe.elements = pretixstripe.stripe.elements();
|
||||||
pretixstripe.card = pretixstripe.elements.create('card', {
|
if ($("#stripe-card").length) {
|
||||||
'style': {
|
pretixstripe.card = pretixstripe.elements.create('card', {
|
||||||
'base': {
|
'style': {
|
||||||
'fontFamily': '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif',
|
'base': {
|
||||||
'fontSize': '14px',
|
'fontFamily': '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif',
|
||||||
'color': '#555555',
|
'fontSize': '14px',
|
||||||
'lineHeight': '1.42857',
|
'color': '#555555',
|
||||||
'border': '1px solid #ccc',
|
'lineHeight': '1.42857',
|
||||||
'::placeholder': {
|
'border': '1px solid #ccc',
|
||||||
color: 'rgba(0,0,0,0.4)',
|
'::placeholder': {
|
||||||
|
color: 'rgba(0,0,0,0.4)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'invalid': {
|
||||||
|
'color': 'red',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'invalid': {
|
classes: {
|
||||||
'color': 'red',
|
focus: 'is-focused',
|
||||||
},
|
invalid: 'has-error',
|
||||||
},
|
}
|
||||||
classes: {
|
});
|
||||||
focus: 'is-focused',
|
pretixstripe.card.mount("#stripe-card");
|
||||||
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 () {
|
$(function () {
|
||||||
if (!$("#stripe-card").length) // Not on the checkout page
|
if (!$(".stripe-container").length) // Not on the checkout page
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ($("input[name=payment][value=stripe]").is(':checked') || $(".payment-redo-form").length) {
|
if ($("input[name=payment][value=stripe]").is(':checked') || $(".payment-redo-form").length) {
|
||||||
|
if ($("#stripe-checkout").length) {
|
||||||
|
pretixstripe.load_checkout();
|
||||||
|
}
|
||||||
pretixstripe.load();
|
pretixstripe.load();
|
||||||
} else {
|
} else {
|
||||||
$("input[name=payment]").change(function () {
|
$("input[name=payment]").change(function () {
|
||||||
if ($(this).val() == 'stripe') {
|
if ($(this).val() === 'stripe') {
|
||||||
|
if ($("#stripe-checkout").length) {
|
||||||
|
pretixstripe.load_checkout();
|
||||||
|
}
|
||||||
pretixstripe.load();
|
pretixstripe.load();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -79,9 +128,12 @@ $(function () {
|
|||||||
$("#stripe_other_card").click(
|
$("#stripe_other_card").click(
|
||||||
function (e) {
|
function (e) {
|
||||||
$("#stripe_token").val("");
|
$("#stripe_token").val("");
|
||||||
$("#stripe-current-card").slideUp();
|
if ($("#stripe-checkout").length) {
|
||||||
$("#stripe-card").slideDown();
|
pretixstripe.show_checkout();
|
||||||
pretixstripe.start();
|
} else {
|
||||||
|
$("#stripe-current-card").slideUp();
|
||||||
|
$("#stripe-card").slideDown();
|
||||||
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -91,13 +143,23 @@ $(function () {
|
|||||||
$("#stripe-card").hide();
|
$("#stripe-card").hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#stripe-card").parents("form").submit(
|
$('.stripe-container').closest("form").submit(
|
||||||
function () {
|
function () {
|
||||||
if (($("input[name=payment][value=stripe]").prop('checked') || $("input[name=payment]").length === 0)
|
if (($("input[name=payment][value=stripe]").prop('checked') || $("input[name=payment]").length === 0)
|
||||||
&& $("#stripe_token").val() == "") {
|
&& $("#stripe_token").val() == "") {
|
||||||
pretixstripe.request();
|
console.log("foo");
|
||||||
|
if ($("#stripe-checkout").length) {
|
||||||
|
pretixstripe.show_checkout();
|
||||||
|
} else {
|
||||||
|
pretixstripe.cc_request();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
$(window).on('popstate', function () {
|
||||||
|
if (pretixstripe.checkout_handler) {
|
||||||
|
pretixstripe.checkout_handler.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<p>{% blocktrans trimmed %}
|
{% if provider.method == "cc" %}
|
||||||
The total amount will be withdrawn from your credit card.
|
<p>{% blocktrans trimmed %}
|
||||||
{% endblocktrans %}</p>
|
The total amount will be withdrawn from your credit card.
|
||||||
<dl class="dl-horizontal">
|
{% endblocktrans %}</p>
|
||||||
<dt>{% trans "Card type" %}</dt>
|
<dl class="dl-horizontal">
|
||||||
<dd>{{ request.session.payment_stripe_brand }}</dd>
|
<dt>{% trans "Card type" %}</dt>
|
||||||
<dt>{% trans "Card number" %}</dt>
|
<dd>{{ request.session.payment_stripe_brand }}</dd>
|
||||||
<dd>**** **** **** {{ request.session.payment_stripe_last4 }}</dd>
|
<dt>{% trans "Card number" %}</dt>
|
||||||
</dl>
|
<dd>**** **** **** {{ request.session.payment_stripe_last4 }}</dd>
|
||||||
|
</dl>
|
||||||
|
{% else %}
|
||||||
|
<p>{% 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 %}</p>
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>{% trans "Payment method" %}</dt>
|
||||||
|
<dd>{{ provider.public_name }}</dd>
|
||||||
|
{% if provider.method == "giropay" %}
|
||||||
|
<dt>{% trans "Account holder" %}</dt>
|
||||||
|
<dd>{{ request.session.payment_stripe_giropay_account }}</dd>
|
||||||
|
{% elif provider.method == "bancontact" %}
|
||||||
|
<dt>{% trans "Account holder" %}</dt>
|
||||||
|
<dd>{{ request.session.payment_stripe_bancontact_account }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
</dl>
|
||||||
|
{% endif %}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="form-horizontal">
|
<div class="form-horizontal stripe-container">
|
||||||
<div class="stripe-errors sr-only">
|
<div class="stripe-errors sr-only">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% bootstrap_form form layout='horizontal' %}
|
||||||
|
<p>{% 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 %}</p>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% bootstrap_form form layout='horizontal' %}
|
||||||
|
<p>{% 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 %}</p>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<p>{% 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 %}</p>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% bootstrap_form form layout='horizontal' %}
|
||||||
|
<p>{% 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 %}</p>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="form-horizontal" id="stripe-checkout">
|
<div class="form-horizontal stripe-container" id="stripe-checkout">
|
||||||
<noscript>
|
<noscript>
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
{% trans "For a credit card payment, please turn on JavaScript." %}
|
{% trans "For a credit card payment, please turn on JavaScript." %}
|
||||||
|
|||||||
@@ -2,28 +2,48 @@
|
|||||||
|
|
||||||
{% if payment_info %}
|
{% if payment_info %}
|
||||||
{% if order.status == "p" %}
|
{% if order.status == "p" %}
|
||||||
<p>{% blocktrans trimmed %}
|
<p>{% blocktrans trimmed with method=provider.verbose_name %}
|
||||||
This order has been paid via Stripe.
|
This order has been paid with {{ method }}.
|
||||||
{% endblocktrans %}</p>
|
{% endblocktrans %}</p>
|
||||||
{% elif order.status == "r" %}
|
{% elif order.status == "r" %}
|
||||||
<p>{% blocktrans trimmed %}
|
<p>{% blocktrans trimmed with method=provider.verbose_name %}
|
||||||
This order has been planned to be paid via Stripe and has been marked as refunded.
|
This order has been planned to be paid with {{ method }} and has been marked as refunded.
|
||||||
{% endblocktrans %}</p>
|
{% endblocktrans %}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{% blocktrans trimmed %}
|
<p>{% blocktrans trimmed with method=provider.verbose_name %}
|
||||||
This order has been planned to be paid via Stripe, but the payment has not yet been completed.
|
This order has been planned to be paid with {{ method }}, but the payment has not yet been completed.
|
||||||
{% endblocktrans %}</p>
|
{% endblocktrans %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if order.status == "p" %}
|
{% if order.status == "p" %}
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
<dt>{% trans "Charge ID" %}</dt>
|
<dt>{% trans "Charge ID" %}</dt>
|
||||||
<dd>{{ payment_info.id }}</dd>
|
<dd>{{ payment_info.id }}</dd>
|
||||||
<dt>{% trans "Card type" %}</dt>
|
{% if payment_info.source.type == "card" or payment_info.source.type == "three_d_secure" %}
|
||||||
<dd>{{ payment_info.source.brand }}</dd>
|
<dt>{% trans "Card type" %}</dt>
|
||||||
<dt>{% trans "Card number" %}</dt>
|
<dd>{{ payment_info.source.brand }}</dd>
|
||||||
<dd>**** **** **** {{ payment_info.source.last4 }}</dd>
|
<dt>{% trans "Card number" %}</dt>
|
||||||
<dt>{% trans "Payer name" %}</dt>
|
<dd>**** **** **** {{ payment_info.source.last4 }}</dd>
|
||||||
<dd>{{ payment_info.source.name }}</dd>
|
<dt>{% trans "Payer name" %}</dt>
|
||||||
|
<dd>{{ payment_info.source.name }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% if payment_info.source.type == "giropay" %}
|
||||||
|
<dt>{% trans "Bank" %}</dt>
|
||||||
|
<dd>{{ payment_info.source.giropay.bank_name }} ({{ payment_info.source.giropay.bic }})</dd>
|
||||||
|
<dt>{% trans "Payer name" %}</dt>
|
||||||
|
<dd>{{ payment_info.source.owner.verified_name|default:payment_info.source.owner.name }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% if payment_info.source.type == "bancontact" %}
|
||||||
|
<dt>{% trans "Bank" %}</dt>
|
||||||
|
<dd>{{ payment_info.source.bancontact.bank_name }} ({{ payment_info.source.bancontact.bic }})</dd>
|
||||||
|
<dt>{% trans "Payer name" %}</dt>
|
||||||
|
<dd>{{ payment_info.source.owner.verified_name|default:payment_info.source.owner.name }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% if payment_info.source.type == "ideal" %}
|
||||||
|
<dt>{% trans "Bank" %}</dt>
|
||||||
|
<dd>{{ payment_info.source.ideal.bank }} ({{ payment_info.source.ideal.bic }})</dd>
|
||||||
|
<dt>{% trans "Payer name" %}</dt>
|
||||||
|
<dd>{{ payment_info.source.owner.verified_name|default:payment_info.source.owner.name }}</dd>
|
||||||
|
{% endif %}
|
||||||
<dt>{% trans "Total value" %}</dt>
|
<dt>{% trans "Total value" %}</dt>
|
||||||
<dd>{{ payment_info.amount|floatformat:2 }}</dd>
|
<dd>{{ payment_info.amount|floatformat:2 }}</dd>
|
||||||
<dt>{% trans "Currency" %}</dt>
|
<dt>{% trans "Currency" %}</dt>
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<p>{% blocktrans trimmed %}
|
{% if provider.method == "sofort" %}
|
||||||
The credit card transaction could not be completed for the following reason:
|
<p>{% blocktrans trimmed %}
|
||||||
{% endblocktrans %}
|
We're waiting for an answer from the payment provider regarding your payment. Please contact us if this
|
||||||
<br />
|
takes more than a few days.
|
||||||
{% if payment_info and payment_info.error %}
|
{% endblocktrans %}</p>
|
||||||
{{ payment_info.message }}
|
{% else %}
|
||||||
{% else %}
|
<p>{% blocktrans trimmed %}
|
||||||
{% trans "Unknown reason" %}
|
The payment transaction could not be completed for the following reason:
|
||||||
{% endif %}
|
{% endblocktrans %}
|
||||||
</p>
|
<br/>
|
||||||
|
{% if payment_info and payment_info.error %}
|
||||||
|
{{ payment_info.message }}
|
||||||
|
{% else %}
|
||||||
|
{% trans "Unknown reason" %}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|||||||
@@ -2,14 +2,8 @@
|
|||||||
{% load compress %}
|
{% load compress %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% if settings.ui == "checkout" %}
|
{% compress js %}
|
||||||
{% compress js %}
|
<script type="text/javascript" src="{% static "pretixplugins/stripe/pretix-stripe.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixplugins/stripe/pretix-stripe-checkout.js" %}"></script>
|
{% endcompress %}
|
||||||
{% endcompress %}
|
|
||||||
{% else %}
|
|
||||||
{% compress js %}
|
|
||||||
<script type="text/javascript" src="{% static "pretixplugins/stripe/pretix-stripe.js" %}"></script>
|
|
||||||
{% endcompress %}
|
|
||||||
{% endif %}
|
|
||||||
<script type="text/plain" id="stripe_pubkey">{{ settings.publishable_key }}</script>
|
<script type="text/plain" id="stripe_pubkey">{{ settings.publishable_key }}</script>
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from pretix.base.payment import PaymentException
|
|||||||
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
from pretix.base.services.orders import mark_order_paid, mark_order_refunded
|
||||||
from pretix.control.permissions import event_permission_required
|
from pretix.control.permissions import event_permission_required
|
||||||
from pretix.multidomain.urlreverse import eventreverse
|
from pretix.multidomain.urlreverse import eventreverse
|
||||||
from pretix.plugins.stripe.payment import Stripe
|
from pretix.plugins.stripe.payment import StripeCC
|
||||||
from pretix.presale.utils import event_view
|
from pretix.presale.utils import event_view
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.plugins.stripe')
|
logger = logging.getLogger('pretix.plugins.stripe')
|
||||||
@@ -48,7 +48,7 @@ def webhook(request, *args, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def charge_webhook(request, event_json, charge_id):
|
def charge_webhook(request, event_json, charge_id):
|
||||||
prov = Stripe(request.event)
|
prov = StripeCC(request.event)
|
||||||
prov._init_api()
|
prov._init_api()
|
||||||
try:
|
try:
|
||||||
charge = stripe.Charge.retrieve(charge_id)
|
charge = stripe.Charge.retrieve(charge_id)
|
||||||
@@ -68,6 +68,10 @@ def charge_webhook(request, event_json, charge_id):
|
|||||||
except Order.DoesNotExist:
|
except Order.DoesNotExist:
|
||||||
return HttpResponse('Order not found', status=200)
|
return HttpResponse('Order not found', status=200)
|
||||||
|
|
||||||
|
if order.payment_provider != prov.identifier:
|
||||||
|
prov = request.event.get_payment_providers()[order.payment_provider]
|
||||||
|
prov._init_api()
|
||||||
|
|
||||||
order.log_action('pretix.plugins.stripe.event', data=event_json)
|
order.log_action('pretix.plugins.stripe.event', data=event_json)
|
||||||
|
|
||||||
is_refund = charge['refunds']['total_count'] or charge['dispute']
|
is_refund = charge['refunds']['total_count'] or charge['dispute']
|
||||||
@@ -97,7 +101,7 @@ def charge_webhook(request, event_json, charge_id):
|
|||||||
|
|
||||||
|
|
||||||
def source_webhook(request, event_json, source_id):
|
def source_webhook(request, event_json, source_id):
|
||||||
prov = Stripe(request.event)
|
prov = StripeCC(request.event)
|
||||||
prov._init_api()
|
prov._init_api()
|
||||||
try:
|
try:
|
||||||
src = stripe.Source.retrieve(source_id)
|
src = stripe.Source.retrieve(source_id)
|
||||||
@@ -118,12 +122,16 @@ def source_webhook(request, event_json, source_id):
|
|||||||
except Order.DoesNotExist:
|
except Order.DoesNotExist:
|
||||||
return HttpResponse('Order not found', status=200)
|
return HttpResponse('Order not found', status=200)
|
||||||
|
|
||||||
|
if order.payment_provider != prov.identifier:
|
||||||
|
prov = request.event.get_payment_providers()[order.payment_provider]
|
||||||
|
prov._init_api()
|
||||||
|
|
||||||
order.log_action('pretix.plugins.stripe.event', data=event_json)
|
order.log_action('pretix.plugins.stripe.event', data=event_json)
|
||||||
go = (event_json['type'] == 'source.chargeable' and order.status == Order.STATUS_PENDING and
|
go = (event_json['type'] == 'source.chargeable' and order.status == Order.STATUS_PENDING and
|
||||||
src.status == 'chargeable')
|
src.status == 'chargeable')
|
||||||
if go:
|
if go:
|
||||||
try:
|
try:
|
||||||
prov._charge_source(source_id, order)
|
prov._charge_source(request, source_id, order)
|
||||||
except PaymentException:
|
except PaymentException:
|
||||||
logger.exception('Webhook error')
|
logger.exception('Webhook error')
|
||||||
|
|
||||||
@@ -178,7 +186,7 @@ class StripeOrderView:
|
|||||||
@method_decorator(event_view, name='dispatch')
|
@method_decorator(event_view, name='dispatch')
|
||||||
class ReturnView(StripeOrderView, View):
|
class ReturnView(StripeOrderView, View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
prov = Stripe(request.event)
|
prov = self.pprov
|
||||||
prov._init_api()
|
prov._init_api()
|
||||||
src = stripe.Source.retrieve(request.GET.get('source'))
|
src = stripe.Source.retrieve(request.GET.get('source'))
|
||||||
if src.client_secret != request.GET.get('client_secret'):
|
if src.client_secret != request.GET.get('client_secret'):
|
||||||
@@ -194,12 +202,13 @@ class ReturnView(StripeOrderView, View):
|
|||||||
|
|
||||||
if src.status == 'chargeable':
|
if src.status == 'chargeable':
|
||||||
try:
|
try:
|
||||||
prov._charge_source(src.id, self.order)
|
prov._charge_source(request, src.id, self.order)
|
||||||
except PaymentException as e:
|
except PaymentException as e:
|
||||||
messages.error(request, str(e))
|
messages.error(request, str(e))
|
||||||
return self._redirect_to_order()
|
return self._redirect_to_order()
|
||||||
finally:
|
finally:
|
||||||
del request.session['payment_stripe_token']
|
if 'payment_stripe_token' in request.session:
|
||||||
|
del request.session['payment_stripe_token']
|
||||||
else:
|
else:
|
||||||
messages.error(self.request, _('We had trouble authorizing your card payment. Please try again and '
|
messages.error(self.request, _('We had trouble authorizing your card payment. Please try again and '
|
||||||
'get in touch with us if this problem persists.'))
|
'get in touch with us if this problem persists.'))
|
||||||
|
|||||||
Reference in New Issue
Block a user