Simplify API: keep only events, categories, billets and scan routes

Remove: orders, orders/{id}, events/{id}/orders, events/{id}/stats,
events/{id}/tickets, events/{id}/scan-stats, events/{id}/billets,
export CSV routes

Keep only 7 routes:
- POST /api/auth/login (auth)
- GET /api/events (list)
- GET /api/events/{id} (detail)
- GET /api/events/{id}/categories (categories of event)
- GET /api/categories/{id}/billets (billets of category)
- GET /api/billets/{id} (billet detail with image)
- POST /api/scan (scan ticket: accepted/refused)

API is focused on: browse events → load categories → load billets → scan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-03-23 19:09:00 +01:00
parent 9981121638
commit 8ab8efbf07

View File

@@ -56,7 +56,7 @@ class ApiDocController extends AbstractController
return [
[
'name' => 'Authentification',
'description' => 'Toutes les routes API necessitent les headers d\'authentification.',
'description' => 'Route commune aux environnements sandbox et live.',
'endpoints' => [
[
'method' => 'POST',
@@ -83,7 +83,7 @@ class ApiDocController extends AbstractController
],
[
'name' => 'Evenements',
'description' => 'Gestion des evenements de l\'organisateur authentifie.',
'description' => 'Liste et detail des evenements de l\'organisateur.',
'endpoints' => [
[
'method' => 'GET',
@@ -98,7 +98,7 @@ class ApiDocController extends AbstractController
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'array', 'example' => '[{"id": 1, "title": "Brocante 2026", "startAt": "2026-08-01T10:00:00", "endAt": "2026-08-01T18:00:00", "address": "1 rue", "city": "Paris", "isOnline": true, "isSecret": false}]'],
'data' => ['type' => 'array', 'example' => '[{"id": 1, "title": "Brocante 2026", "startAt": "2026-08-01T10:00:00", "endAt": "2026-08-01T18:00:00", "address": "1 rue de la Paix", "zipcode": "75001", "city": "Paris", "isOnline": true, "isSecret": false}]'],
'meta' => ['type' => 'object', 'example' => '{"page": 1, "limit": 20, "total": 5}'],
],
'statuses' => [
@@ -110,7 +110,7 @@ class ApiDocController extends AbstractController
'method' => 'GET',
'path' => '/api/events/{id}',
'summary' => 'Detail d\'un evenement',
'description' => 'Retourne un evenement avec ses categories et billets.',
'description' => 'Retourne les informations completes d\'un evenement.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
@@ -118,7 +118,7 @@ class ApiDocController extends AbstractController
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'object', 'example' => '{"id": 1, "title": "Brocante", "categories": [{"id": 1, "name": "General", "billets": [{"id": 1, "name": "Entree", "priceHT": 1500, "quantity": 100, "sold": 42, "type": "billet"}]}]}'],
'data' => ['type' => 'object', 'example' => '{"id": 1, "title": "Brocante 2026", "description": "Grande brocante annuelle", "startAt": "2026-08-01T10:00:00", "endAt": "2026-08-01T18:00:00", "address": "1 rue", "zipcode": "75001", "city": "Paris", "isOnline": true, "isSecret": false, "imageUrl": "https://ticket.e-cosplay.fr/uploads/events/brocante.jpg"}'],
],
'statuses' => [
200 => 'Evenement retourne',
@@ -127,11 +127,17 @@ class ApiDocController extends AbstractController
404 => 'Evenement introuvable',
],
],
],
],
[
'name' => 'Categories',
'description' => 'Categories de billets d\'un evenement.',
'endpoints' => [
[
'method' => 'GET',
'path' => '/api/events/{id}/stats',
'summary' => 'Statistiques d\'un evenement',
'description' => 'CA, nombre de commandes, billets vendus, billets scannes.',
'path' => '/api/events/{id}/categories',
'summary' => 'Liste des categories d\'un evenement',
'description' => 'Retourne les categories avec leurs dates de vente et visibilite.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
@@ -139,10 +145,10 @@ class ApiDocController extends AbstractController
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'object', 'example' => '{"totalHT": 4200.50, "nbOrders": 28, "nbTicketsSold": 42, "nbTicketsScanned": 35}'],
'data' => ['type' => 'array', 'example' => '[{"id": 1, "name": "General", "position": 0, "startAt": "2026-06-01T00:00:00", "endAt": "2026-08-01T18:00:00", "isHidden": false, "isActive": true}]'],
],
'statuses' => [
200 => 'Stats retournees',
200 => 'Categories retournees',
401 => 'Non authentifie',
403 => 'Evenement non accessible',
404 => 'Evenement introuvable',
@@ -151,88 +157,62 @@ class ApiDocController extends AbstractController
],
],
[
'name' => 'Commandes',
'description' => 'Consultation des commandes d\'un evenement.',
'name' => 'Billets',
'description' => 'Billets d\'une categorie et detail d\'un billet.',
'endpoints' => [
[
'method' => 'GET',
'path' => '/api/events/{id}/orders',
'summary' => 'Liste des commandes',
'description' => 'Retourne les commandes d\'un evenement avec filtrage optionnel par statut.',
'path' => '/api/categories/{id}/billets',
'summary' => 'Liste des billets d\'une categorie',
'description' => 'Retourne les billets d\'une categorie avec stock et quantite vendue.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
'status' => ['type' => 'string', 'required' => false, 'description' => 'Filtrer par statut : pending, paid, cancelled, refunded, partially_refunded'],
'page' => ['type' => 'int', 'required' => false, 'default' => 1, 'description' => 'Page courante'],
'limit' => ['type' => 'int', 'required' => false, 'default' => 20, 'description' => 'Nombre par page (max 100)'],
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de la categorie'],
],
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'array', 'example' => '[{"orderNumber": "2026-03-23-1", "status": "paid", "firstName": "Jean", "lastName": "Dupont", "email": "jean@test.fr", "totalHT": 15.00, "paidAt": "2026-03-23T14:30:00", "items": [{"billetName": "Entree", "quantity": 1, "unitPriceHT": 15.00}]}]'],
'meta' => ['type' => 'object', 'example' => '{"page": 1, "limit": 20, "total": 28}'],
'data' => ['type' => 'array', 'example' => '[{"id": 1, "name": "Entree", "priceHT": 1500, "quantity": 100, "sold": 42, "type": "billet", "isGeneratedBillet": true, "notBuyable": false, "position": 0}]'],
],
'statuses' => [
200 => 'Commandes retournees',
200 => 'Billets retournes',
401 => 'Non authentifie',
403 => 'Evenement non accessible',
404 => 'Evenement introuvable',
403 => 'Categorie non accessible',
404 => 'Categorie introuvable',
],
],
[
'method' => 'GET',
'path' => '/api/orders/{orderNumber}',
'summary' => 'Detail d\'une commande',
'description' => 'Retourne une commande avec ses items et tickets generes.',
'path' => '/api/billets/{id}',
'summary' => 'Detail d\'un billet',
'description' => 'Retourne toutes les informations d\'un billet : nom, prix, quantite, type, description, image, categorie, evenement.',
'headers' => $this->authHeaders(),
'params' => [
'orderNumber' => ['type' => 'string', 'required' => true, 'description' => 'Numero de commande'],
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID du billet'],
],
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'object', 'example' => '{"orderNumber": "2026-03-23-1", "status": "paid", "firstName": "Jean", "lastName": "Dupont", "email": "jean@test.fr", "totalHT": 15.00, "items": [...], "tickets": [{"reference": "ETICKET-XXXX-XXXX", "state": "valid", "firstScannedAt": null}]}'],
'data' => ['type' => 'object', 'example' => '{"id": 1, "name": "Entree VIP", "description": "Acces VIP avec boissons incluses", "priceHT": 2500, "quantity": 50, "sold": 18, "type": "billet", "isGeneratedBillet": true, "hasDefinedExit": false, "notBuyable": false, "position": 0, "imageUrl": "https://ticket.e-cosplay.fr/uploads/billets/entree-vip.jpg", "category": {"id": 3, "name": "Premium"}, "event": {"id": 1, "title": "Brocante 2026"}}'],
],
'statuses' => [
200 => 'Commande retournee',
200 => 'Billet retourne',
401 => 'Non authentifie',
403 => 'Commande non accessible',
404 => 'Commande introuvable',
403 => 'Billet non accessible',
404 => 'Billet introuvable',
],
],
],
],
[
'name' => 'Scanner',
'description' => 'Endpoints pour l\'application mobile de scan de billets.',
'description' => 'Scan de billets pour l\'application mobile.',
'endpoints' => [
[
'method' => 'GET',
'path' => '/api/events/{id}/tickets',
'summary' => 'Liste des billets generes',
'description' => 'Retourne tous les billets generes pour un evenement.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
'page' => ['type' => 'int', 'required' => false, 'default' => 1, 'description' => 'Page courante'],
'limit' => ['type' => 'int', 'required' => false, 'default' => 50, 'description' => 'Nombre par page (max 100)'],
],
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'array', 'example' => '[{"reference": "ETICKET-XXXX-XXXX", "billetName": "Entree", "state": "valid", "isInvitation": false, "firstScannedAt": null, "buyerName": "Jean Dupont"}]'],
],
'statuses' => [
200 => 'Billets retournes',
401 => 'Non authentifie',
403 => 'Evenement non accessible',
],
],
[
'method' => 'POST',
'path' => '/api/scan',
'summary' => 'Scanner un billet',
'description' => 'Decode le QR code, verifie la reference, l\'etat du billet, et le marque comme scanne si valide. Retourne "accepted" ou "refused" avec la raison du refus. Gere la sortie definitive si activee.',
'description' => 'Decode le QR code, verifie la reference et l\'etat du billet, le marque comme scanne si valide. Retourne "accepted" ou "refused" avec la raison. Gere la sortie definitive si activee.',
'headers' => $this->authHeaders(),
'params' => [],
'request' => [
@@ -258,117 +238,6 @@ class ApiDocController extends AbstractController
],
],
],
[
'method' => 'GET',
'path' => '/api/events/{id}/scan-stats',
'summary' => 'Stats de scan temps reel',
'description' => 'Nombre de billets scannes, restants, invalides et dernier scan.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
],
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'object', 'example' => '{"scanned": 35, "remaining": 7, "invalid": 2, "lastScanAt": "2026-03-23T16:45:00"}'],
],
'statuses' => [
200 => 'Stats retournees',
401 => 'Non authentifie',
403 => 'Evenement non accessible',
],
],
],
],
[
'name' => 'Billets & Stock',
'description' => 'Gestion des billets et du stock.',
'endpoints' => [
[
'method' => 'GET',
'path' => '/api/events/{id}/billets',
'summary' => 'Liste des billets avec stock',
'description' => 'Retourne les billets d\'un evenement avec quantite disponible et vendue.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
],
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'array', 'example' => '[{"id": 1, "name": "Entree", "priceHT": 1500, "quantity": 100, "sold": 42, "type": "billet", "isGeneratedBillet": true}]'],
],
'statuses' => [
200 => 'Billets retournes',
401 => 'Non authentifie',
403 => 'Evenement non accessible',
],
],
[
'method' => 'GET',
'path' => '/api/billets/{id}',
'summary' => 'Detail d\'un billet',
'description' => 'Retourne toutes les informations d\'un billet : nom, prix, quantite, type, description, image, categorie, etc.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID du billet'],
],
'request' => null,
'response' => [
'success' => ['type' => 'bool', 'example' => true],
'data' => ['type' => 'object', 'example' => '{"id": 1, "name": "Entree VIP", "description": "Acces VIP avec boissons incluses", "priceHT": 2500, "quantity": 50, "sold": 18, "type": "billet", "isGeneratedBillet": true, "hasDefinedExit": false, "notBuyable": false, "position": 0, "imageUrl": "https://ticket.e-cosplay.fr/uploads/billets/entree-vip.jpg", "category": {"id": 3, "name": "Premium"}, "event": {"id": 1, "title": "Brocante 2026"}}'],
],
'statuses' => [
200 => 'Billet retourne',
401 => 'Non authentifie',
403 => 'Billet non accessible',
404 => 'Billet introuvable',
],
],
],
],
[
'name' => 'Export',
'description' => 'Export de donnees en CSV.',
'endpoints' => [
[
'method' => 'GET',
'path' => '/api/events/{id}/export/orders.csv',
'summary' => 'Export CSV des commandes',
'description' => 'Telecharge un fichier CSV avec toutes les commandes payees de l\'evenement.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
],
'request' => null,
'response' => [
'file' => ['type' => 'text/csv', 'example' => 'Commande;Date;Acheteur;Email;Billets;Total HT'],
],
'statuses' => [
200 => 'Fichier CSV retourne',
401 => 'Non authentifie',
403 => 'Evenement non accessible',
],
],
[
'method' => 'GET',
'path' => '/api/events/{id}/export/tickets.csv',
'summary' => 'Export CSV des billets scannes',
'description' => 'Telecharge un fichier CSV avec tous les billets generes et leur etat de scan.',
'headers' => $this->authHeaders(),
'params' => [
'id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement'],
],
'request' => null,
'response' => [
'file' => ['type' => 'text/csv', 'example' => 'Reference;Billet;Acheteur;Etat;Scanne le'],
],
'statuses' => [
200 => 'Fichier CSV retourne',
401 => 'Non authentifie',
403 => 'Evenement non accessible',
],
],
],
],
];