Skip to content

Checkout API

Multi-step checkout flow for booking tour packages. Handles session management, selection updates, payment processing, and booking finalization.

The checkout API provides a stateful multi-step flow:

  1. Start session - Initialize checkout with offer data
  2. Select flights - Choose outbound/inbound flights (economy or business)
  3. Select hotels - Optional hotel upgrades
  4. Select activities - Optional activity add-ons
  5. Select transfers - Optional transfer upgrades
  6. Enter travelers - Passenger personal data
  7. Payment - Process deposit via Stripe
  8. Confirmation - View booking summary

Session storage: Server-side PHP session (checkout key)

Base URL: /api/{market}/{lang}/checkout

POST /checkout/{offerId} (start)
├─► GET /checkout (view session)
├─► PUT /checkout/flights
├─► PUT /checkout/hotels
├─► PUT /checkout/activities
├─► PUT /checkout/transfers
├─► PUT /checkout/travelers
├─► POST /checkout/payment/intent
├─► POST /checkout/payment/confirm
└─► GET /checkout/confirmation/{reference}

Start checkout session for an offer. Idempotent - returns existing session if same offer.

Response: 201 Created (new) or 200 OK (existing)

{
"success": true,
"data": {
"offer_id": 123,
"started_at": "2026-02-15T10:30:00Z",
"base_price": 2499.00,
"extras_price": 0.00,
"total_price": 2499.00,
"pax_count": 2,
"currency": { "code": "EUR", "symbol": "EUR" }
}
}

Get current session state. Returns 404 if no active session.

Update flight selection.

Request:

{
"cabin_class": "ECONOMY",
"outbound": {
"departure_date": "2026-02-15",
"departure_time": "09:10",
"arrival_time": "15:15",
"departure_airport": "MAD",
"arrival_airport": "NBO",
"flight_numbers": ["EK123", "EK456"],
"airlines": ["EK"],
"stops": 1,
"stopover_airports": ["DXB"]
},
"inbound": { /* same structure */ },
"business_extra_price_per_person": null
}

Update hotel upsell selections.

Request:

{
"hotel_selections": [
{
"upsell_hotel_id": 6,
"nights_start": 1,
"nights_end": 3,
"price_difference": 150.00
}
]
}

Update activity upsell selections. Prices calculated server-side.

Request:

{
"activity_selections": [
{ "activity_id": 5, "day_number": 2 }
]
}

Update transfer selections. Prices calculated server-side.

Request:

{
"transfer_selections": [
{ "transfer_id": 1, "day_number": 1 }
]
}

Store traveler personal data. Count must match offer’s pax_count.

Request:

{
"travelers": [
{
"first_name": "John",
"last_name": "Doe",
"nationality": "ES",
"birth_date": "1990-05-15",
"phone": "+34612345678",
"email": "john@example.com",
"passport_number": "AB1234567",
"passport_expiry": "2030-01-01"
}
]
}

Get booking data for confirmation page after successful payment.

Response:

{
"success": true,
"data": {
"booking_reference": "VOL-2026-00001",
"tour_name": "Aventura en Japon",
"duration_days": 10,
"duration_nights": 9,
"travelers": 2,
"hero_image": "https://cdn.example.com/japan.jpg",
"trip_url": "https://..."
}
}

Prices update automatically as selections change:

total_price = base_price + extras_price
extras_price = sum of:
+ business_extra_price_per_person * pax_count
+ hotel_price_differences
+ activity_prices * pax_count
+ transfer_prices

Security: Activity and transfer prices are calculated server-side from database. Client-provided prices are ignored.

See Payment Gateway System for full payment documentation.

Key points:

  1. createIntent uses session’s total_price (including extras) for deposit calculation
  2. confirm calls BookingFinalizationService::finalizeFromCheckoutSession() after successful payment
  3. After payment, redirect to /confirmation/{reference}

After successful payment, BookingFinalizationService finalizes the booking:

  1. Creates Passenger records from traveler_data
  2. Attaches passengers to booking via booking_passenger pivot (first = lead)
  3. Attaches passengers to client via client_passenger pivot
  4. Creates BookingUpsell records from session selections (hotels, activities, transfers)
  5. Updates booking prices with base_price, extras_price, total_amount

Source: backend/app/Services/Booking/BookingFinalizationService.php

Idempotency: If booking already has passengers attached, finalization is skipped.

CodeErrorDescription
400no_checkout_sessionNo active session, call start first
400traveler_data_requiredTravelers must be entered before payment
404offer_not_foundOffer not found or not active in market
404booking_not_foundBooking reference not found
ComponentFile
Controllerbackend/app/Http/Controllers/Api/CheckoutController.php
Payment Controllerbackend/app/Http/Controllers/Api/PaymentController.php
Session Servicebackend/app/Services/Checkout/CheckoutSessionService.php
Finalization Servicebackend/app/Services/Booking/BookingFinalizationService.php
Form Requestsbackend/app/Http/Requests/Api/Checkout/