Skip to main content

Add Journey - Thêm Hành Trình

API Endpoint: POST /api/v1/services/booking/v1/them-hanh-trinh


📋 Overview

Add a new journey (flight segment) to existing VJ booking.

Use Case:

  • Convert one-way → round-trip
  • Add connecting flight
  • Add return flight after initial booking

Important:

  • VJ supports adding journeys to both HOLD and ISSUED bookings
  • Must re-search for fresh flight UUID (bookingKey TTL ~30s)
  • Backend re-validates fee before confirm

🔄 Flow Steps

1. Get Booking Details

  • API: GET /thong-tin-ve/{ve_id}
  • Check current journeys

2. Search New Flight

  • API: POST /tim-chuyen (with ve_id)
  • ⚠️ CRITICAL: Include ve_id in search request
  • Select flight to add

3. Preview Add Journey Fee

  • API: POST /them-hanh-trinh (preview mode)
  • Calculate fee before commit

4. Re-search for Fresh UUID

  • Why: bookingKey expires in ~30s
  • Search same flight again
  • Get fresh UUID

5. Confirm Add Journey

  • API: POST /them-hanh-trinh (confirm mode)
  • Use fresh UUID from step 4
  • Use fee from preview

🔌 API Specification

Endpoint

POST {{base_url}}/api/v1/services/booking/v1/them-hanh-trinh

Headers

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

Preview Mode

Calculate fee before adding journey.

Request Body

{
"ve_id": 661,
"loai_phi_dai_ly": "Nhập",
"uuid_chuyen_bay": "d4259b72-b93d-11f0-8157-86d224031664",
"index_chang_bay_moi": 0,
"tong_tien": 0,
"xac_nhan": ""
}

Fields:

FieldTypeRequiredDescription
ve_idnumber✅ YesBooking ID
loai_phi_dai_lystring✅ YesFee type: "Nhập" or "Cơ bản"
uuid_chuyen_baystring✅ YesFlight UUID from search
index_chang_bay_moinumber✅ YesFare index (0-based)
tong_tiennumber✅ YesTotal amount (0 for preview)
xac_nhanstring✅ YesConfirm flag ("" = preview)

Response

{
"message": "Tổng tiền thanh toán là 1,086,300",
"data": {
"pnr": "ZCHMUA",
"tong_tien": 1086300,
"phi_thay_doi": 0,
"f2_info": {
"id": "432ff8ad-31a7-45ed-90cc-b92e7e542271",
"code": "ST2010",
"name": "F2 - 9S - TƯỜNG VÕ",
"balance": 694264297,
"sufficient": true
}
},
"status": "success",
"code": 200
}

Response Fields:

FieldTypeDescription
tong_tiennumberTotal fee for new journey
phi_thay_doinumberModification fee (if any)
f2_info.balancenumberCurrent F2 wallet balance
f2_info.sufficientbooleanBalance sufficient for payment

Confirm Mode

Execute add journey with validated fee.

Request Body

{
"ve_id": 661,
"loai_phi_dai_ly": "Nhập",
"uuid_chuyen_bay": "d530daa4-b93d-11f0-8157-86d224031664",
"index_chang_bay_moi": 0,
"tong_tien": 3978500,
"xac_nhan": "y"
}

Key Differences from Preview:

  • xac_nhan: "y" (confirm mode)
  • tong_tien: 3978500 (use fee from preview)
  • uuid_chuyen_bay (MUST be fresh UUID from re-search)

Response

{
"message": "Thêm hành trình thành công",
"data": {
"pnr": "ZCHMUA",
"ve_id": 661,
"hang_bay": "VJ",
"journey_added": true,
"tong_tien": 3978500,
"vj_response": null
},
"status": "success",
"code": 200
}

⚠️ Critical: Fresh UUID Required

VJ bookingKey expires in ~30 seconds:

1. Initial search → UUID: abc123
2. Preview fee (5s later) → Still valid ✅
3. User reviews (25s later) → ...
4. Confirm with abc123 → ❌ EXPIRED!

Solution: Re-search Before Confirm

// Step 3: Preview with initial UUID
const preview = await previewAddJourney({
uuid_chuyen_bay: initialUUID,
xac_nhan: ""
});

const fee = preview.data.tong_tien;

// Step 4: Re-search to get FRESH UUID
const freshSearch = await searchFlights(...);
const freshUUID = freshSearch.flights[0].uuid;

// Step 5: Confirm with FRESH UUID + PREVIEW fee
const confirm = await confirmAddJourney({
uuid_chuyen_bay: freshUUID, // ← Fresh UUID
tong_tien: fee, // ← Fee from preview
xac_nhan: "y"
});

💰 Payment Logic

HOLD Booking

  • Fee validated
  • Wallet NOT deducted (Payment Later)
  • Will charge when issuing ticket

ISSUED Booking

  • Fee validated
  • Wallet deducted immediately
  • Journey added to booking

🐛 Error Handling

UUID Expired

Response:

{
"status": "error",
"message": "bookingKey expired or invalid"
}

Solution: Re-search to get fresh UUID

Insufficient Balance (ISSUED)

Response:

{
"status": "error",
"message": "Số dư không đủ"
}

📝 Example Flow

// 1. Get booking
const booking = await getBookingDetails(ve_id);

// 2. Search for journey to add (INCLUDE ve_id!)
const searchResult = await searchFlights({
...searchParams,
ve_id: booking.ve.id // ← CRITICAL
});

// 3. Preview fee
const preview = await fetch('/them-hanh-trinh', {
method: 'POST',
body: JSON.stringify({
ve_id: booking.ve.id,
uuid_chuyen_bay: selectedFlight.uuid,
index_chang_bay_moi: fareIndex,
loai_phi_dai_ly: "Nhập",
tong_tien: 0,
xac_nhan: ""
})
});

const fee = preview.data.tong_tien;

// 4. Re-search for fresh UUID (after user confirms)
const freshSearch = await searchFlights({...});
const freshUUID = freshSearch.flights[0].uuid;

// 5. Confirm with fresh UUID
const result = await fetch('/them-hanh-trinh', {
method: 'POST',
body: JSON.stringify({
ve_id: booking.ve.id,
uuid_chuyen_bay: freshUUID, // Fresh!
index_chang_bay_moi: fareIndex,
loai_phi_dai_ly: "Nhập",
tong_tien: fee, // From preview
xac_nhan: "y"
})
});

Prerequisites:

  • 02-SEARCH-FLIGHTS.md - Search for journey to add (with ve_id)
  • 02-GET-DETAILS.md - Get current booking info

After:

  • 03-PAYMENT.md - Pay for HOLD booking
  • 02-GET-DETAILS.md - Verify journey added