Fix regression in handling gift card payments (#2936)

This commit is contained in:
Raphael Michel
2022-12-05 11:32:27 +01:00
committed by GitHub
parent 547cfdffd6
commit 6a8df75a9f
3 changed files with 70 additions and 38 deletions

View File

@@ -74,7 +74,7 @@ from pretix.base.models.orders import (
)
from pretix.base.models.organizer import TeamAPIToken
from pretix.base.models.tax import TAXED_ZERO, TaxedPrice, TaxRule
from pretix.base.payment import PaymentException
from pretix.base.payment import GiftCardPayment, PaymentException
from pretix.base.reldate import RelativeDateWrapper
from pretix.base.secrets import assign_ticket_secret
from pretix.base.services import tickets
@@ -1029,6 +1029,9 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
locked = True
lockfn = event.lock
warnings = []
any_payment_failed = False
with lockfn() as now_dt:
positions = list(
positions.select_related('item', 'variation', 'subevent', 'seat', 'addon_to').prefetch_related('addons')
@@ -1042,25 +1045,52 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
order, payment_objs = _create_order(event, email, positions, now_dt, payment_requests,
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel,
shown_total=shown_total, customer=customer)
try:
for p in payment_objs:
if p.provider == 'free':
p.confirm(send_mail=False, lock=not locked, generate_invoice=False)
except Quota.QuotaExceededException:
pass
free_order_flow = (
payment_objs and
any(p['provider'] == 'free' for p in payment_requests) and
order.pending_sum == Decimal('0.00') and
not order.require_approval
)
if free_order_flow:
# We give special treatment to GiftCardPayment here because our invoice renderer expects gift cards to already be
# processed, and because we historically treat gift card orders like free orders with regards to email texts.
# It would be great to give external gift card plugins the same special treatment, but it feels to risky for now, as
# (a) there would be no email at all if the plugin fails in a weird way and (b) we'd be able to run into
# contradictions when a plugin set both execute_payment_needs_user=False as well as requires_invoice_immediately=True
for p in payment_objs:
if isinstance(p.payment_provider, GiftCardPayment):
try:
for p in payment_objs:
if p.provider == 'free':
p.confirm(send_mail=False, lock=not locked)
except Quota.QuotaExceededException:
pass
p.process_initiated = True
p.save(update_fields=['process_initiated'])
p.payment_provider.execute_payment(None, p, is_early_special_case=True)
except PaymentException as e:
warnings.append(str(e))
any_payment_failed = True
except Exception:
logger.exception('Error during payment attempt')
pending_sum = order.pending_sum
free_order_flow = (
payment_objs and
(
any(p['provider'] == 'free' for p in payment_requests) or
all(p['provider'] == 'giftcard' for p in payment_requests)
) and
pending_sum == Decimal('0.00') and
not order.require_approval
)
invoice = order.invoices.last() # Might be generated by plugin already
if not invoice and invoice_qualified(order):
if event.settings.get('invoice_generate') == 'True' or (
event.settings.get('invoice_generate') == 'paid' and any(p['pprov'].requires_invoice_immediately for p in payment_requests)):
invoice_required = (
event.settings.get('invoice_generate') == 'True' or (
event.settings.get('invoice_generate') == 'paid' and (
any(p['pprov'].requires_invoice_immediately for p in payment_requests) or
pending_sum <= Decimal('0.00')
)
)
)
if invoice_required:
invoice = generate_invoice(
order,
trigger_pdf=not event.settings.invoice_email_attachment or not order.email
@@ -1100,23 +1130,22 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
_order_placed_email_attendee(event, order, p, email_attendees_template, subject_attendees_template, log_entry,
is_free=free_order_flow)
warnings = []
any_failed = False
for p in payment_objs:
if not p.payment_provider.execute_payment_needs_user:
try:
p.process_initiated = True
p.save(update_fields=['process_initiated'])
resp = p.payment_provider.execute_payment(None, p)
if isinstance(resp, str):
logger.warning('Payment provider returned URL from execute_payment even though execute_payment_needs_user is not set')
except PaymentException as e:
warnings.append(str(e))
any_failed = True
except Exception:
logger.exception('Error during payment attempt')
if not any_payment_failed:
for p in payment_objs:
if not p.payment_provider.execute_payment_needs_user and not p.process_initiated:
try:
p.process_initiated = True
p.save(update_fields=['process_initiated'])
resp = p.payment_provider.execute_payment(None, p)
if isinstance(resp, str):
logger.warning('Payment provider returned URL from execute_payment even though execute_payment_needs_user is not set')
except PaymentException as e:
warnings.append(str(e))
any_payment_failed = True
except Exception:
logger.exception('Error during payment attempt')
if any_failed:
if any_payment_failed:
# Cancel all other payments because their amount might be wrong now.
for p in payment_objs:
if p.state == OrderPayment.PAYMENT_STATE_CREATED: