Skip to main content

Booking Session

API Endpoint: POST /api/v1/services/booking/v1/booking-session


📋 Overview

Create or update booking session to save flight selection and booking progress.

Purpose:

  • Save selected flight UUIDs
  • Store passenger count
  • Maintain booking state across page refreshes
  • Get session_id for tracking

Session Expiry: 3600 seconds (1 hour)


🔌 API Specification

Endpoint

POST {{base_url}}/api/v1/services/booking/v1/booking-session

Headers

{
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer {access_token}"
}

Request Body

Minimal Example (may work in some cases):

{
"flights": [
{
"uuid": "9de5bd3a-b951-11f0-a4ad-86d224031664",
"journey_type": "departure"
}
],
"passenger_number": {
"adult": 1,
"child": 0,
"infant": 0
}
}

Complete Example - One Way (recommended for production):

{
"flights": [
{
"uuid": "9de5bd3a-b951-11f0-a4ad-86d224031664",
"journey_type": "departure"
}
],
"selected_fare_departure": {
"index": 0,
"ma": "E1_ECO",
"ten": "Eco",
"so_ghe": 104,
"cabin": "ECONOMY",
"nguoi_lon": {
"gia_dai_ly": 1086300,
"gia_hien_thi": 1086300,
"gia_fare": 455800,
"gia_net": 1086300,
"phi_ve": 33800,
"phi_khuyen_mai": 0,
"phi_khuyen_mai_th": 0,
"phi_dai_ly": 0,
"thue": 630500,
"chi_tiet_thue": [
{
"ma": "AS",
"ten": "Airport Security Scan",
"gia_fare": 21700,
"gia_net": 21700,
"thue": 0
},
{
"ma": "AI",
"ten": "Airport Improvement Fund",
"gia_fare": 107000,
"gia_net": 107000,
"thue": 0
},
{
"ma": "AM",
"ten": "Admin Fee",
"gia_fare": 232300,
"gia_net": 250900,
"thue": 18600
},
{
"ma": "MF",
"ten": "Management Fee",
"gia_fare": 232300,
"gia_net": 250900,
"thue": 18600
}
],
"phi_xuat_ve": 0
},
"tre_em": {
"gia_dai_ly": 957600,
"gia_hien_thi": 957600,
"gia_fare": 455800,
"gia_net": 957600,
"phi_ve": 33800,
"phi_khuyen_mai": 0,
"phi_khuyen_mai_th": 0,
"phi_dai_ly": 0,
"thue": 501800,
"chi_tiet_thue": [
{
"ma": "AM",
"ten": "Admin Fee",
"gia_fare": 232300,
"gia_net": 250900,
"thue": 18600
},
{
"ma": "MF",
"ten": "Management Fee",
"gia_fare": 232300,
"gia_net": 250900,
"thue": 18600
}
],
"phi_xuat_ve": 0
},
"em_be": {
"gia_dai_ly": 0,
"gia_hien_thi": 0,
"gia_fare": 0,
"gia_net": 0,
"phi_ve": 0,
"phi_khuyen_mai": 0,
"phi_khuyen_mai_th": 0,
"phi_dai_ly": 0,
"thue": 0,
"chi_tiet_thue": [],
"phi_xuat_ve": 0
},
"du_lieu_hang": {
"key": "nlBxOkrƒeWPjoL8DOWVTrXCPjL¥pnaiavtzLƒ¥funlsrVo1je9VxvrADhtaskP3gNoYMALO5rƒQ4jmSfa1¥5n25FnYr1yc7CCbkaA6Tj2a9oCuPMmzKcYLNFq8ƒNodIh86n8Y549WU8XEK9JWEQDJcco1xnMpLdgZbFtL3SgB1RMxdƒqyv8hlR6uXorzRwx1I7aGy52JhbmGXCCnQgo6qAkdo1DBTGtfM01WJne8hdLkWv991Gj9ZCV75WokU7ƒeSxGH5yFYp6cQnZ4cjBWWywlqƒrGmve46thvBpFbhGu1Wy¥eC5TPy3wwcbrOJ8HxO1KceTj1vAdyVQva87rYuifqUnvLRJDMVlaxzJni8cJEij1K6LwE66JQS8veJdsin¥1pTKwq3RIsYD2fsSerb7jqc4atLa0zKZChg8yrlUFm5HBHQ1q4jCKsAPOYjjsUvJJfBrDw2K9QiJLyNi0m8W9o3GLo75mkRK2H1jLdEjP¥ƒMhWƒOJlbsZYIUznHB6znEgmvA8ƒGmmzLEVQSHxI4trAfiN0m9jhTlz¥A0fcx55Naovpm6A¥7IƒReXLKGSRƒG"
},
"ma_khuyen_mai": null
},
"passenger_number": {
"adult": 1,
"child": 0,
"infant": 0
}
}

Complete Example - Round Trip (recommended for round-trip bookings):

{
"flights": [
{
"uuid": "departure-flight-uuid",
"journey_type": "departure"
},
{
"uuid": "return-flight-uuid",
"journey_type": "return"
}
],
"selected_fare_departure": {
"index": 0,
"ma": "E1_ECO",
"ten": "Eco",
"cabin": "ECONOMY",
"nguoi_lon": { /* pricing details */ },
"tre_em": { /* pricing details */ },
"em_be": { /* pricing details */ },
"du_lieu_hang": {
"key": "EXAMPLE_KEY_FROM_DEPARTURE_FLIGHT"
},
"ma_khuyen_mai": null
},
"selected_fare_return": {
"index": 0,
"ma": "E1_ECO",
"ten": "Eco",
"cabin": "ECONOMY",
"nguoi_lon": { /* pricing details */ },
"tre_em": { /* pricing details */ },
"em_be": { /* pricing details */ },
"du_lieu_hang": {
"key": "EXAMPLE_KEY_FROM_RETURN_FLIGHT"
},
"ma_khuyen_mai": null
},
"passenger_number": {
"adult": 2,
"child": 1,
"infant": 0
}
}

Fields:

FieldTypeRequiredDescription
flightsarray✅ YesFlight selections
flights[].uuidstring✅ YesFlight UUID from search
flights[].journey_typestring✅ YesJourney type: "departure" or "return"
selected_fare_departureobject⚠️ RecommendedComplete fare details for departure flight
selected_fare_departure.indexnumber⚠️ RecommendedFare index (0-based) from search results
selected_fare_departure.mastring⚠️ RecommendedFare code (e.g., "E1_ECO", "E2_SKYBOSS")
selected_fare_departure.tenstring⚠️ RecommendedFare name display
selected_fare_departure.cabinstring⚠️ RecommendedCabin class: "ECONOMY" or "BUSINESS"
selected_fare_departure.nguoi_lonobject⚠️ RecommendedAdult pricing details
selected_fare_departure.tre_emobject⚠️ RecommendedChild pricing details
selected_fare_departure.em_beobject⚠️ RecommendedInfant pricing details
selected_fare_departure.du_lieu_hangobject⚠️ RecommendedAirline validation data
selected_fare_departure.du_lieu_hang.keystring⚠️ RecommendedCritical: Encoded key from airline API
selected_fare_departure.ma_khuyen_maistring/null⚠️ RecommendedPromotion code (if any)
selected_fare_returnobject⚠️ For Round-TripComplete fare details for return flight
selected_fare_return.**⚠️ For Round-TripSame structure as selected_fare_departure
passenger_numberobject✅ YesPassenger count
passenger_number.adultnumber✅ YesNumber of adults
passenger_number.childnumber✅ YesNumber of children
passenger_number.infantnumber✅ YesNumber of infants

⚠️ Important Notes:

  1. selected_fare_departure is HIGHLY RECOMMENDED for production use:

    • Contains du_lieu_hang.key required for airline validation
    • Preserves exact pricing from search results
    • Prevents price mismatch errors
  2. For Round-Trip bookings:

    • Include both selected_fare_departure AND selected_fare_return
    • Each must have its own du_lieu_hang.key from respective flight search results
    • Both flights must be included in the flights array
  3. du_lieu_hang.key is critical:

    • Generated by airline API during search
    • Cannot be manually created or modified
    • Must be passed exactly as received from search results
    • Used for fare validation and booking confirmation
  4. Data Source:

    • Get selected_fare_departure from Socket.IO flight search results (departure)
    • Get selected_fare_return from Socket.IO flight search results (return)
    • Copy the entire fare object from the selected fare class
    • Do NOT manually construct or modify these objects

✅ Success Response

Status: 200 OK

{
"message": "Booking session created successfully",
"data": {
"session_id": "b042ea16-da6a-4185-8723-6193b9e4effb",
"flights": [
{
"uuid": "9de5bd3a-b951-11f0-a4ad-86d224031664",
"journey_type": "departure",
"flight": "168",
"route": "SGN-HAN"
}
],
"expires_in_seconds": 3600
},
"status": "success",
"code": 200
}

Key Response Fields:

FieldTypeDescription
data.session_idstringSession ID for tracking
data.flights[].uuidstringConfirmed flight UUID
data.flights[].flightstringFlight number
data.flights[].routestringRoute code (e.g., "SGN-HAN")
data.expires_in_secondsnumberSession validity (3600s = 1 hour)

🔄 Round-Trip Booking Details

For round-trip bookings, you must include:

  • Both flight UUIDs in the flights array
  • selected_fare_departure with fare details for the departure flight
  • selected_fare_return with fare details for the return flight

Example: Minimal Round-Trip

{
"flights": [
{
"uuid": "departure-flight-uuid",
"journey_type": "departure"
},
{
"uuid": "return-flight-uuid",
"journey_type": "return"
}
],
"passenger_number": {
"adult": 2,
"child": 1,
"infant": 0
}
}
{
"flights": [
{
"uuid": "departure-flight-uuid",
"journey_type": "departure"
},
{
"uuid": "return-flight-uuid",
"journey_type": "return"
}
],
"selected_fare_departure": {
"index": 0,
"ma": "E1_ECO",
"ten": "Eco",
"cabin": "ECONOMY",
"nguoi_lon": {
"gia_dai_ly": 1086300,
"gia_hien_thi": 1086300,
"gia_net": 1086300,
"thue": 630500
},
"tre_em": {
"gia_dai_ly": 957600,
"gia_hien_thi": 957600,
"gia_net": 957600,
"thue": 501800
},
"em_be": {
"gia_dai_ly": 0,
"gia_hien_thi": 0,
"gia_net": 0,
"thue": 0
},
"du_lieu_hang": {
"key": "DEPARTURE_KEY_FROM_SEARCH_RESULT"
},
"ma_khuyen_mai": null
},
"selected_fare_return": {
"index": 1,
"ma": "E2_DELUXE",
"ten": "Deluxe",
"cabin": "ECONOMY",
"nguoi_lon": {
"gia_dai_ly": 1425300,
"gia_hien_thi": 1425300,
"gia_net": 1425300,
"thue": 630500
},
"tre_em": {
"gia_dai_ly": 1296600,
"gia_hien_thi": 1296600,
"gia_net": 1296600,
"thue": 501800
},
"em_be": {
"gia_dai_ly": 0,
"gia_hien_thi": 0,
"gia_net": 0,
"thue": 0
},
"du_lieu_hang": {
"key": "RETURN_KEY_FROM_SEARCH_RESULT"
},
"ma_khuyen_mai": null
},
"passenger_number": {
"adult": 2,
"child": 1,
"infant": 0
}
}

⚠️ Critical for Round-Trip:

  • Each flight (departure & return) has its own unique du_lieu_hang.key
  • Keys must come from their respective search results
  • Do NOT reuse the same key for both flights

💡 Usage Notes

When to Call

Call after flight selection:

1. Search flights (Socket.IO)
2. User selects flight + fare
3. → CREATE_BOOKING_SESSION ← Save selection
4. Proceed to services/booking

Session Benefits

  • ✅ Persist flight selection across page refresh
  • ✅ Track booking progress
  • ✅ Prevent duplicate bookings
  • ✅ Resume incomplete bookings

Session Expiry

After 3600 seconds:

  • Session expires automatically
  • Must create new session
  • Flight UUIDs may also expire

🐛 Error Handling

Invalid Flight UUID

Response:

{
"status": "error",
"message": "Flight UUID không hợp lệ",
"code": 400
}

Solution: Search flights again to get fresh UUIDs


Missing Required Fields

Response:

{
"status": "error",
"message": "flights is required",
"code": 400
}

Before:

  • 02-SEARCH-FLIGHTS.md - Search and select flight

After:

  • 03-GET-SERVICES.md - Get baggage, food, seats
  • 05-HOLD-BOOKING.md - Complete booking (HOLD)
  • 06-ISSUE-TICKET.md - Complete booking (ISSUED)

📝 Example Flow

// 1. After flight selection
const selectedFlight = flights[0];
const fareIndex = 0;

// 2. Create booking session
const sessionResponse = await fetch('/api/v1/services/booking/v1/booking-session', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
flights: [{
uuid: selectedFlight.uuid,
journey_type: 'departure'
}],
passenger_number: {
adult: 1,
child: 0,
infant: 0
}
})
});

const session = await sessionResponse.json();
console.log('Session ID:', session.data.session_id);
console.log('Expires in:', session.data.expires_in_seconds, 'seconds');

// 3. Continue to services selection