diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index 4e6c3f5397..d5abc7c6b0 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -262,7 +262,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet): return qs - @action(detail=False, methods=['POST'], url_name='redeem', url_path='(?P[^/]+)/redeem') + @action(detail=False, methods=['POST'], url_name='redeem', url_path='(?P.*)/redeem') def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) type = self.request.data.get('type', None) or Checkin.TYPE_ENTRY diff --git a/src/tests/api/test_checkin.py b/src/tests/api/test_checkin.py index 06e0f50a6c..b0a25bb825 100644 --- a/src/tests/api/test_checkin.py +++ b/src/tests/api/test_checkin.py @@ -4,6 +4,7 @@ from decimal import Decimal from unittest import mock import pytest +from django.utils.http import urlquote from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled @@ -643,6 +644,19 @@ def test_by_secret(token_client, organizer, clist, event, order): assert resp.data['status'] == 'ok' +@pytest.mark.django_db +def test_by_secret_special_chars(token_client, organizer, clist, event, order): + with scopes_disabled(): + p = order.positions.first() + p.secret = "abc+-/==" + p.save() + resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'.format( + organizer.slug, event.slug, clist.pk, urlquote(p.secret, safe='') + ), {}, 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():