docs: define staff check-in flow

This commit is contained in:
2026-04-28 10:59:01 +02:00
parent 18ab0a8b99
commit 97427a0864
5 changed files with 157 additions and 16 deletions

View File

@@ -0,0 +1,29 @@
# ADR-0006: Staff Check-In with Token Validation
Date: 2026-04-28
## Status
Accepted
## Context
Confirmed reservations need a simple entrance check-in process. Staff should be able to use a mobile-friendly web page to scan a visitor's QR code or enter the token manually.
QR codes may be shown on smartphones, printed, forwarded, or photographed, so they must not expose personal data. Check-in must be restricted to authenticated staff or admin users and must prevent duplicate entrance.
## Decision
Use staff-only Django REST Framework endpoints for QR verification preview and check-in confirmation.
The QR code will contain only an opaque verification token or URL. The backend will validate the token server-side, require a confirmed reservation, reject duplicate check-ins, and create or update a `CheckIn` record with timestamp and staff user on successful confirmation.
No Celery, Redis, or separate check-in service is required.
## Consequences
- Staff can use a simple mobile web page for scanning or manual token entry.
- QR codes remain privacy-preserving because they do not contain personal data.
- Check-in decisions stay centralized in the Django backend and PostgreSQL state.
- The entrance workflow depends on backend availability at the venue.
- Duplicate check-ins can be blocked with application checks and database constraints.

View File

@@ -243,13 +243,58 @@ Status codes:
## Check-In
### Verify QR Code
Check-in endpoints are for authenticated staff or admin users. Staff use a mobile-friendly Angular page to scan the QR code with a device camera or enter the token manually.
The QR code must contain only an opaque verification token or a verification URL containing that token. The backend resolves and validates the token server-side.
### QR Verification Preview
```http
POST /api/check-ins/verify/
POST /api/check-ins/preview/
```
Validates a QR token and records check-in. This endpoint is for authenticated staff or an authenticated scanning interface.
Validates a QR token and returns a preview before staff confirms entrance. This endpoint must not create a successful check-in.
Request:
```json
{
"token": "opaque-check-in-token"
}
```
Response `200 OK`:
```json
{
"status": "valid",
"reservation_id": 123,
"performance_id": 10,
"show_title": "The Open Stage",
"starts_at": "2026-05-15T20:30:00+02:00",
"party_size": 2
}
```
Status codes:
- `200 OK`: token is valid and reservation can be checked in;
- `400 Bad Request`: token is missing or malformed;
- `401 Unauthorized`: staff authentication is missing;
- `403 Forbidden`: authenticated user cannot preview check-in;
- `404 Not Found`: token is unknown;
- `409 Conflict`: reservation is not confirmed or was already checked in;
- `410 Gone`: token is expired.
The preview response should include only the minimum information staff need to validate the party. It must not expose unnecessary reservation personal data.
### Check-In Confirmation
```http
POST /api/check-ins/confirm/
```
Validates the token again and records successful entrance.
Request:
@@ -267,7 +312,8 @@ Response `200 OK`:
"reservation_id": 123,
"performance_id": 10,
"party_size": 2,
"checked_in_at": "2026-05-15T19:55:00+02:00"
"checked_in_at": "2026-05-15T19:55:00+02:00",
"checked_in_by": 7
}
```
@@ -276,12 +322,32 @@ Status codes:
- `200 OK`: reservation checked in;
- `400 Bad Request`: token is missing or malformed;
- `401 Unauthorized`: staff authentication is missing;
- `403 Forbidden`: authenticated user cannot perform check-in;
- `403 Forbidden`: authenticated user cannot confirm check-in;
- `404 Not Found`: token is unknown;
- `409 Conflict`: reservation is not confirmed or was already checked in;
- `410 Gone`: token is expired.
The response should include only the minimum information staff need to admit the party.
Successful confirmation creates a `CheckIn` record, or updates an existing incomplete check-in record for the reservation. A reservation cannot have two successful check-ins.
Error responses should use clear machine-readable states so the staff interface can show simple messages.
Example `409 Conflict` for duplicate check-in:
```json
{
"status": "already_checked_in",
"detail": "This reservation has already been checked in."
}
```
Example `409 Conflict` for unconfirmed reservation:
```json
{
"status": "reservation_not_confirmed",
"detail": "This reservation is not confirmed."
}
```
## Administration

View File

@@ -79,19 +79,27 @@ The QR code must not contain:
## 5. Entrance Check-In
1. Staff opens the authenticated check-in interface.
2. Staff scans the visitor's QR code.
3. The frontend submits the scanned token to the backend.
4. The backend validates:
1. Staff signs in with an account that has check-in permission.
2. Staff opens a mobile-friendly web page for entrance check-in.
3. Staff scans the visitor's QR code with the device camera or enters the QR token manually.
4. The QR code provides only an opaque check-in token or verification URL.
5. The frontend submits the token to the backend for a verification preview.
6. The backend validates:
- staff authentication and permission;
- token exists and has the `check_in` purpose;
- reservation exists;
- reservation is confirmed;
- token is valid for the performance check-in window if such a window is configured;
- reservation has not already been checked in.
5. The backend creates a `CheckIn` record.
6. The backend returns a successful check-in response.
7. Staff admits the visitor party.
7. The backend returns a preview with only the minimum information needed for admission, such as performance, party size, and check-in state.
8. Staff confirms check-in in the mobile web page.
9. The backend validates the token again server-side.
10. The backend creates a `CheckIn` record, or updates an existing incomplete check-in record for the same reservation.
11. The backend stores the check-in timestamp and authenticated staff user.
12. The backend returns a successful check-in response.
13. Staff admits the visitor party.
The token remains opaque throughout the flow. The QR code must not expose visitor name, email address, phone number, notes, or other personal data.
## Duplicate Check-In
@@ -103,6 +111,21 @@ If the same QR code is scanned again:
The response should be clear enough for staff to understand that the reservation was already used.
## Check-In Failure States
Failed validation must return clear error states without creating a successful check-in.
Expected check-in failures include:
- missing or malformed token;
- unknown token;
- expired token;
- staff user is not authenticated;
- staff user does not have check-in permission;
- reservation is not confirmed;
- reservation was already checked in;
- token is not valid for the selected performance or check-in window.
## Capacity Handling
Capacity is calculated as:

View File

@@ -157,9 +157,10 @@ Suggested fields:
- `id`: internal identifier;
- `reservation`: required unique reference to `Reservation`;
- `checked_in_at`: timestamp of successful check-in;
- `checked_in_by`: optional authenticated staff user reference;
- `checked_in_by`: required authenticated staff user reference for successful check-in;
- `source`: optional source such as `qr_scan` or `manual`;
- `created_at`: creation timestamp.
- `created_at`: creation timestamp;
- `updated_at`: last update timestamp.
Relationships:
@@ -168,8 +169,14 @@ Relationships:
Rules:
- check-in is performed only by authenticated staff or admin users;
- staff use a mobile-friendly web page to scan the QR code or enter the token manually;
- the QR code contains only an opaque verification token or URL;
- the backend validates the token server-side;
- only confirmed reservations can be checked in;
- a reservation cannot be checked in twice;
- successful check-in creates a `CheckIn` record, or updates an existing incomplete check-in record for the same reservation;
- successful check-in records must include `checked_in_at` and `checked_in_by`;
- failed check-in attempts should return a clear status without changing successful check-in state;
- check-in must not expose unnecessary personal data to scanning clients.

View File

@@ -20,6 +20,7 @@ Controls:
- collect only data required to manage the reservation;
- do not expose reservation personal data through public APIs;
- do not include personal data in QR codes;
- keep check-in preview and confirmation responses limited to operational admission data;
- avoid logging request bodies from booking and confirmation endpoints;
- avoid logging raw tokens;
- restrict admin access to staff users who need it.
@@ -56,12 +57,27 @@ QR codes must not contain:
The check-in endpoint resolves the token server-side and returns only the minimum information staff need.
## Check-In Security
Check-in is performed by authenticated staff or admin users through a mobile-friendly web page.
Controls:
- require staff authentication for QR verification preview and check-in confirmation;
- allow QR scanning and manual token entry in the staff interface;
- validate every token server-side;
- require the reservation to be confirmed before check-in;
- reject duplicate check-in attempts;
- store successful check-in timestamp and staff user;
- return clear validation states without exposing unnecessary personal data;
- do not log raw QR tokens.
## Authentication and Authorization
Required controls:
- Django admin requires authenticated staff accounts;
- check-in verification requires authenticated staff or an authenticated scanning client;
- check-in verification preview and confirmation require authenticated staff or admin users;
- staff permissions should separate content management from operational check-in when practical;
- public APIs must not allow clients to set protected fields such as reservation status, token values, or check-in state.