Checkout API
Multi-step checkout flow for booking tour packages. Handles session management, selection updates, payment processing, and booking finalization.
Overview
Section titled “Overview”The checkout API provides a stateful multi-step flow:
- Start session - Initialize checkout with offer data
- Select flights - Choose outbound/inbound flights (economy or business)
- Select hotels - Optional hotel upgrades
- Select activities - Optional activity add-ons
- Select transfers - Optional transfer upgrades
- Enter contact - Booking contact person (client data)
- Enter travelers - Passenger personal data
- Payment - Process deposit via Stripe
- Confirmation - View booking summary
Session storage: Server-side PHP session (checkout key)
Selection persistence: The frontend preserves user selections (flights, hotels, activities, transfers) when navigating back through checkout steps, so users don’t lose their choices.
Funnel tracking: A checkout-flow booking is created when checkout starts (initial status: checkout), tracking which step each user reaches. See Checkout Funnel Tracking for details.
Base URL: /api/{market}/{lang}/checkout
Session Flow
Section titled “Session Flow”POST /checkout/{offerId} (start - always creates fresh session) │ ├─► POST /checkout/{offerId}/search-economy-flights │ (live re-pricing during loading screen) │ ├─► GET /checkout (view session, auto-refreshes price) │ ├─► PUT /checkout/flights ├─► PUT /checkout/hotels ├─► PUT /checkout/activities ├─► PUT /checkout/transfers ├─► PUT /checkout/contact ├─► PUT /checkout/travelers │ ├─► POST /checkout/payment/intent ├─► POST /checkout/payment/confirm │ └─► GET /checkout/confirmation/{reference}Endpoints
Section titled “Endpoints”POST /checkout/{offerId}
Section titled “POST /checkout/{offerId}”Start checkout session for an offer. Always creates a fresh session, discarding any previous one. Creates a checkout booking via BookingFunnelService::createDraft() (status checkout) for funnel tracking and returns the booking_id in the response.
Bookability check: The offer must pass the bookable() scope (Active status AND departure date >= today + 5 days). If the offer is Active but too close to departure, returns 410 Gone with error: "offer_expired". If the offer doesn’t exist at all, returns 404. See Bookability for details.
Request (optional):
{ "actual_pax_count": 3, "actual_room_type": "2A+1CH"}Request validation:
actual_pax_count(optional): 1-4 passengers, defaults to offer’s pax_countactual_room_type(optional): Must exist insupplier_service_rate_pricesfor the tour’s base hotels, defaults to room type matching pax count
If actual_room_type is unavailable for the tour’s base hotels, returns 422 Unprocessable Entity with error room_type_not_available.
Pricing for non-standard pax (not 2A):
When actual_pax_count differs from the offer’s 2A default:
- Land price is recalculated using
AutoOfferGeneratorService::calculateLandPrice()for the actual room type - Flight price scales linearly (offer’s per-person flight price × actual_pax_count)
- Per-pax marketing rounding applied:
rawPerPax → roundToMarketingPrice → multiply by paxCount - Session stores
is_non_standard_pax: trueto control price refresh behavior
Response: 201 Created
{ "success": true, "data": { "offer_id": 123, "booking_id": 456, "booking_reference": "BK-A1B2C3D4", "started_at": "2026-02-15T10:30:00Z", "base_price": 2499.00, "extras_price": 0.00, "total_price": 2499.00, "pax_count": 2, "actual_pax_count": 3, "actual_room_type": "2A+1CH", "is_non_standard_pax": true, "currency": { "code": "EUR", "symbol": "EUR" } }}After receiving the response, the TripConfigurator stores booking_id in Astro.session via POST /api/checkout/session for SSR step tracking.
GET /checkout
Section titled “GET /checkout”Get current session state. Returns 404 if no active session.
Auto-refreshes base_price and total_price if the offer’s final_price has changed since the session was created (e.g., after a live economy flight search updated the price via EconomyFlightCacheUpdateService).
Important: For non-standard pax sessions (is_non_standard_pax: true), the base_price is NOT refreshed from the offer’s 2A-based final_price. Instead, it uses the recalculated price from session start or from refreshBasePriceFromLiveFlights().
POST /checkout/{offerId}/search-economy-flights
Section titled “POST /checkout/{offerId}/search-economy-flights”Trigger a live economy flight search during the checkout loading screen. Calls the Aerticket API to re-price the offer’s international and domestic flights before the user proceeds.
Rate limit: 5 requests per minute.
Behavior:
- Extracts route info (departure, destination, dates) from the offer’s cached flight segments
- Applies admin-configured search filters from Settings page (airlines, max stops, fare sources, baggage, latest arrival time) with forced ECONOMY cabin class
- Detects multi-city vs round-trip based on whether inbound departure differs from outbound destination
- Searches domestic flight legs (one-way per leg) alongside international — domestic failures are graceful with cached prices as fallback
- Selects the best fare: cheapest with baggage, fallback to cheapest overall
- Updates
DynamicFlightCachewith fresh results (top 5 fares) for both international and domestic legs - Re-links the offer’s
OfferFlightrecords and updates individual leg prices - Recalculates the offer’s
final_price(international + domestic total) - Stores economy search metadata in session for building the per-leg breakdown during flight selection:
international_fare_price(from the selected fare’stotalPrice), domestic legs enriched withsearched_price(live API price when available), and international route info - For non-standard pax: Calls
refreshBasePriceFromLiveFlights()to update session’s base_price using live flight total (international + domestic) + recalculated land price
Response (success):
{ "success": true, "data": { "has_flights": true, "price_changed": true, "original_price": 2499.00, "new_price": 2549.00 }}Note: For non-standard pax, original_price and new_price reflect the session’s recalculated base_price (not the offer’s 2A-based final_price).
Response (fallback — API timeout or error):
{ "success": true, "data": { "has_flights": true, "fallback": true, "price_changed": false }}On fallback, the checkout continues with existing cached flight data. The cache update failure is non-fatal.
POST /checkout/{offerId}/business-flights
Section titled “POST /checkout/{offerId}/business-flights”Search for business class flight options during checkout. Performs a LIVE search via the Aerticket API and calculates final prices including margin.
Requirements: Must have an active checkout session (protected by stateful.api middleware).
Behavior:
- Extracts route from offer’s cached flight segments (including inbound departure/destination for multi-city detection)
- Applies admin-configured search filters from Settings page via
SettingsService::buildCheckoutSearchOptions()(airlines, max stops, fare sources, baggage) with forced BUSINESS cabin class - Detects multi-city vs round-trip: multi-city when inbound departure differs from outbound destination
- Performs live Aerticket search using session’s
actual_pax_count - Searches domestic flight legs (one-way BUSINESS per leg) alongside international — if no business fares found for a domestic leg, falls back to cached economy price (already available from the economy search that runs first)
- Domestic total is added to every business fare card’s “+x€” extra price calculation
- Skips incomplete fares that do not contain both outbound and inbound legs
- Returns
fareIdon each outbound/inbound leg option so frontend can pair legs by fare identity (not by array index) - Stores per-leg search metadata in session (
legs_metadata) for later use when user selects a business fare — includes domestic leg details, fare-to-price mapping, fare details (outbound/inbound flight numbers keyed by fare ID), and international route info - For non-standard pax, passes session context (
actual_pax_count,actual_room_type,base_price) to service - Calculates prices:
- Standard 2-pax: Uses offer’s
land_base_priceandfinal_pricedirectly - Non-standard pax: Recalculates land price for actual room type, applies per-pax marketing rounding
Response:
{ "success": true, "data": { "outbound_options": [...], "inbound_options": [...], "cabinClass": "BUSINESS", "has_flights": true, "source_type": "live_search", "original_final_price": 2499.00, "pax_count": 3, "apihubflowid": "..." }}Source: backend/app/Services/Checkout/BusinessFlightSearchService.php
PUT /checkout/flights
Section titled “PUT /checkout/flights”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": [{"code": "EK", "name": "Emirates"}], "stops": 1, "stopover_airports": ["DXB"], "arrivalDayOffset": 1 }, "inbound": { /* same structure */ }, "business_extra_price_per_person": null, "fare_id": null}Business integrity rule: for cabin_class = BUSINESS, send fare_id from the selected business card. The backend uses this ID to canonicalize outbound/inbound flight numbers from server-side search metadata before persisting booking.flight_selection, preventing mismatches when multiple fares share the same outbound leg.
GET /checkout/{offerId}/hotels
Section titled “GET /checkout/{offerId}/hotels”Get available hotel options for an offer, grouped by time period. Returns all 3 tiers: Selection (base, included in price), Luxury (upgrade), and Grand Luxury (upgrade).
Response shape per hotel entry:
| Field | Type | Description |
|---|---|---|
id | string | Unique key: {hotelId}-{tier}-{startDay}-{endDay} |
hotelId | number|null | Supplier hotel ID (null for package fallback entries) |
name | string | Hotel name (or package name for legacy fallback) |
location | string|null | City (null for package fallback entries) |
tier | string | selection, luxury, or grand_luxury |
tier_label | string | Human-readable tier label |
nights | object | { start: number, end: number } |
imageUrl | string|null | First hotel image URL |
description | string|null | Hotel description text from SupplierHotel |
isIncluded | boolean | true for Selection tier, false for upgrades |
priceDifference | number|null | Price difference vs Selection tier, null for Selection |
selectionHotelName | string|null | Base hotel name (for upgrades), null for Selection |
Hotels are grouped by consecutive nights at the same Selection hotel. Each group contains one Selection entry and optional Luxury/Grand Luxury upgrade entries. Selection-only days (no upgrades available) are included as standalone entries.
Package tours: When a package tour has selection hotels assigned in the admin, the API returns actual hotel names, images, and locations — same as non-package tours. Only legacy package tours without selection hotels fall back to using the package service name as a placeholder.
Room type pricing: Hotel upgrade price differences use the session’s actual_room_type (e.g., 3A for 3 travelers). The room_type query parameter overrides the session value if provided. Hotels without pricing for the selected room type return priceDifference: null and are shown as unavailable (non-selectable in the frontend).
Query parameters:
| Param | Type | Description |
|---|---|---|
room_type | string (optional) | Room type for pricing: 1A, 2A, 3A, or 4A. Falls back to session’s actual_room_type, then to 2A. |
Source: backend/app/Services/Checkout/CheckoutHotelService.php
PUT /checkout/hotels
Section titled “PUT /checkout/hotels”Update hotel upgrade selections. Supports Luxury and Grand Luxury tier upgrades.
Request:
{ "advance": true, "hotel_selections": [ { "upgrade_hotel_id": 6, "nights_start": 1, "nights_end": 3 } ]}Notes:
advance(optional, defaulttrue): Whentrue, callsadvanceStepto record funnel progression. Frontend sendsfalseon backward navigation so the funnel step is not advanced.upgrade_hotel_idrefers to a Luxury or Grand Luxury tier hotelprice_differenceis calculated server-side (not accepted from client)- See Service Tiers for tier details
PUT /checkout/activities
Section titled “PUT /checkout/activities”Update activity upsell selections. Prices calculated server-side.
Request:
{ "advance": true, "activity_selections": [ { "activity_id": 5, "day_number": 2 } ]}advance(optional, defaulttrue): Whenfalse, skipsadvanceStep(used on backward navigation).
PUT /checkout/transfers
Section titled “PUT /checkout/transfers”Update transfer selections. Prices calculated server-side.
Request:
{ "advance": true, "transfer_selections": [ { "transfer_id": 1, "day_number": 1 } ]}advance(optional, defaulttrue): Whenfalse, skipsadvanceStep(used on backward navigation).
GET /checkout/{offerId}/contact
Section titled “GET /checkout/{offerId}/contact”Get offer summary for the client contact data entry page. Returns the same offer metadata as the travelers endpoint.
Response: 200 OK — Same structure as GET /checkout/{offerId}/travelers.
Errors:
404- Offer not found, not bookable (expired or within lead time), or belongs to different market
Source: backend/app/Http/Controllers/Api/CheckoutController.php
PUT /checkout/contact
Section titled “PUT /checkout/contact”Store client contact data (the booking contact person). Separate from traveler data — the client is the person responsible for the booking, not necessarily a traveler.
Request:
{ "client": { "first_name": "John", "last_name": "Doe", "email": "john@example.com", "phone": "+34612345678" }}Validation: See StoreClientDataRequest for rules. No ASCII-only restriction on names (client data is not sent to airline APIs).
| Field | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | First name (max 100 chars) |
last_name | string | Yes | Last name (min 2, max 100 chars) |
email | string | Yes | Valid email (max 255 chars) |
phone | string | Yes | Phone number (max 30 chars) |
Pricing: Client data does NOT affect pricing.
Errors:
400- No checkout session (error: "no_checkout_session")400- Validation error
PUT /checkout/travelers
Section titled “PUT /checkout/travelers”Store traveler personal data. Count must match offer’s pax_count.
Name validation: Names are validated using the same PassengerValidationRules as the Filament admin panel, enforcing AerTicket API requirements to prevent booking failures:
- ASCII letters and spaces only (
/^[a-zA-Z\s]+$/) — no accents, numbers, or symbols - No
+character - Last name minimum 2 characters
- Each name maximum 57 characters
- Combined first + last name: 2-57 characters total
Validation source: StoreTravelerDataRequest reuses PassengerValidationRules::firstName() and ::lastName().
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 /checkout/confirmation/{reference}
Section titled “GET /checkout/confirmation/{reference}”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://..." }}Price Calculation
Section titled “Price Calculation”Prices update automatically as selections change:
total_price = base_price + extras_price
extras_price = sum of: + business_extra_price_per_person * actual_pax_count + hotel_price_differences (uses session's actual_room_type) + activity_prices * actual_pax_count + transfer_pricesSecurity: Activity and transfer prices are calculated server-side from database. Client-provided prices are ignored.
Variable pax count: The actual_pax_count from the session is used for flight and activity extras calculation. Hotel upgrade pricing uses the session’s actual_room_type to match the selected passenger configuration.
Payment Flow
Section titled “Payment Flow”See Payment Gateway System for full payment documentation.
Key points:
createIntentuses session’stotal_price(including extras) for deposit calculationconfirmcreates/updates Client fromclient_data(nottraveler_data[0]), then callsBookingFinalizationService::finalizeFromCheckoutSession()- Booking stores
payment_method_codeandpayment_gateway_codefor operational tracking - Status is routed via
PaymentService::updateBookingPaymentStatus():- Flight booking (
ECONOMY/BUSINESS) ->pending_flight_booking - Land-only booking ->
pending_land_confirmation
- Flight booking (
- Session cleanup: After successful payment (PaymentStatus::Succeeded), the checkout session is cleared via
CheckoutSessionService::clear()to prevent session reuse - After payment, redirect to
/confirmation/{reference}
Admin-Driven Flight Booking (Per-Leg)
Section titled “Admin-Driven Flight Booking (Per-Leg)”After a successful checkout payment, flight bookings move to pending_flight_booking and are dispatched manually by admins from the booking view page. Each flight leg is booked independently with its own FlightBooking record.
Trigger condition: booking status is pending_flight_booking (or retry from flight_booking_failed).
Process:
- Admin clicks Book All Flights / Book Flight (or Retry All Flights / Retry) in Filament
- Booking transitions to
flight_booking_in_progress - Action computes which legs are still unbooked (skips legs with existing
FlightBookingrecords) - Dispatches one
CreateCheckoutFlightBookingJobper unbooked leg toaerticket-bookingsqueue (each with$legIndexparameter) - Each job independently:
- International legs: Re-searches via
EconomyFlightSearchService::searchInternationalOnly()(economy) orBusinessFlightSearchService::searchBusinessFlightsRaw()(business), matches round-trip by flight numbers + price - Domestic legs: Searches one-way via
AerticketSearchService, matches by flight numbers + price
- International legs: Re-searches via
- Verifies availability with Aerticket
- Creates booking via
AerticketBookService - Creates
FlightBookingrecord linked toBookingviabooking_idFK withleg_indexandflight_type - Dispatches
AerticketRetrieveBookingJobto fetch PNR details - All-legs-booked check: Only transitions to
flights_confirmedwhen ALL legs haveFlightBookingrecords. Handles concurrent transition race with try/catch onInvalidArgumentException.
- Partial success -> admin notification “Leg N booked (M/N legs)”
- All legs booked ->
flights_confirmed - Failure ->
flight_booking_failed(with error metadata includingleg_index)
Business class specifics:
- Business fares are bundled (1 fare = both legs, itinerary index 1 only), unlike economy mix-and-match
BookingUpsellwith typeflight_upgradecontinues to be created for financial tracking
Idempotency: Per-leg check via FlightBooking::where(booking_id, leg_index)->exists() with row-level lock. The job implements ShouldBeUnique with uniqueId = "checkout-flight:{bookingId}:{legIndex}".
Error handling:
Retry behavior depends on the failure type, classified by CheckoutFlightBookingFailure enum:
- noMatchingFare (fare no longer available): Released back to queue after 300 seconds (5 minutes) to allow fare inventory to refresh. If still failing on second attempt, the job is explicitly failed. This delay gives the airline fare cache time to update.
- Other failures (searchFailed, verifyFailed, bookingFailed): Standard retry with 120 seconds backoff via framework
$backoffproperty.
Job configuration: $tries = 3, $maxExceptions = 2, $timeout = 420, $backoff = [120], $uniqueFor = 480.
- Admin users receive Filament database notifications on success or failure (per-leg and all-legs-complete)
- Failed bookings can be retried from booking view actions (top-level or per-leg)
Structured error context:
CheckoutFlightBookingException carries a context array with structured details about WHY a booking failed. This context is spread into the booking_status_transitions.metadata JSON on failure, making error details visible to admins in the status timeline.
Context is automatically extracted from Aerticket exception types via extractContext():
| Aerticket Exception | Context Fields |
|---|---|
AerticketPriceChangeException | sub_type, original_price, new_price, currency, percentage_change |
AerticketFareExpiredException | sub_type, fare_id, expired_at |
AerticketTimeoutException | sub_type (timeout) |
AerticketValidationException | sub_type, validation_errors |
AerticketVerifyException | sub_type, fare_id |
AerticketBookingException | sub_type, fare_id |
API error responses (verify/book returning provider errors) include sub_type: api_error and provider_errors array.
The noMatchingFare factory also accepts context with flight_numbers, cabin_class, total_fares_searched, expected_price, and leg_type/leg_index for debugging fare matching failures.
The status timeline Blade component displays the message field from failed transition metadata directly under the status badge.
Matching logic:
- International (economy and business): Per-leg
fare_price * pax_countmust match fare’stotalPrice(within 0.01 precision, for 2-pax only) AND outbound/inbound flight numbers must match exactly. Non-2-pax bookings match by flight numbers only.fare_priceis always stored per-person inflight_selection.legs. - Domestic: One-way search, matched by flight numbers + price (
fare_price * pax_countfor 2-pax).findMatchingOneWayFare()matches on outbound leg only. - If no matching fare found, job releases with 5-minute delay before failing
Source:
- Job:
backend/app/Jobs/CreateCheckoutFlightBookingJob.php - Exception:
backend/app/Exceptions/CheckoutFlightBookingException.php - Failure enum:
backend/app/Enums/CheckoutFlightBookingFailure.php - Service:
backend/app/Services/Checkout/CheckoutFlightBookingService.php - Status transitions:
backend/app/Services/Booking/BookingStatusService.php
Related: AerTicket Integration
Booking Finalization
Section titled “Booking Finalization”After successful payment, PaymentController creates/updates the Client from client_data, then BookingFinalizationService finalizes the booking:
- Creates Passenger records from
traveler_data - Attaches passengers to booking via
booking_passengerpivot (first = lead) - Attaches passengers to client via
client_passengerpivot - Persists flight selection to
booking.flight_selectionwith enriched airport data (both economy and business), including per-leg breakdown withfare_price,flight_numbers,route,type, and for domestic legs:departure_time,arrival_time,airline_names,stopover_airports - Creates BookingUpsell records from session selections (hotels, activities, transfers, business class flights)
Source: backend/app/Services/Booking/BookingFinalizationService.php
Idempotency: If booking already has passengers attached, finalization is skipped.
Flight storage:
- Both economy and business flights: Stored in
booking.flight_selectioncolumn with city names, duration, andlegsarray (per-legfare_price,flight_numbers,route,type,cabin_class; domestic legs also includedeparture_time,arrival_time,airline_names,stopover_airports) - Business class: Additionally stored in
booking_upsells.flight_search_paramsJSON (for financial tracking via BookingUpsell) - Per-leg data enables independent booking of each leg during admin-driven flight booking (see Flight Selection Storage)
Error Responses
Section titled “Error Responses”| Code | Error | Description |
|---|---|---|
| 400 | no_checkout_session | No active session, call start first |
| 400 | client_data_required | Client contact must be entered before payment |
| 400 | traveler_data_required | Travelers must be entered before payment |
| 404 | offer_not_found | Offer not found or not active in market |
| 404 | booking_not_found | Booking reference not found |
| 410 | offer_expired | Offer is Active but within the 5-day booking lead time (departure too soon). Only returned by POST /checkout/{offerId} (start). |
Frontend handling: All checkout step pages redirect to /{market}/home when receiving no_checkout_session error to prevent rendering with missing data.
Funnel Step Tracking (Internal API)
Section titled “Funnel Step Tracking (Internal API)”Checkout step tracking uses an internal server-to-server API authenticated via Sanctum Bearer token.
PATCH /api/internal/bookings/{id}/checkout-step
Section titled “PATCH /api/internal/bookings/{id}/checkout-step”Track which checkout step the user is currently viewing. Called by Astro SSR during page rendering.
Authentication: Sanctum Bearer token with internal:read ability (INTERNAL_API_TOKEN env var).
Request:
{ "step": "hotels"}Valid step values: flights, hotels, activities, transfers, main_contact, travelers, summary
Response: 200 OK
{ "success": true}Error: 422 if step value is invalid.
Architecture
Section titled “Architecture”User navigates to /checkout/{offerId}/hotels (full page load) -> Astro SSR: reads booking_id from Astro.session -> Astro SSR: trackCheckoutStepSSR() calls PATCH /api/internal/... -> BookingFunnelService::trackStep(): - Advances checkout_step forward-only (furthest step reached) - Sets checkout_snapshot['current_step'] to actual step (even backward) - Records timestamp if first visit to that step -> Page renders (tracking already complete)Every checkout step page calls trackCheckoutStepSSR(Astro.session, '<step>') in its frontmatter. Since every checkout navigation is a full page load (Astro MPA), this is 100% reliable with no client-side JS dependency.
The booking_id is bridged from React to Astro.session via POST /api/checkout/session (Astro API route) after checkout session creation.
Backward navigation: When a user navigates back (e.g., from Activities to Hotels), the frontend sends advance: false on the selection save request so advanceStep is skipped. The SSR trackStep call updates current_step to show the user’s actual position while checkout_step remains at the furthest step reached.
Source:
- Tracking utility:
frontend/src/features/checkout/server/trackCheckoutStepSSR.ts - Session bridge:
frontend/src/pages/api/checkout/session.ts - Internal API client:
frontend/src/shared/config/internalApiClient.ts - Controller:
backend/app/Http/Controllers/Api/BookingController.php
Source Files
Section titled “Source Files”| Component | File |
|---|---|
| Controller | backend/app/Http/Controllers/Api/CheckoutController.php |
| Payment Controller | backend/app/Http/Controllers/Api/PaymentController.php |
| Session Service | backend/app/Services/Checkout/CheckoutSessionService.php |
| Funnel Tracking Service | backend/app/Services/Checkout/BookingFunnelService.php |
| Flight Options Service | backend/app/Services/Checkout/CheckoutFlightService.php |
| Hotel Options Service | backend/app/Services/Checkout/CheckoutHotelService.php |
| Economy Search Service | backend/app/Services/Checkout/EconomyFlightSearchService.php |
| Business Search Service | backend/app/Services/Checkout/BusinessFlightSearchService.php |
| Economy Cache Update Service | backend/app/Services/Checkout/EconomyFlightCacheUpdateService.php |
| Checkout Flight Booking Service | backend/app/Services/Checkout/CheckoutFlightBookingService.php |
| Checkout Flight Booking Job | backend/app/Jobs/CreateCheckoutFlightBookingJob.php |
| Finalization Service | backend/app/Services/Booking/BookingFinalizationService.php |
| Form Requests | backend/app/Http/Requests/Api/Checkout/ |
Related
Section titled “Related”- Payment Gateway System - Payment processing
- Bookings - Booking data model and checkout funnel tracking
- Swagger: http://localhost/api/documentation