diff --git a/src/pretix/plugins/paypal/payment.py b/src/pretix/plugins/paypal/payment.py index e9d34fb9a8..fd441f8206 100644 --- a/src/pretix/plugins/paypal/payment.py +++ b/src/pretix/plugins/paypal/payment.py @@ -8,7 +8,7 @@ from django.contrib import messages from django.template.loader import get_template from django.utils.translation import ugettext as __, ugettext_lazy as _ -from pretix.base.models import Quota +from pretix.base.models import Quota, RequiredAction from pretix.base.payment import BasePaymentProvider from pretix.base.services.mail import SendMailException from pretix.base.services.orders import mark_order_paid, mark_order_refunded @@ -203,6 +203,12 @@ class Paypal(BasePaymentProvider): mark_order_paid(order, 'paypal', json.dumps(payment.to_dict())) except Quota.QuotaExceededException as e: messages.error(request, str(e)) + RequiredAction.objects.create( + event=request.event, action_type='pretix.plugins.paypal.overpaid', data=json.dumps({ + 'order': order.code, + 'payment': payment.id + }) + ) except SendMailException: messages.warning(request, _('There was an error sending the confirmation mail.')) return None diff --git a/src/pretix/plugins/paypal/signals.py b/src/pretix/plugins/paypal/signals.py index d5d207c14c..3af96299f9 100644 --- a/src/pretix/plugins/paypal/signals.py +++ b/src/pretix/plugins/paypal/signals.py @@ -39,10 +39,15 @@ def pretixcontrol_logentry_display(sender, logentry, **kwargs): @receiver(signal=requiredaction_display, dispatch_uid="paypal_requiredaction_display") def pretixcontrol_action_display(sender, action, request, **kwargs): - if action.action_type != 'pretix.plugins.paypal.refund': + if not action.action_type.startswith('pretix.plugins.paypal'): return data = json.loads(action.data) - template = get_template('pretixplugins/paypal/action_refund.html') + + if action.action_type == 'pretix.plugins.paypal.refund': + template = get_template('pretixplugins/paypal/action_refund.html') + elif action.action_type == 'pretix.plugins.paypal.overpaid': + template = get_template('pretixplugins/paypal/action_overpaid.html') + ctx = {'data': data, 'event': sender, 'action': action} return template.render(ctx, request) diff --git a/src/pretix/plugins/paypal/templates/pretixplugins/paypal/action_overpaid.html b/src/pretix/plugins/paypal/templates/pretixplugins/paypal/action_overpaid.html new file mode 100644 index 0000000000..12dec90b74 --- /dev/null +++ b/src/pretix/plugins/paypal/templates/pretixplugins/paypal/action_overpaid.html @@ -0,0 +1,10 @@ +{% load i18n %} + +

+ {% url "control:event.order" organizer=event.organizer.slug event=event.slug code=data.order as ourl %} + {% blocktrans trimmed with payment=data.payment order=""|add:data.order|add:""|safe %} + The PayPal transaction {{ payment }} has succeeded, but the order {{ order }} is expired and the product + was sold out in the meantime. Therefore, the payment could not be acceped. Please contact the user and refund + the money via PayPal's interface. + {% endblocktrans %} +

diff --git a/src/pretix/plugins/paypal/views.py b/src/pretix/plugins/paypal/views.py index c37980fca0..a60db893d6 100644 --- a/src/pretix/plugins/paypal/views.py +++ b/src/pretix/plugins/paypal/views.py @@ -11,7 +11,7 @@ from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST -from pretix.base.models import Order, RequiredAction +from pretix.base.models import Order, Quota, RequiredAction from pretix.base.services.orders import mark_order_paid, mark_order_refunded from pretix.control.permissions import event_permission_required from pretix.multidomain.urlreverse import eventreverse @@ -103,8 +103,18 @@ def webhook(request, *args, **kwargs): 'sale': sale['id'] }) ) - elif order.status == Order.STATUS_PENDING and sale['state'] == 'completed': - mark_order_paid(order, user=None) + elif order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) and sale['state'] == 'completed': + try: + mark_order_paid(order, user=None) + except Quota.QuotaExceededException: + if not RequiredAction.objects.filter(event=request.event, action_type='pretix.plugins.paypal.overpaid', + data__icontains=order.code).exists(): + RequiredAction.objects.create( + event=request.event, action_type='pretix.plugins.paypal.overpaid', data=json.dumps({ + 'order': order.code, + 'payment': sale['parent_payment'] + }) + ) return HttpResponse(status=200) diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index bf1de5ef92..f4c2de546e 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -8,7 +8,7 @@ from django.contrib import messages from django.template.loader import get_template from django.utils.translation import ugettext_lazy as _ -from pretix.base.models import Quota +from pretix.base.models import Quota, RequiredAction from pretix.base.payment import BasePaymentProvider from pretix.base.services.mail import SendMailException from pretix.base.services.orders import mark_order_paid, mark_order_refunded @@ -137,6 +137,12 @@ class Stripe(BasePaymentProvider): mark_order_paid(order, 'stripe', str(charge)) except Quota.QuotaExceededException as e: messages.error(request, str(e)) + RequiredAction.objects.create( + event=request.event, action_type='pretix.plugins.stripe.overpaid', data=json.dumps({ + 'order': order.code, + 'charge': charge.id + }) + ) except SendMailException: messages.warning(request, _('There was an error sending the confirmation mail.')) diff --git a/src/pretix/plugins/stripe/signals.py b/src/pretix/plugins/stripe/signals.py index 354dd55b05..5f99740b12 100644 --- a/src/pretix/plugins/stripe/signals.py +++ b/src/pretix/plugins/stripe/signals.py @@ -63,10 +63,15 @@ def pretixcontrol_logentry_display(sender, logentry, **kwargs): @receiver(signal=requiredaction_display, dispatch_uid="stripe_requiredaction_display") def pretixcontrol_action_display(sender, action, request, **kwargs): - if action.action_type != 'pretix.plugins.stripe.refund': + if not action.action_type.startswith('pretix.plugins.stripe'): return data = json.loads(action.data) - template = get_template('pretixplugins/stripe/action_refund.html') + + if action.action_type == 'pretix.plugins.stripe.refund': + template = get_template('pretixplugins/stripe/action_refund.html') + elif action.action_type == 'pretix.plugins.stripe.overpaid': + template = get_template('pretixplugins/stripe/action_overpaid.html') + ctx = {'data': data, 'event': sender, 'action': action} return template.render(ctx, request) diff --git a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/action_overpaid.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/action_overpaid.html new file mode 100644 index 0000000000..1bcbb4e511 --- /dev/null +++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/action_overpaid.html @@ -0,0 +1,10 @@ +{% load i18n %} + +

+ {% url "control:event.order" organizer=event.organizer.slug event=event.slug code=data.order as ourl %} + {% blocktrans trimmed with charge=data.charge stripe_href="href='https://dashboard.stripe.com/payments/"|add:data.charge|add:"' target='_blank'"|safe order=""|add:data.order|add:""|safe %} + The Stripe transaction {{ charge }} has succeeded, but the order {{ order }} is + expired and the product was sold out in the meantime. Therefore, the payment could not be acceped. Please + contact the user and refund the money via Stripe's interface. + {% endblocktrans %} +

diff --git a/src/pretix/plugins/stripe/views.py b/src/pretix/plugins/stripe/views.py index ff84cbf2b0..eb74521d88 100644 --- a/src/pretix/plugins/stripe/views.py +++ b/src/pretix/plugins/stripe/views.py @@ -11,7 +11,7 @@ from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST -from pretix.base.models import Order, RequiredAction +from pretix.base.models import Order, Quota, RequiredAction from pretix.base.services.orders import mark_order_paid, mark_order_refunded from pretix.control.permissions import event_permission_required from pretix.plugins.stripe.payment import Stripe @@ -68,8 +68,18 @@ def webhook(request, *args, **kwargs): 'charge': charge_id }) ) - elif order.status == Order.STATUS_PENDING and charge['status'] == 'succeeded' and not is_refund: - mark_order_paid(order, user=None) + elif order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) and charge['status'] == 'succeeded' and not is_refund: + try: + mark_order_paid(order, user=None) + except Quota.QuotaExceededException: + if not RequiredAction.objects.filter(event=request.event, action_type='pretix.plugins.stripe.overpaid', + data__icontains=order.code).exists(): + RequiredAction.objects.create( + event=request.event, action_type='pretix.plugins.stripe.overpaid', data=json.dumps({ + 'order': order.code, + 'charge': charge.id + }) + ) return HttpResponse(status=200)