Files
fotospiel-app/docs/prp/03-api.md
2025-10-04 16:38:42 +02:00

4.7 KiB
Raw Blame History

03 — API Contract

  • Base URL: /api/v1
  • Auth
    • Tenant apps: OAuth2 Authorization Code + PKCE, refresh tokens.
    • Super Admin: session-authenticated Filament (web only).
  • Common
    • Pagination: page, per_page (max 100).
    • Errors: { error: { code, message, trace_id }, details?: {...} }.
    • Rate limits: per-tenant and per-device for tenant apps; 429 with x-rate-limit-* headers.

Key Endpoints (abridged)

  • Auth: /oauth/authorize, /oauth/token, /oauth/token/refresh.
  • Tenants (Super Admin only): list/read; no create via API for MVP.
  • Events (tenant): CRUD, publish, archive; unique by tenant_id + slug.
  • Photos (tenant): signed upload URL, create, list, moderate, feature.
  • Emotions & Tasks: list, tenant overrides; task library scoping.
  • Purchases & Ledger: create purchase intent, webhook ingest, ledger list.
  • Settings: read/update tenant theme, limits, legal page links.

Guest Polling (no WebSockets in v1)

  • GET /events/{slug}/stats — lightweight counters for Home info bar.
    • Response: { online_guests: number, tasks_solved: number, latest_photo_at: ISO8601 }.
    • Cache: Cache-Control: no-store; include ETag for conditional requests.
  • GET /events/{slug}/photos?since=<ISO8601|cursor> — incremental gallery refresh.
    • Response: { data: Photo[], next_cursor?: string, latest_photo_at: ISO8601 }.
    • Use If-None-Match or If-Modified-Since to return 304 Not Modified when unchanged.

Webhooks

  • Payment provider events, media pipeline status, and deletion callbacks. All signed with shared secret per provider.

Purchase Wizard Endpoints (Marketing Flow)

These endpoints support the frontend purchase wizard for package selection, authentication, and payment. They are web routes under /purchase/ (not /api/v1), designed for Inertia.js integration with JSON responses for AJAX/fetch calls. No tenant middleware for auth steps (pre-tenant creation); auth required for payment.

Flow Overview

  1. Package Selection: User selects package via marketing page; redirects to wizard with package ID.
  2. Auth (Login/Register): Handle user creation/login; creates tenant if registering. Returns user data and next_step ('payment' or 'success' for free packages).
  3. Payment: Create intent/order, complete via provider callback, finalize purchase (assign package, update tenant).
  4. Success: Redirect to success page; email welcome if new user.

Error Handling:

  • 422 Validation: { errors: { field: ['message'] }, message: 'Summary' } display in forms without reload.
  • 401/403: { error: 'Auth required' } show login prompt.
  • 500/Other: { error: 'Server error' } generic alert, log trace_id.
  • Non-JSON (e.g., 404): Frontend catches "unexpected end of data" and shows "Endpoint not found" or retry.

All responses: JSON only for AJAX; CSRF-protected.

Endpoints

  • POST /purchase/auth/login

    • Body: { login: string (email/username), password: string, remember?: boolean }
    • Response (200): { status: 'authenticated', user: { id, email, name, pending_purchase, email_verified }, next_step: 'payment', needs_verification: boolean }
    • Errors: 422 { errors: { login: ['Invalid credentials'] } }
  • POST /purchase/auth/register

    • Body: { username, email, password, password_confirmation, first_name, last_name, address, phone, privacy_consent: boolean, package_id?: number }
    • Response (200): { status: 'registered', user: { ... }, next_step: 'payment'|'success', needs_verification: boolean, package?: { id, name, price, type } }
    • Errors: 422 { errors: { email: ['Taken'], password: ['Too weak'] } }; creates tenant/user on success.
  • POST /purchase/stripe/intent (auth required)

    • Body: { package_id: number }
    • Response (200): { client_secret: string, payment_intent_id: string }
    • Errors: 422 { errors: { package_id: ['Invalid'] } }
  • POST /purchase/stripe/complete (auth required)

    • Body: { package_id: number, payment_intent_id: string }
    • Response (200): { status: 'completed' }
    • Errors: 422 { errors: { payment: ['Not succeeded'] } } finalizes purchase.
  • POST /purchase/paypal/order (auth required)

    • Body: { package_id: number }
    • Response (200): { order_id: string, status: 'CREATED' }
    • Errors: 422 { error: 'Order creation failed' }
  • POST /purchase/paypal/capture (auth required)

    • Body: { order_id: string, package_id: number }
    • Response (200): { status: 'captured' }
    • Errors: 422 { error: 'Capture incomplete' } finalizes purchase.
  • POST /purchase/free (auth required)

    • Body: { package_id: number }
    • Response (200): { status: 'assigned' }
    • Errors: 422 { errors: { package_id: ['Not free'] } } assigns for zero-price packages.