683 lines
27 KiB
JSON
683 lines
27 KiB
JSON
{
|
|
"openapi": "3.0.3",
|
|
"info": {
|
|
"title": "Tri-Hub Ernährungsberatung & Booking API",
|
|
"description": "API-Spezifikation für die Angebotsübersicht (US2.1), die Paket-Detailansicht und den Buchungsservice (US2.4 Paket buchen).",
|
|
"version": "1.1.0"
|
|
},
|
|
"servers": [
|
|
{
|
|
"url": "http://localhost:3000/api",
|
|
"description": "Lokaler Node.js Entwicklungsserver"
|
|
}
|
|
],
|
|
"paths": {
|
|
"/packages": {
|
|
"get": {
|
|
"summary": "Alle Ernährungs-Pakete abrufen (Kurzübersicht)",
|
|
"description": "Liefert die Basisdaten aus packages.js für die Angebotsübersicht (US2.1).",
|
|
"tags": ["Packages"],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Erfolgreiche Rückgabe der Paketliste",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/PackageOverview"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/packages/{id}": {
|
|
"get": {
|
|
"summary": "Detaillierte Paketinformationen für Detailseite & Bestellprozess abrufen",
|
|
"description": "Liefert die ausführlichen Paketdaten aus packagesdetails.js (inkl. Preis, Laufzeit, Leistungen und Varianten), um sie auf der Detailseite und in der Bestellzusammenfassung des Booking-Service anzuzeigen.",
|
|
"tags": ["Packages", "Booking"],
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"description": "Eindeutige ID des Pakets (z. B. ernaehrung-starter)",
|
|
"schema": {
|
|
"type": "string",
|
|
"example": "ernaehrung-standard"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Detaillierte Paketdaten erfolgreich geladen",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PackageDetail"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Paket mit dieser ID wurde nicht gefunden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/bookings/preview": {
|
|
"post": {
|
|
"summary": "Preis- und Leistungsübersicht für den Bestellprozess berechnen",
|
|
"description": "Berechnet die Bestellübersicht aus dem serverseitigen Paketkatalog. Preisangaben aus der Anfrage werden ignoriert. Ohne Varianten-ID wird bei Paketen mit Varianten die erste Variante gewählt. Es wird keine Buchung angelegt.",
|
|
"tags": ["Booking"],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BookingPreviewRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Berechnete Bestellübersicht für das Checkout-Formular",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BookingPreviewResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Ungültige Paket- oder Varianten-ID",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/bookings": {
|
|
"post": {
|
|
"summary": "Paket verbindlich buchen (US2.4)",
|
|
"description": "Speichert Paket, gewählte Variante, Leistungen und Preise zum Buchungszeitpunkt und startet den Bestätigungsversand. Der aktuelle Ablauf simuliert die Bestellung ohne Zahlung.",
|
|
"tags": ["Booking"],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BookingCreateRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"201": {
|
|
"description": "Buchung erfolgreich angelegt (Bestellbestätigung)",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BookingConfirmation"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Fehlende oder ungültige Eingabedaten",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"200": {
|
|
"description": "Bereits gespeicherte Buchung; keine zweite Buchung oder Mail",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BookingConfirmation"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"202": {
|
|
"description": "Buchung gespeichert; Versand ausstehend, fehlgeschlagen oder unklar",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/BookingConfirmation"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Anfrageschlüssel wurde mit anderen Buchungsdaten verwendet",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"413": {
|
|
"description": "Anfrage größer als 8 KiB",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"415": {
|
|
"description": "JSON-Content-Type erforderlich",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"503": {
|
|
"description": "Speicherung oder Statusprüfung nicht verfügbar",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"parameters": [
|
|
{
|
|
"name": "Idempotency-Key",
|
|
"in": "header",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string",
|
|
"format": "uuid"
|
|
},
|
|
"description": "UUID v4; bei Wiederholung denselben Schlüssel und dieselben Buchungsdaten verwenden."
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"components": {
|
|
"schemas": {
|
|
"PackageOverview": {
|
|
"type": "object",
|
|
"required": [
|
|
"id",
|
|
"type",
|
|
"title",
|
|
"description",
|
|
"fokus",
|
|
"anamnese",
|
|
"begleitung",
|
|
"buttonText"
|
|
],
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"example": "ernaehrung-standard"
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"example": "STANDARD"
|
|
},
|
|
"title": {
|
|
"type": "string",
|
|
"example": "Für Best Ager mit ersten Erfahrungen und klaren Zielen"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"fokus": {
|
|
"type": "string"
|
|
},
|
|
"anamnese": {
|
|
"type": "string"
|
|
},
|
|
"begleitung": {
|
|
"type": "string"
|
|
},
|
|
"buttonText": {
|
|
"type": "string",
|
|
"example": "Standard-Paket ansehen"
|
|
},
|
|
"highlight": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"example": "BESONDERS PASSEND"
|
|
}
|
|
}
|
|
},
|
|
"PackageVariant": {
|
|
"type": "object",
|
|
"properties": {
|
|
"variantId": {
|
|
"type": "string",
|
|
"example": "ernaehrung-premium-24"
|
|
},
|
|
"label": {
|
|
"type": "string",
|
|
"example": "24-Wochen-Variante"
|
|
},
|
|
"durationWeeks": {
|
|
"type": "integer",
|
|
"example": 24
|
|
},
|
|
"price": {
|
|
"type": "number",
|
|
"format": "float",
|
|
"example": 799.0,
|
|
"description": "Bruttopreis der Variante inklusive MwSt."
|
|
}
|
|
}
|
|
},
|
|
"PackageDetail": {
|
|
"type": "object",
|
|
"required": [
|
|
"id",
|
|
"type",
|
|
"title",
|
|
"subtitle",
|
|
"durationWeeks",
|
|
"price",
|
|
"currency",
|
|
"taxRate",
|
|
"includedFeatures"
|
|
],
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"example": "ernaehrung-standard"
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"example": "STANDARD"
|
|
},
|
|
"badge": {
|
|
"type": "string",
|
|
"example": "BESONDERS PASSEND"
|
|
},
|
|
"title": {
|
|
"type": "string",
|
|
"example": "Für Best Ager mit ersten Erfahrungen und klaren Zielen"
|
|
},
|
|
"subtitle": {
|
|
"type": "string"
|
|
},
|
|
"durationWeeks": {
|
|
"type": "integer",
|
|
"example": 24
|
|
},
|
|
"durationLabel": {
|
|
"type": "string",
|
|
"example": "24 Wochen Begleitung"
|
|
},
|
|
"price": {
|
|
"type": "number",
|
|
"format": "float",
|
|
"example": 449.0,
|
|
"description": "Bruttopreis inklusive der angegebenen MwSt."
|
|
},
|
|
"currency": {
|
|
"type": "string",
|
|
"example": "EUR"
|
|
},
|
|
"billingInterval": {
|
|
"type": "string",
|
|
"example": "einmalig"
|
|
},
|
|
"taxRate": {
|
|
"type": "integer",
|
|
"example": 19
|
|
},
|
|
"summaryForBooking": {
|
|
"type": "string",
|
|
"example": "Standard-Paket Sporternährung (24 Wochen Coaching)"
|
|
},
|
|
"targetGroup": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"includedFeatures": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"processSteps": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"step": {
|
|
"type": "string"
|
|
},
|
|
"text": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"variants": {
|
|
"type": "array",
|
|
"nullable": true,
|
|
"items": {
|
|
"$ref": "#/components/schemas/PackageVariant"
|
|
}
|
|
},
|
|
"ctaButtonText": {
|
|
"type": "string",
|
|
"example": "Standard-Paket jetzt buchen"
|
|
}
|
|
}
|
|
},
|
|
"BookingPreviewRequest": {
|
|
"type": "object",
|
|
"required": ["packageId"],
|
|
"properties": {
|
|
"packageId": {
|
|
"type": "string",
|
|
"example": "ernaehrung-premium"
|
|
},
|
|
"variantId": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"example": "ernaehrung-premium-52"
|
|
}
|
|
}
|
|
},
|
|
"BookingPreviewResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"packageId": {
|
|
"type": "string",
|
|
"example": "ernaehrung-premium"
|
|
},
|
|
"variantId": {
|
|
"type": "string",
|
|
"example": "ernaehrung-premium-52",
|
|
"nullable": true
|
|
},
|
|
"title": {
|
|
"type": "string",
|
|
"example": "Für maximale Individualität und intensive 1:1-Begleitung"
|
|
},
|
|
"summaryForBooking": {
|
|
"type": "string"
|
|
},
|
|
"durationWeeks": {
|
|
"type": "integer",
|
|
"example": 52
|
|
},
|
|
"netPrice": {
|
|
"type": "number",
|
|
"format": "float",
|
|
"example": 1175.63
|
|
},
|
|
"taxAmount": {
|
|
"type": "number",
|
|
"format": "float",
|
|
"example": 223.37
|
|
},
|
|
"grossPrice": {
|
|
"type": "number",
|
|
"format": "float",
|
|
"example": 1399.0
|
|
},
|
|
"currency": {
|
|
"type": "string",
|
|
"example": "EUR"
|
|
},
|
|
"variantLabel": {
|
|
"type": "string",
|
|
"nullable": true
|
|
},
|
|
"type": {
|
|
"type": "string"
|
|
},
|
|
"subtitle": {
|
|
"type": "string"
|
|
},
|
|
"durationLabel": {
|
|
"type": "string"
|
|
},
|
|
"quantity": {
|
|
"type": "integer",
|
|
"enum": [1]
|
|
},
|
|
"billingInterval": {
|
|
"type": "string",
|
|
"example": "einmalig"
|
|
},
|
|
"taxRate": {
|
|
"type": "number",
|
|
"example": 19
|
|
},
|
|
"includedFeatures": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"processSteps": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"step": {
|
|
"type": "string"
|
|
},
|
|
"text": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"required": [
|
|
"packageId",
|
|
"variantId",
|
|
"title",
|
|
"summaryForBooking",
|
|
"durationWeeks",
|
|
"netPrice",
|
|
"taxAmount",
|
|
"grossPrice",
|
|
"currency",
|
|
"variantLabel",
|
|
"type",
|
|
"subtitle",
|
|
"durationLabel",
|
|
"quantity",
|
|
"billingInterval",
|
|
"taxRate",
|
|
"includedFeatures",
|
|
"processSteps"
|
|
]
|
|
},
|
|
"BookingCreateRequest": {
|
|
"type": "object",
|
|
"required": ["packageId"],
|
|
"properties": {
|
|
"packageId": {
|
|
"type": "string",
|
|
"example": "ernaehrung-standard"
|
|
},
|
|
"variantId": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"example": "ernaehrung-premium-52"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 120,
|
|
"example": "Thomas Müller"
|
|
},
|
|
"email": {
|
|
"type": "string",
|
|
"format": "email",
|
|
"maxLength": 254,
|
|
"example": "thomas.mueller@example.de"
|
|
},
|
|
"phone": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"description": "Optionale Telefonnummer: deutsche Nummer mit Vorwahl oder internationale Nummer mit Ländervorwahl. Prüfung des Nummernformats, keine Bestätigung der Erreichbarkeit. Bei customer gilt customer.phone.",
|
|
"example": "+49 170 1234567"
|
|
},
|
|
"customer": {
|
|
"type": "object",
|
|
"required": ["firstName", "lastName", "email"],
|
|
"properties": {
|
|
"firstName": {
|
|
"type": "string",
|
|
"example": "Thomas"
|
|
},
|
|
"lastName": {
|
|
"type": "string",
|
|
"example": "Müller"
|
|
},
|
|
"email": {
|
|
"type": "string",
|
|
"format": "email",
|
|
"example": "thomas.mueller@example.de"
|
|
},
|
|
"phone": {
|
|
"type": "string",
|
|
"maxLength": 50,
|
|
"description": "Optional. Deutsche Nummer mit Vorwahl oder internationale Nummer mit Ländervorwahl; keine Bestätigung der Erreichbarkeit.",
|
|
"example": "+49 170 1234567"
|
|
},
|
|
"ageGroup": {
|
|
"type": "string",
|
|
"example": "50-59"
|
|
},
|
|
"notes": {
|
|
"type": "string",
|
|
"example": "Vorbereitung auf meine erste Mitteldistanz im August."
|
|
}
|
|
}
|
|
},
|
|
"acceptedTerms": {
|
|
"type": "boolean",
|
|
"example": true,
|
|
"enum": [true]
|
|
}
|
|
},
|
|
"anyOf": [
|
|
{
|
|
"required": ["customer", "acceptedTerms"]
|
|
},
|
|
{
|
|
"required": ["name", "email"]
|
|
}
|
|
],
|
|
"description": "Kundendaten gemäß customer mit acceptedTerms=true. Bestehende Clients können alternativ name und email sowie optional phone übergeben."
|
|
},
|
|
"BookingConfirmation": {
|
|
"type": "object",
|
|
"properties": {
|
|
"bookingId": {
|
|
"type": "string",
|
|
"example": "TH-000001"
|
|
},
|
|
"status": {
|
|
"type": "string",
|
|
"example": "CONFIRMED"
|
|
},
|
|
"createdAt": {
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
"bookedPackage": {
|
|
"allOf": [
|
|
{
|
|
"$ref": "#/components/schemas/BookingPreviewResponse"
|
|
}
|
|
],
|
|
"nullable": true,
|
|
"description": "Gespeicherter Bestellstand; bei älteren Buchungen ohne diese Daten null."
|
|
},
|
|
"customerEmail": {
|
|
"type": "string",
|
|
"example": "thomas.mueller@example.de"
|
|
},
|
|
"packageId": {
|
|
"type": "string"
|
|
},
|
|
"packageName": {
|
|
"type": "string"
|
|
},
|
|
"saved": {
|
|
"type": "boolean"
|
|
},
|
|
"emailStatus": {
|
|
"type": "string",
|
|
"enum": ["pending", "accepted", "failed", "unknown"],
|
|
"description": "accepted bedeutet SMTP-Annahme, keine bestätigte Zustellung."
|
|
},
|
|
"replayed": {
|
|
"type": "boolean"
|
|
}
|
|
}
|
|
},
|
|
"ErrorResponse": {
|
|
"type": "object",
|
|
"required": ["error"],
|
|
"properties": {
|
|
"error": {
|
|
"type": "object",
|
|
"required": ["code", "message"],
|
|
"properties": {
|
|
"code": {
|
|
"type": "string",
|
|
"example": "INVALID_VARIANT"
|
|
},
|
|
"message": {
|
|
"type": "string"
|
|
},
|
|
"fields": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|