diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py
index 2ac2ab8ba..0e20f9c9c 100644
--- a/src/pretix/api/serializers/order.py
+++ b/src/pretix/api/serializers/order.py
@@ -53,7 +53,7 @@ from pretix.base.decimal import round_decimal
from pretix.base.i18n import language
from pretix.base.invoicing.transmission import get_transmission_types
from pretix.base.models import (
- CachedFile, Checkin, Customer, Device, Invoice, InvoiceAddress,
+ CachedFile, Checkin, Customer, Device, GiftCard, Invoice, InvoiceAddress,
InvoiceLine, Item, ItemVariation, Order, OrderPosition, Question,
QuestionAnswer, ReusableMedium, SalesChannel, Seat, SubEvent, TaxRule,
Voucher,
@@ -1831,6 +1831,18 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
except PaymentException:
pass
+ except GiftCard.DoesNotExist as e:
+ payment = order.payments.create(
+ amount=order.pending_sum,
+ provider=GiftCardPayment.identifier,
+ info_data={
+ 'gift_card_secret': gift_card_secret,
+ },
+ state=OrderPayment.PAYMENT_STATE_CREATED
+ )
+ payment.fail(info={**payment.info_data, 'error': str(e)},
+ send_mail=False)
+
if order.total == Decimal('0.00') and validated_data.get('status') != Order.STATUS_PAID and not validated_data.get('require_approval'):
order.status = Order.STATUS_PAID
order.save()
diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py
index f7fdd3c7a..14fe05829 100644
--- a/src/pretix/base/payment.py
+++ b/src/pretix/base/payment.py
@@ -1525,18 +1525,26 @@ class GiftCardPayment(BasePaymentProvider):
def payment_control_render(self, request, payment) -> str:
from .models import GiftCard
- if 'gift_card' in payment.info_data:
- gc = GiftCard.objects.get(pk=payment.info_data.get('gift_card'))
+ if any(key in payment.info_data for key in ('gift_card', 'error')):
template = get_template('pretixcontrol/giftcards/payment.html')
-
ctx = {
'request': request,
'event': self.event,
- 'gc': gc,
**({'error': payment.info_data[
- 'error']} if 'error' in payment.info_data else {})
+ 'error']} if 'error' in payment.info_data else {}),
+ **({'gift_card_secret': payment.info_data[
+ 'gift_card_secret']} if 'gift_card_secret' in payment.info_data else {})
}
- return template.render(ctx)
+
+ try:
+ gc = GiftCard.objects.get(pk=payment.info_data.get('gift_card'))
+ ctx = {
+ 'gc': gc,
+ }
+ except GiftCard.DoesNotExist:
+ pass
+ finally:
+ return template.render(ctx)
def payment_control_render_short(self, payment: OrderPayment) -> str:
d = payment.info_data
@@ -1551,7 +1559,10 @@ class GiftCardPayment(BasePaymentProvider):
try:
gc = GiftCard.objects.get(pk=payment.info_data.get('gift_card'))
except GiftCard.DoesNotExist:
- return {}
+ return {
+ **({'error': payment.info_data[
+ 'error']} if 'error' in payment.info_data else {})
+ }
return {
'gift_card': {
'id': gc.pk,
diff --git a/src/pretix/control/templates/pretixcontrol/giftcards/payment.html b/src/pretix/control/templates/pretixcontrol/giftcards/payment.html
index 8ccbb0565..ae3368707 100644
--- a/src/pretix/control/templates/pretixcontrol/giftcards/payment.html
+++ b/src/pretix/control/templates/pretixcontrol/giftcards/payment.html
@@ -3,18 +3,24 @@
- {% trans "Gift card code" %}
-
-
- {{ gc.secret }}
-
- {% if gc.issuer != request.organizer %}
-
-
- {{ gc.issuer }}
-
+ {% if gc %}
+
+ {{ gc.secret }}
+
+ {% if gc.issuer.slug != request.organizer %}
+
+
+ {{ gc.issuer }}
+
+ {% endif %}
+ {% elif gift_card_secret %}
+ {{ gift_card_secret }}
{% endif %}
- - {% trans "Issuer" %}
- - {{ gc.issuer }}
+ {% if gc %}
+ - {% trans "Issuer" %}
+ - {{ gc.issuer }}
+ {% endif %}
{% if error %}
- {% trans "Error" %}
- {{ error }}
diff --git a/src/tests/api/test_order_create.py b/src/tests/api/test_order_create.py
index a7ac6e51c..fe0377946 100644
--- a/src/tests/api/test_order_create.py
+++ b/src/tests/api/test_order_create.py
@@ -3535,14 +3535,14 @@ def test_order_create_use_gift_card_exclusive_with_payment_provider(token_client
res['use_gift_cards'] = [gc.secret]
- res_with_payment_provider=copy.deepcopy(res)
+ res_with_payment_provider = copy.deepcopy(res)
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/orders/'.format(
organizer.slug, event.slug
), format='json', data=res_with_payment_provider
)
assert resp.status_code == 400
- assert resp.json() == {"use_gift_cards":["The attribute use_gift_cards is not compatible with payment_provider or payment_info"]}
+ assert resp.json() == {"use_gift_cards": ["The attribute use_gift_cards is not compatible with payment_provider or payment_info"]}
res_with_payment_info = copy.deepcopy(res)
res_with_payment_info['payment_info'] = {"a": "b"}
@@ -3553,7 +3553,7 @@ def test_order_create_use_gift_card_exclusive_with_payment_provider(token_client
), format='json', data=res_with_payment_info
)
assert resp.status_code == 400
- assert resp.json() == {"use_gift_cards":["The attribute use_gift_cards is not compatible with payment_provider or payment_info"]}
+ assert resp.json() == {"use_gift_cards": ["The attribute use_gift_cards is not compatible with payment_provider or payment_info"]}
@pytest.mark.django_db
@@ -3584,3 +3584,38 @@ def test_order_create_use_gift_card_repeated(token_client, organizer, event, ite
)
assert resp.status_code == 400
assert resp.json() == {'use_gift_cards': ['Multiple copies of the same gift card secret are not allowed']}
+
+
+@pytest.mark.django_db
+def test_order_create_use_gift_card_invalid_secret(token_client, organizer, event, item, quota, question):
+ res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
+ res['positions'][0]['item'] = item.pk
+ res['positions'][0]['answers'][0]['question'] = question.pk
+ with scopes_disabled():
+ customer = organizer.customers.create()
+
+ res['customer'] = customer.identifier
+ res['api_meta'] = {
+ 'test': 1
+ }
+ del res['payment_provider']
+
+ gc_enough_eur = GiftCard.objects.create(issuer=organizer, currency='EUR')
+ gc_enough_eur.transactions.create(value=Decimal("100.00"),
+ acceptor=organizer).save()
+
+ res['use_gift_cards'] = ["INVALID", gc_enough_eur.secret]
+
+ resp = token_client.post(
+ '/api/v1/organizers/{}/events/{}/orders/'.format(
+ organizer.slug, event.slug
+ ), format='json', data=res
+ )
+ assert resp.status_code == 201
+
+ with scopes_disabled():
+ o = Order.objects.get(code=resp.data['code'])
+ assert o.status == Order.STATUS_PAID
+ assert o.payments.count() == 2
+ assert o.payments.all()[0].state == OrderPayment.PAYMENT_STATE_FAILED
+ assert o.payments.all()[1].state == OrderPayment.PAYMENT_STATE_CONFIRMED