diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index e246df59ca..a3e5e83849 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -430,7 +430,13 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet): if self.kwargs['pk'].isnumeric(): op = queryset.get(Q(pk=self.kwargs['pk']) | Q(secret=self.kwargs['pk'])) else: - op = queryset.get(secret=self.kwargs['pk']) + # In application/x-www-form-urlencoded, you can encodes space ' ' with '+' instead of '%20'. + # `id`, however, is part of a path where this technically is not allowed. Old versions of our + # scan apps still do it, so we try work around it! + try: + op = queryset.get(secret=self.kwargs['pk']) + except OrderPosition.DoesNotExist: + op = queryset.get(secret=self.kwargs['pk'].replace('+', ' ')) except OrderPosition.DoesNotExist: revoked_matches = list(self.request.event.revoked_secrets.filter(secret=self.kwargs['pk'])) if len(revoked_matches) == 0: diff --git a/src/tests/api/test_checkin.py b/src/tests/api/test_checkin.py index bfe71c0f2e..d9d47eeaf0 100644 --- a/src/tests/api/test_checkin.py +++ b/src/tests/api/test_checkin.py @@ -687,6 +687,19 @@ def test_by_secret_special_chars(token_client, organizer, clist, event, order): assert resp.data['status'] == 'ok' +@pytest.mark.django_db +def test_by_secret_special_chars_space_fallback(token_client, organizer, clist, event, order): + with scopes_disabled(): + p = order.positions.first() + p.secret = "foo bar" + p.save() + resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'.format( + organizer.slug, event.slug, clist.pk, "foo+bar" + ), {}, format='json') + assert resp.status_code == 201 + assert resp.data['status'] == 'ok' + + @pytest.mark.django_db def test_only_once(token_client, organizer, clist, event, order): with scopes_disabled():