Booking System - Combined API (1.0.0)

Download OpenAPI specification:

Unified API documentation for all booking system services

Booking Orchestrator

Main orchestration service that coordinates authentication, bookings, and course management

Initiate OAuth login

Initiates the OAuth authentication flow by redirecting the user's browser to the OAuth adapter service.

Authentication Flow Entry Point: This is the starting point for user authentication. The browser is redirected to the OAuth adapter, which handles the actual OAuth flow with the external provider (Google).

Provider Validation: Currently only 'google' is supported as a provider. Any other provider value returns 400 Bad Request.

Redirect Target: The user is redirected to: {OAUTH_ADAPTER_SERVICE_URL}/api/v1/oauth/{provider}/login

Next Steps: After successful authentication with the provider, the OAuth adapter will redirect the user back to the /api/v1/auth/complete endpoint with a nonce parameter.

path Parameters
provider
required
string
Value: "google"

OAuth provider name (currently only 'google' is supported)

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Complete OAuth login

Finalizes the OAuth authentication flow after the OAuth adapter redirects the user back with a verified assertion.

Completion Flow:

  1. Validates the nonce parameter is present
  2. Retrieves the pending assertion from temporary storage using the nonce
  3. Extracts user information (userId, picture, frontendRedirectUrl)
  4. Generates new access and refresh JWT tokens
  5. Stores the refresh token JTI in an allowlist for validation
  6. Sets httpOnly cookies for both tokens
  7. Redirects user to the frontend application

Token Details:

  • Access token: Short-lived (default 15 minutes), used for API authentication
  • Refresh token: Long-lived (default 7 days), used only for token refresh, restricted to /auth/refresh path

Security: The nonce serves as a one-time use token that links the OAuth callback to the original login request. Once used, the pending assertion is removed from storage.

Cookie Configuration:

  • httpOnly: true (prevents JavaScript access)
  • secure: false (demo environment)
  • sameSite: lax (CSRF protection)

Error Scenarios:

  • 400: Missing or invalid nonce, or no pending assertion found (expired or already used)
query Parameters
nonce
required
string

Unique nonce from the OAuth flow

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Logout user

Logs out the user by revoking their refresh token and clearing authentication cookies.

Logout Process:

  1. Reads the refresh_token cookie
  2. If present and valid, extracts the JTI and revokes it from the allowlist
  3. Clears both access_token and refresh_token cookies
  4. Returns 204 No Content

Token Revocation: Revoking the refresh token JTI prevents it from being used to obtain new access tokens, effectively invalidating the user's session across all clients using that refresh token.

Cookie Clearing: Both cookies are cleared from the client's browser, ensuring the access token cannot be used for subsequent requests.

Graceful Handling: If the refresh token is invalid or missing, the endpoint still clears the cookies and returns success. This ensures logout always succeeds from the user's perspective.

Response: Returns 204 No Content with cleared cookies in Set-Cookie headers.

Responses

Refresh access token

Exchanges a valid refresh token for a new access token and refresh token pair, implementing token rotation for enhanced security.

Token Rotation Flow:

  1. Validates refresh_token cookie is present (401 if missing)
  2. Verifies the JWT signature and expiration
  3. Confirms the token type is 'refresh' (403 if not)
  4. Checks the token's JTI is in the allowlist and matches the user (401 if not)
  5. Revokes the old refresh token JTI
  6. Generates new access and refresh tokens with new JTI
  7. Adds new refresh token JTI to allowlist
  8. Sets new cookies with updated tokens
  9. Returns 204 No Content

Security - Token Rotation: Each refresh operation invalidates the old refresh token and issues a new one. This limits the window of vulnerability if a refresh token is compromised.

Allowlist Validation: The refresh token's JTI must be in the allowlist and associated with the correct user. This enables immediate revocation (e.g., on logout) and prevents use of old or stolen tokens.

Cookie Updates: Both access_token and refresh_token cookies are updated with new values and reset expiration times.

Error Scenarios:

  • 401: No refresh token provided, invalid/expired token, or JTI not in allowlist
  • 403: Token is not a refresh token type

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Get authenticated user info

Returns basic profile information for the currently authenticated user extracted from the access token.

Authentication Required: Requires valid access_token cookie. The authentication middleware validates the token and populates req.user.

Response Data:

  • authenticated: Always true (endpoint only accessible with valid token)
  • userId: Unique user identifier from OAuth provider (format: 'provider:subject')
  • picture: User's profile picture URL from OAuth provider

Token Source: All data is extracted from the JWT access token claims, no additional service calls required.

Use Cases:

  • Display user profile information in UI
  • Verify authentication status
  • Retrieve user picture for avatar display

Error Scenarios:

  • 401: No access token or invalid/expired token
Authorizations:
None

Responses

Response samples

Content type
application/json
{}

Internal OAuth assertion endpoint

Internal service-to-service endpoint used by OAuth adapters to submit verified user assertions after successful OAuth authentication. Not intended for direct client use.

Architecture Role: This endpoint is called by the OAuth adapter service after it successfully authenticates a user with an external OAuth provider (Google). It bridges the OAuth adapter and the main orchestrator.

Assertion Validation:

  1. Validates all required fields are present (provider, sub, nonce, signature)
  2. Verifies the assertion signature using shared secret
  3. Checks the nonce hasn't been used before (replay protection)
  4. Validates the issued-at time is recent (prevents old assertions)

Assertion Storage: Stores the validated assertion temporarily in-memory with the nonce as key, allowing the /api/v1/auth/complete endpoint to retrieve it when the user's browser is redirected back.

Security:

  • Signature verification ensures assertion authenticity
  • Nonce prevents replay attacks
  • Time validation ensures freshness
  • Temporary storage (assertions expire)

Error Scenarios:

  • 400: Missing required fields or invalid data format
  • 401: Invalid signature or assertion verification failed

Response: 204 No Content on successful assertion storage.

Request Body schema: application/json
required
provider
required
string
sub
required
string
iat
integer
nonce
required
string
signature
required
string
picture
string
frontendRedirectUrl
string

Responses

Request samples

Content type
application/json
{
  • "provider": "string",
  • "sub": "string",
  • "iat": 0,
  • "nonce": "string",
  • "signature": "string",
  • "picture": "string",
  • "frontendRedirectUrl": "string"
}

Response samples

Content type
application/json
{
  • "error": {
    }
}

Get all courses

Orchestrator endpoint that delegates to the booking-data service to retrieve the complete course catalog.

Orchestration Pattern: This endpoint acts as a simple proxy, forwarding the request to the data layer service and returning the response. No business logic or multi-service coordination occurs at this level.

Service Delegation:

  • Downstream Service: booking-data (data layer)
  • Operation: GET /api/v1/courses
  • Purpose: Retrieve all courses with slot availability

Error Handling Strategy:

  • 4xx errors: Propagated as-is from booking-data (client error passthrough)
  • 5xx errors: Translated to 500 Internal Server Error (shields downstream implementation details)
  • Network errors: Translated to 500 with generic error response

Response Structure: Standardized wrapper format: { data: { courses: [...] } }

Use Cases:

  • Public course catalog display
  • Availability checking before booking initiation
  • Course browsing without authentication

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get course by ID

Orchestrator endpoint that validates input and delegates to booking-data service for single course retrieval.

Orchestration Responsibilities:

  1. Input validation (course ID presence)
  2. Delegate to data layer service
  3. Error transformation and propagation

Service Delegation:

  • Downstream Service: booking-data (data layer)
  • Operation: GET /api/v1/courses/:id
  • Purpose: Retrieve single course with real-time slot availability

Error Handling Strategy:

  • 400 (orchestrator): Missing course ID parameter
  • 4xx (propagated): Course not found, invalid ID format from booking-data
  • 5xx: Translated to 500 to hide downstream implementation

Response Structure: Wrapped format: { data: { course: {...} } }

Use Cases:

  • Course detail page rendering
  • Pre-reservation availability verification
  • Slot selection interface data
path Parameters
id
required
string

Course ID

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get user bookings

Orchestrator endpoint that extracts authenticated user context and delegates to booking-logic service for booking retrieval.

Authentication Layer (Orchestrator Responsibility): Access token validation performed by middleware before delegation. User ID extracted from JWT claims and passed to downstream service.

Service Delegation:

  • Downstream Service: booking-logic (business layer)
  • Operation: GET /api/v1/bookings/user/:userId
  • Context Injection: userId from authenticated token

Orchestration Flow:

  1. Middleware validates access_token cookie → populates req.user
  2. Orchestrator extracts userId from req.user.id
  3. Delegates to booking-logic with userId parameter
  4. Returns serialized booking data (already transformed by business layer)

Error Translation:

  • 401 (orchestrator): Authentication failure at gateway level
  • 5xx → 502/503/504: Translates booking-logic errors to appropriate gateway error codes

Use Cases:

  • User booking history display
  • Dashboard booking list
  • Account management views
Authorizations:
None

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Start booking process

Multi-service orchestration endpoint that coordinates reservation creation with payment order initialization.

Orchestration Pattern: Sequential service coordination with conditional logic for reservation creation vs. retrieval.

Service Choreography:

  1. Input Validation (Orchestrator): Validates courseId, slotIds presence
  2. Authentication Context (Orchestrator): Extracts userId from JWT token
  3. Service Call #1 - Booking-Logic:
    • Conditional: If reservationId provided → retrieve existing reservation
    • Otherwise: Create new pending reservation with slot availability check
    • Returns: reservationId, priceToPay
  4. Service Call #2 - PayPal Adapter:
    • Create payment order with reservation details
    • Returns: orderID for frontend payment flow

Downstream Services:

  • booking-logic (business layer): Reservation validation and creation/retrieval
  • paypal-adapter (external integration): Payment order creation

Error Handling Strategy:

  • 4xx from booking-logic: Propagated as-is (client error passthrough for business rule violations)
  • 5xx from any service: Translated to 502/503/504 with context-specific error codes
  • Orchestrator errors: RESERVATION_CREATION_FAILED, PAYMENT_ORDER_FAILED

Aggregated Response: Combines data from both services:

  • reservationId (from booking-logic)
  • priceToPay (from booking-logic)
  • orderID (from paypal-adapter)

Continuation: Client proceeds to /api/v1/bookings/confirm after PayPal payment approval.

Authorizations:
None
Request Body schema: application/json
required
courseId
required
string
slotIds
required
Array of strings
reservationId
string

Optional existing reservation id to reuse instead of creating a new one

Responses

Request samples

Content type
application/json
{
  • "courseId": "695ba111b2e63b75bee66279",
  • "slotIds": [
    ],
  • "reservationId": "696prev1234567890abcdef"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Confirm booking

Complex orchestration endpoint implementing a distributed saga pattern with compensation logic for payment capture and booking confirmation.

Saga Orchestration Pattern: Coordinates a two-phase distributed transaction across payment and booking services with automatic rollback on failure.

Service Choreography:

  1. Input Validation (Orchestrator): orderID, reservationId, userId extraction
  2. Phase 1 - Payment Capture (paypal-adapter):
    • Capture authorized payment
    • Extract captureId for potential refund
    • Validate status = COMPLETED
  3. Phase 2 - Booking Creation (booking-logic):
    • Atomically create booking and complete reservation
    • Validate reservation ownership, expiration, status
  4. Success: Return created booking
  5. Compensation (if Phase 2 fails):
    • Automatically trigger refund via paypal-adapter
    • Track compensation success/failure for error reporting

Downstream Services:

  • paypal-adapter (external integration): Payment capture and refund operations
  • booking-logic (business layer): Atomic booking creation with reservation completion

Saga Compensation Logic:

  • Booking fails after capture → Refund succeeds: 409 BOOKING_FAILED_PAYMENT_REFUNDED (user not charged)
  • Booking fails after capture → Refund fails: 502 BOOKING_FAILED_REFUND_FAILED (manual intervention required)

Orchestrator Error Handling:

  • Pre-capture: 400 for validation failures, 401 for authentication
  • Capture failure: 502/503/504 PAYMENT_CAPTURE_FAILED
  • Post-capture business failures: Trigger compensation saga
  • Idempotency: Not idempotent - orchestrator does not track saga state across requests

Critical State Management: This orchestrator manages critical financial state transitions. Compensation ensures payment refunds occur when booking creation fails, preventing user charges without service delivery.

Response: Successfully created booking in serialized format (transformed by booking-logic service).

Authorizations:
None
Request Body schema: application/json
required
orderID
required
string
reservationId
required
string

Responses

Request samples

Content type
application/json
{
  • "orderID": "8VX12345AB6789012",
  • "reservationId": "6969808214d2f13caa1e5f16"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get pending reservation

Orchestrator endpoint that injects authenticated user context and delegates to booking-logic for pending reservation query.

Orchestration Responsibilities:

  1. Extract userId from authentication token
  2. Validate courseId from request body
  3. Delegate filtered query to business layer
  4. Propagate business logic errors

Service Delegation:

  • Downstream Service: booking-logic (business layer)
  • Operation: POST /api/v1/bookings/reservations/pending
  • Context Injection: userId (from token), courseId (from body)
  • Business Logic (delegated): Filtering for held, non-expired, user-owned reservations

Orchestration Flow:

  1. Orchestrator: Extract userId from JWT token via middleware
  2. Orchestrator: Validate courseId presence in request body
  3. Delegate: Pass userId + courseId to booking-logic
  4. Booking-Logic: Apply business rules (held status, not expired, ownership match)
  5. Return: Serialized reservation or 404 if none found

Error Handling Strategy:

  • 400 (orchestrator): Missing courseId validation
  • 401 (orchestrator): Authentication failure at gateway
  • 4xx (propagated): Business rule violations from booking-logic
  • 5xx → 502/503/504: Service availability errors

Use Cases:

  • Booking flow resumption after interruption
  • Duplicate reservation prevention
  • Reservation expiration countdown display
Authorizations:
None
Request Body schema: application/json
required
courseId
required
string

Responses

Request samples

Content type
application/json
{
  • "courseId": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Cancel reservation

Orchestrator endpoint that enforces authentication and delegates reservation cancellation with authorization to booking-logic service.

Orchestration Responsibilities:

  1. Path parameter validation (reservationId)
  2. Authentication context injection (userId from token)
  3. Delegate to business layer for authorization and cancellation
  4. Error propagation to client

Service Delegation Chain:

  • Orchestrator → booking-logic: Cancellation request with userId and reservationId
  • Booking-logic → booking-data: Atomic cancellation and slot restoration (if authorized)

Orchestration Flow:

  1. Orchestrator: Extract reservationId from path, userId from token
  2. Delegate to booking-logic: PATCH /api/v1/bookings/reservations/:reservationId/cancel
  3. Booking-logic: Perform authorization (ownership check) and status validation
  4. Booking-logic → booking-data: Atomic transaction for status update and slot increment
  5. Orchestrator: Propagate 4xx business errors or return 204 on success

Authorization (Delegated to Business Layer): Ownership validation performed by booking-logic service - ensures users can only cancel their own reservations. Orchestrator passes userId for authorization context.

Error Handling Strategy:

  • 400 (orchestrator): Missing reservationId parameter
  • 401 (orchestrator): Authentication failure
  • 4xx (propagated): Authorization failures, business rule violations from booking-logic
  • 5xx → 502/503/504: Service dependency errors

Success Response: 204 No Content - cancellation completed, no response body.

Authorizations:
None
path Parameters
reservationId
required
string

Reservation ID to cancel

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Booking Logic

Business logic service for managing bookings and reservations

Retrieve bookings with optional user filter

Retrieves confirmed bookings from the booking-data service with optional user filtering. Results are serialized to expose only client-relevant fields.

Behavior:

  • When userId is provided: Returns only bookings for that specific user
  • When no userId is provided: Returns all bookings in the system (typically admin use)

Data Transformation: Bookings are serialized before being returned, transforming MongoDB fields to cleaner client-facing format:

  • _id → id
  • course → courseId
  • user → userId
  • Internal fields like __v, updatedAt, and reservation are excluded

Use cases:

  • Display user's booking history
  • Administrative overview of all bookings
  • Booking confirmation pages
query Parameters
userId
string
Example: userId=648a1f4e2f8fb814c8d6f9b1

ID of the user to filter bookings

Responses

Response samples

Content type
application/json
Example
{
  • "data": {
    }
}

Create a new booking

Converts a held reservation into a confirmed booking after successful payment. Performs comprehensive validation before delegating to the booking-data service for atomic transaction execution.

Validation Flow:

  1. Validates all required fields are present (userId, reservationId, transactionId, price)
  2. Retrieves the reservation from booking-data service
  3. Verifies the reservation belongs to the requesting user (403 if mismatch)
  4. Confirms the reservation is in held status (409 if not)
  5. Checks the reservation hasn't expired (409 if expired)
  6. Delegates to booking-data service to atomically create booking and complete reservation

Transaction Safety: The actual booking creation and reservation completion happen atomically in the booking-data service's transaction, ensuring consistency.

Response: Returns the newly created booking in serialized format with clean field names and only client-relevant data.

Error Scenarios:

  • 400: Missing required fields
  • 403: Reservation doesn't belong to the user
  • 404: Reservation not found
  • 409: Reservation not in held status or has expired
  • 500: Downstream service error
Request Body schema: application/json
required
userId
required
string
reservationId
required
string
transactionId
required
string
price
required
string

Responses

Request samples

Content type
application/json
{
  • "userId": "TEST_USER_12345",
  • "reservationId": "696b690186faaabbfded290e",
  • "transactionId": "52V73452E9342521J",
  • "price": "79.00"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Create a new reservation

Creates a new slot reservation with comprehensive validation before delegating to the booking-data service for atomic slot holding.

Validation Flow:

  1. Validates request data (userId, courseId, slotIds array)
  2. Retrieves course details from booking-data service
  3. Validates all requested slot IDs exist in the course
  4. Checks each slot has available capacity (returns 409 if any slot unavailable)
  5. Verifies a pricing option exists for the number of slots selected (returns 400 if no match)
  6. Delegates to booking-data service to atomically create reservation and decrement slot availability

Reservation Expiration: The reservation is created with a configurable expiration time (default from bookingSettings). After expiration, a cleanup job will cancel the reservation and restore slot availability.

Response: Returns a simplified response containing:

  • reservationId: The unique identifier for the created reservation
  • priceToPay: The price based on the matching pricing option

Atomicity: Slot availability is decremented atomically with reservation creation in the booking-data service's transaction.

Error Scenarios:

  • 400: Invalid data or no pricing option for slot count
  • 404: Course not found
  • 409: One or more slots not available
  • 500: Downstream service error
Request Body schema: application/json
required
userId
required
string
courseId
required
string
slotIds
required
Array of strings

Responses

Request samples

Content type
application/json
{
  • "userId": "TEST_USER_12345",
  • "courseId": "695ba111b2e63b75bee66284",
  • "slotIds": [
    ]
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get reservation details by ID

Retrieves reservation details with pricing information, enforcing user authorization checks.

Authorization: The endpoint requires the userId query parameter and validates that the reservation belongs to the requesting user. Returns 403 if there's a mismatch.

Processing Flow:

  1. Validates reservation ID is provided
  2. Retrieves reservation from booking-data service
  3. Verifies reservation belongs to requesting user (403 if not)
  4. Fetches associated course details
  5. Calculates price based on number of slots in reservation

Response: Returns a simplified response with:

  • reservationId: The reservation identifier
  • priceToPay: Price calculated from course's pricing options based on slot count

Use cases:

  • Retrieve pricing before payment processing
  • Verify reservation details during booking flow
  • Check reservation status for payment confirmation

Error Scenarios:

  • 400: Missing reservation ID or no pricing option found
  • 403: Reservation doesn't belong to the requesting user
  • 404: Reservation not found
  • 500: Downstream service error
path Parameters
id
required
string
Example: 6969808214d2f13caa1e5f16

Reservation ID

query Parameters
userId
required
string
Example: userId=TEST_USER_12345

ID of the user requesting the reservation

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get active reservation for a user and course

Retrieves the most recent active reservation for a specific user and course, filtering by status and expiration.

Query Requirements: Both userId and courseId query parameters are required. Returns 400 if either is missing.

Active Reservation Criteria: A reservation is considered active if:

  1. Status is held (not completed, cancelled, or expired)
  2. Expiration timestamp is in the future (not expired)

Filtering Logic:

  1. Fetches all reservations for the user and course from booking-data service
  2. Filters client-side for active reservations (held status + not expired)
  3. Returns the most recent active reservation (last in filtered array)
  4. Returns 404 if no active reservations found

Response: Returns the full reservation object in serialized format with clean field names:

  • MongoDB _id → id
  • course → courseId
  • user → userId
  • Internal fields excluded

Use cases:

  • Check if user has pending reservation before creating a new one
  • Resume incomplete booking flow
  • Display active reservation during course selection

Error Scenarios:

  • 400: Missing userId or courseId
  • 404: No active reservation found
  • 500: Downstream service error
query Parameters
userId
required
string
Example: userId=TEST_USER_12345

ID of the user

courseId
required
string
Example: courseId=695ba111b2e63b75bee66284

ID of the course

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Cancel a reservation

Cancels a held reservation with authorization and status validation, delegating to booking-data service for atomic slot restoration.

Validation Flow:

  1. Validates reservation ID and userId are provided (400 if missing)
  2. Retrieves reservation from booking-data service
  3. Verifies reservation belongs to requesting user (403 if mismatch)
  4. Confirms reservation is in held status (409 if not)
  5. Delegates to booking-data service to atomically cancel reservation and restore slot availability

Authorization: Strict ownership validation ensures users can only cancel their own reservations. Returns 403 if the reservation's user ID doesn't match the requesting userId.

Status Validation: Only reservations in held status can be cancelled. Attempting to cancel completed, already-cancelled, or expired reservations returns 409 conflict.

Atomicity: The booking-data service handles the actual cancellation in a transaction that:

  • Updates reservation status to cancelled
  • Increments available slots by the number that were held
  • Ensures consistency even under concurrent operations

Success Response: Returns 204 No Content upon successful cancellation.

Use cases:

  • User explicitly abandons booking process
  • User selects different slots and needs to cancel previous reservation
  • Timeout handler cancels expired reservations

Error Scenarios:

  • 400: Missing reservation ID or userId
  • 403: Reservation belongs to different user
  • 404: Reservation not found
  • 409: Reservation not in held status
  • 500: Downstream service error
path Parameters
id
required
string
Example: 696b69fd86faaabbfded291e

The unique identifier of the reservation to cancel

Request Body schema: application/json
required
userId
required
string

Responses

Request samples

Content type
application/json
{
  • "userId": "string"
}

Response samples

Content type
application/json
{
  • "error": {
    }
}

Booking Data

Data persistence layer for bookings, courses, and reservations

Retrieve bookings with optional filters

Retrieves all booking records from the database, with optional filtering by user ID.

This endpoint performs a read-only query against the bookings collection. When no filters are provided, all confirmed bookings across all users are returned. Results are returned as raw MongoDB documents including all internal fields.

Filtering:

  • userId: Returns only bookings associated with the specified user ID. When provided, the query filters by the user field in the booking document.

Use cases:

  • Retrieve a user's booking history
  • Admin view of all bookings in the system
  • Audit and reporting purposes
query Parameters
userId
string

ID of the user holding the bookings

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Create a Booking atomically

Atomically creates a new confirmed booking and finalizes the associated reservation in a single database transaction.

Transaction Flow:

  1. Validates all required booking data (courseId, userId, reservationId, slots, transactionId, price)
  2. Validates that the specified reservation exists
  3. Creates a new booking document in confirmed status
  4. Updates the associated reservation status to completed
  5. Commits the transaction only if all operations succeed

Atomicity Guarantee: All database operations are executed within a MongoDB transaction. If any step fails, all changes are rolled back to maintain data consistency. This ensures that a booking is never created without properly completing its reservation.

Important:

  • The reservation must exist before creating a booking
  • Slot availability is NOT modified by this endpoint (it should have been decremented when the reservation was created)
  • The transactionId should reference a successful payment transaction
Request Body schema: application/json
required
courseId
required
string
userId
required
string
reservationId
required
string
slots
required
string
transactionId
required
string
price
required
string

Responses

Request samples

Content type
application/json
{
  • "courseId": "695ba111b2e63b75bee6627e",
  • "userId": "648a1f4e2f8fb814c8d6f9b1",
  • "reservationId": "69696655989dec9e8a6e9fed",
  • "slots": [
    ],
  • "transactionId": "05V54435V6286635A",
  • "price": "99.00"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Retrieve the list of all available courses.

Retrieves all course records stored in the database without any filtering or pagination.

Response Details: Returns complete course documents including:

  • Course metadata (name, description, open status)
  • All time slots with real-time availability counts
  • Pricing options for different slot combinations
  • MongoDB internal fields (_id, __v, createdAt, updatedAt)

Slot Availability: The available field in each slot reflects real-time availability. This value is decremented when reservations are created and incremented when reservations are cancelled or expire.

Use cases:

  • Display course catalog to users
  • Check current availability before creating reservations
  • Administrative course management

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Retrieve a single course by its id.

Retrieves detailed information for a single course identified by its MongoDB ObjectId.

Validation:

  • The ID parameter is validated as a proper MongoDB ObjectId format
  • Returns 400 if the ID format is invalid
  • Returns 404 if no course exists with the specified ID

Response Details: Returns the complete course document including:

  • Course information (name, description)
  • All available time slots with current capacity and availability
  • Pricing structure for different slot combinations
  • Timestamps and MongoDB metadata

Real-time Availability: The slot availability counts reflect the current state and are updated atomically when reservations are created or cancelled.

path Parameters
id
string
Examples:
  • 695ba111b2e63b75bee66279 -

ID of the course to retrieve.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Retrieve reservations with optional filtering.

Retrieves reservation records from the database with optional filtering by user ID and/or course ID.

Filtering Options:

  • No filters: Returns all reservations in the system regardless of status
  • userId: Returns only reservations for the specified user
  • courseId: Returns only reservations for the specified course (validates ObjectId format)
  • Both filters: Returns reservations matching both criteria (user AND course)

Reservation Statuses: Results may include reservations in any status:

  • held: Active reservation with slot availability temporarily held
  • completed: Reservation successfully converted to a confirmed booking
  • cancelled: Manually cancelled reservation with slots released
  • expired: Reservation that expired before being confirmed (may be cleaned up by cron job)

Use cases:

  • Check user's active and past reservations
  • Audit reservation history for a course
  • Monitor system-wide reservation activity
query Parameters
userId
string
Examples:
  • userId=648a1f4e2f8fb814c8d6f9b1 -

ID of the user holding the reservations

courseId
string
Examples:
  • courseId=695ba111b2e63b75bee6627e -

ID of the course associated with the reservations

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Create a reservation and hold slot availability atomically.

Atomically creates a new reservation and holds the specified slot availability within a single database transaction.

Transaction Flow:

  1. Validates the course exists
  2. Verifies all specified slot IDs exist within the course
  3. Checks that each slot has at least 1 available space
  4. Decrements the available count for each selected slot by 1
  5. Creates a new reservation in held status with calculated expiration time
  6. Commits the transaction only if all operations succeed

Atomicity Guarantee: All operations are wrapped in a MongoDB transaction. If any validation fails or if slots become unavailable between checks, the entire transaction is rolled back and no changes are persisted. This prevents race conditions and ensures data consistency.

Expiration: The reservation is created with an expiration timestamp calculated as now + expiresInMinutes. After this time, the reservation may be automatically cancelled by the cleanup cron job, which will restore the held slot availability.

Conflict Handling:

  • Returns 404 if the course doesn't exist
  • Returns 400 if any slot ID is not found in the course
  • Returns 409 if any slot has zero availability
Request Body schema: application/json
required
test
string
userId
required
string
courseId
required
string
slotIds
required
Array of strings
expiresInMinutes
required
number

Responses

Request samples

Content type
application/json
{
  • "userId": "648a1f4e2f8fb814c8d6f9b1",
  • "courseId": "695ba111b2e63b75bee66279",
  • "slotIds": [
    ],
  • "expiresInMinutes": 15
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Retrieve a reservation by its unique identifier.

Retrieves complete details for a single reservation identified by its MongoDB ObjectId.

Validation:

  • Validates the ID parameter is a properly formatted MongoDB ObjectId
  • Returns 400 for invalid ID format
  • Returns 404 if no reservation exists with the specified ID

Response: Returns the full reservation document including:

  • Associated course and user IDs
  • Array of reserved slot IDs
  • Current status (held, completed, cancelled, or expired)
  • Expiration timestamp (for held reservations)
  • Creation and modification timestamps
  • MongoDB metadata fields

Use cases:

  • Verify reservation details before payment
  • Check reservation status and expiration
  • Retrieve reservation data for booking confirmation
path Parameters
id
required
string
Examples:
  • 695d651ad97543f2ef9b5231 -

The unique identifier of the reservation to retrieve.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Cancel a reservation and restore slot availability.

Atomically cancels an existing reservation and restores slot availability within a single database transaction.

Transaction Flow:

  1. Validates the reservation ID format (MongoDB ObjectId)
  2. Retrieves the reservation document
  3. Verifies the associated course exists
  4. Validates all reserved slots still exist in the course
  5. Increments the available count for each reserved slot by 1
  6. Updates the reservation status to cancelled
  7. Commits the transaction only if all operations succeed

Atomicity Guarantee: All database operations are executed within a MongoDB transaction. If any step fails (e.g., course not found, slot mismatch), the entire transaction is rolled back and no changes are persisted.

Idempotency: This endpoint can be called on already-cancelled reservations. The operation will still validate data integrity but may not modify any documents if the status is already cancelled.

Use cases:

  • User explicitly cancels a pending reservation
  • Timeout/cleanup of abandoned reservations
  • Administrative cancellation of problematic reservations

Success Response: Returns 204 No Content with no response body upon successful cancellation.

path Parameters
id
required
string
Examples:
  • 695d651ad97543f2ef9b5231 -

The unique identifier of the reservation to cancel.

Responses

Response samples

Content type
application/json
null

Clean up expired held reservations.

Batch cleanup job that identifies and cancels expired reservations in held status, restoring their slot availability.

Cleanup Process:

  1. Queries for reservations with status=held and expiration timestamp < current time
  2. Limits results to a configured batch size to prevent overwhelming the database
  3. For each expired reservation, executes a cancellation transaction:
    • Validates the associated course exists
    • Verifies all reserved slots exist
    • Increments slot availability counters
    • Updates reservation status to cancelled
  4. Tracks success/failure counts for each reservation processed

Transaction Isolation: Each reservation is cancelled in its own separate transaction. If one reservation fails to clean up, others in the batch continue processing. This prevents a single failure from blocking the entire cleanup job.

Error Handling: Errors during individual reservation cleanup are caught and logged, with the reservation counted as failed. The endpoint continues processing remaining reservations in the batch.

Response: Returns statistics about the cleanup operation:

  • processed: Total number of expired reservations found and attempted
  • cleaned: Number successfully cancelled with slots restored
  • failed: Number that encountered errors during cancellation

Intended Usage: This endpoint should be called periodically by a cron job or scheduled task to ensure expired reservations don't permanently hold slot availability.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

OAuth Adapter

OAuth authentication adapter service

Initiate Google OAuth login

Adapter entry point that initiates the OAuth 2.0 authorization code flow by redirecting the user's browser to Google's consent screen.

Adapter Pattern Role: Adapts internal authentication request to Google's OAuth 2.0 protocol requirements.

External Service Integration:

  • Provider: Google OAuth 2.0
  • Protocol: OAuth 2.0 Authorization Code Flow
  • Redirect Target: Google OAuth authorization endpoint

Adapter Responsibilities:

  1. Generate OAuth state parameter for CSRF protection
  2. Construct Google authorization URL with required parameters (client_id, redirect_uri, scope, state)
  3. Redirect user's browser to Google consent screen

OAuth Parameters:

  • scope: profile, email (requests user profile and email access)
  • response_type: code (authorization code flow)
  • redirect_uri: This adapter's callback endpoint

Flow Continuation: After user consent, Google redirects back to /api/v1/oauth/google/callback with authorization code.

Responses

Handle Google OAuth callback

Adapter callback endpoint that translates Google's OAuth 2.0 response into an internal authentication assertion for the orchestrator service.

Adapter Pattern Role: Protocol translation layer that converts external OAuth provider response into internal authentication format, isolating the orchestrator from Google-specific OAuth implementation details.

External Service Integration:

  • Provider: Google OAuth 2.0 Token Exchange and UserInfo API
  • Operations: Exchange authorization code for access token, retrieve user profile

Adapter Processing Flow:

  1. Validate callback: Verify state parameter for CSRF protection
  2. Token exchange: POST to Google token endpoint with authorization code → receive access token
  3. Profile retrieval: GET Google UserInfo API with access token → extract user profile (sub, picture)
  4. Protocol translation: Convert OAuth response to internal assertion format
  5. Assertion creation: Generate nonce, create HMAC-SHA256 signature, package assertion
  6. Service notification: POST assertion to orchestrator's /api/v1/auth/internal/assert endpoint
  7. Browser redirect: Redirect user to orchestrator's /api/v1/auth/complete?nonce={nonce}

Assertion Structure (Internal Protocol):

  • provider: 'google'
  • sub: Google user identifier
  • iat: Issued-at timestamp
  • nonce: UUID for session correlation
  • signature: HMAC-SHA256(provider:sub:iat:nonce, shared_secret)
  • picture: User profile picture URL
  • frontendRedirectUrl: Final redirect destination

Error Handling:

  • OAuth errors from Google are logged and return 500
  • Assertion delivery failures return 500 CREDENTIAL_ASSERTION_FAILED

Security: Signature verification ensures assertion authenticity when orchestrator retrieves it. The adapter acts as a trusted intermediary between Google and the internal system.

query Parameters
code
required
string

Authorization code from Google OAuth

state
string

State parameter for CSRF protection

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

PayPal Adapter

PayPal payment integration service

Create a PayPal order

Adapter endpoint that translates internal reservation payment request into PayPal Orders API v2 format.

Adapter Pattern Role: Data model translation layer that converts internal reservation and pricing representation to PayPal-specific order structure, isolating orchestrator from PayPal API changes.

External Service Integration:

  • Provider: PayPal Orders API v2
  • Operation: POST /v2/checkout/orders
  • Authentication: OAuth 2.0 client credentials flow with PayPal

Adapter Translation:

  1. Input (internal model): reservationId, priceToPay (EUR string)
  2. Translation: Map to PayPal order structure with purchase_units, amount, currency_code
  3. PayPal API Call: Create order with intent=CAPTURE
  4. Output (internal model): orderID

PayPal Order Configuration:

  • intent: CAPTURE (immediate payment capture after approval)
  • currency: EUR (hardcoded in adapter)
  • purchase_unit: Single item with reservation price

Error Handling:

  • 400: Missing or invalid input parameters (adapter validation)
  • 500: PayPal API errors translated to PAYPAL_ORDER_CREATION_FAILED

Use Case: Called by orchestrator during booking initiation to prepare payment infrastructure before user payment approval.

Request Body schema: application/json
required
reservationId
required
string

The reservation ID to associate with this payment

priceToPay
required
string

The amount to charge in EUR (must be a string with 2 decimals)

Responses

Request samples

Content type
application/json
{
  • "reservationId": "6969808214d2f13caa1e5f16",
  • "priceToPay": "49.00"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Capture a PayPal order

Adapter endpoint that executes payment capture via PayPal API and translates capture response to internal format.

Adapter Pattern Role: Operation translation layer that adapts PayPal's capture operation and response structure to internal payment confirmation model.

External Service Integration:

  • Provider: PayPal Orders API v2
  • Operation: POST /v2/checkout/orders/{orderID}/capture
  • Purpose: Finalize payment and move funds from customer to merchant

Adapter Translation Flow:

  1. Input (internal): orderID (from previous create operation)
  2. PayPal API Call: Execute capture on approved order
  3. Response parsing: Extract capture details from nested PayPal response structure
  4. Output (internal): Simplified capture model (status, captureId, pricePaid)

PayPal Response Extraction: Navigates complex PayPal response: order.purchase_units[0].payments.captures[0] → extracts id, status, amount.value

Status Translation:

  • COMPLETED: Payment successfully captured
  • Other statuses returned as-is for orchestrator evaluation

Error Handling:

  • 400: Missing orderID or invalid format
  • 500: PayPal API errors (order not approved, already captured, etc.) → PAYPAL_ORDER_CAPTURE_FAILED

Critical Operation: This endpoint executes the financial transaction. Called by orchestrator after user approves payment in frontend PayPal UI.

Request Body schema: application/json
required
orderID
required
string

The PayPal order ID to capture

Responses

Request samples

Content type
application/json
{
  • "orderID": "8VX12345AB6789012"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Refund a captured payment

Adapter endpoint that executes payment refund via PayPal API as part of distributed saga compensation logic.

Adapter Pattern Role: Compensation operation adapter that translates internal refund request to PayPal Payments API refund operation.

External Service Integration:

  • Provider: PayPal Payments API v2
  • Operation: POST /v2/payments/captures/{captureId}/refund
  • Purpose: Return funds to customer (compensation for failed booking)

Adapter Translation Flow:

  1. Input (internal): captureId (from previous capture), reservationId (for logging/tracking)
  2. PayPal API Call: Execute full refund on captured payment
  3. Response parsing: Extract refund ID and status from PayPal response
  4. Output (internal): Simplified refund model (refundId, status)

Refund Type: Full refund (no amount specified) - returns entire captured amount to customer.

Saga Compensation Context: Primarily used by orchestrator's saga compensation logic when booking creation fails after payment capture. Adapter isolates orchestrator from PayPal-specific refund API details.

Error Handling:

  • 400: Missing captureId/reservationId or invalid format
  • 500: PayPal API errors (capture not found, already refunded, insufficient funds) → PAYPAL_PAYMENT_REFUND_FAILED

Idempotency: PayPal API handles duplicate refund attempts - adapter passes through PayPal's error response if refund already exists.

Critical Financial Operation: Failure of this operation in saga compensation requires manual intervention to ensure customer receives refund.

Request Body schema: application/json
required
captureId
required
string

The PayPal capture ID to refund

reservationId
required
string

The reservation ID associated with this refund

Responses

Request samples

Content type
application/json
{
  • "captureId": "6KT41896LL345210G",
  • "reservationId": "6969808214d2f13caa1e5f16"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}