Booking Flow - Complete Guide
Hướng dẫn đầy đủ quy trình đặt vé từ A-Z.
📋 Overview
Complete booking flow từ tìm kiếm đến xuất vé, bao gồm thêm dịch vụ và chọn ghế.
Total Steps: 6 bước chính (+ Authentication module riêng)
Time Required: ~30-60 seconds (depending on selections)
🔄 Complete Flow Diagram
📚 Step-by-Step Guide
Prerequisites: Authentication
Module: ../authentication/
APIs:
POST /oauth/token- Get access tokenGET /api/v1/credentials/me- Get user profile & agency info
See: ../authentication/README.md for complete authentication guide
Step 1: Get Airports
Doc: 01-GET-AIRPORTS.md
API: GET /api/v1/current/airports
Purpose: Get list of airports for departure/arrival selection
Output:
- 6,073+ airports worldwide
- Vietnam domestic: ~22 airports
Popular Routes:
- SGN (Ho Chi Minh) ↔ HAN (Hanoi)
- SGN ↔ DAD (Da Nang)
- HAN ↔ DAD
Step 2: Search Flights
Doc: 02-SEARCH-FLIGHTS.md
API: POST /api/v1/services/booking/v1/tim-chuyen + Socket.IO
Step 2a: Create Booking Session
Doc: 02a-BOOKING-SESSION.md
API: POST /api/v1/services/booking/v1/booking-session
Purpose: Save selected flight, fare details, and passenger count to session
Input (Minimal):
- Flight UUID from search
- Passenger count (adults, children, infants)
Input (Recommended for Production):
- Flight UUID from search
selected_fare_departure- Complete fare details for departure flightselected_fare_return- Complete fare details for return flight (if round-trip)- Passenger count (adults, children, infants)
Why use selected_fare_departure/return?
- ✅ Contains
du_lieu_hang.keyrequired for airline validation - ✅ Preserves exact pricing from search results
- ✅ Prevents price mismatch errors during booking
Output:
- Session ID (expires in 3600s)
- Confirmed flight info
⚠️ Important:
- Call after flight selection, before getting services
- For round-trip: include both
selected_fare_departureANDselected_fare_return - Each flight must have its own unique
du_lieu_hang.keyfrom search results
Step 3: Get Services & Seats
Doc: 03-GET-SERVICES.md
API: POST /api/v1/services/booking/v1/danh-sach-phu-tro-tim-ve
Purpose: Get available baggage, food, and seats for selected flight
Input:
{
"chang_bay": [
{
"index": 0,
"index_chuyen_bay": 0,
"ve_id": null
}
]
}
Output:
- Baggage options (20kg, 30kg, 40kg)
- Food/meal options
- Available seats with prices
⚠️ Important: Save flight.uuid and fare.index for next step
Step 4: Select Services & Seats
Doc: 04-SELECT-SERVICES-SEATS.md
Purpose: Prepare data for booking request (no API call)
Actions:
- Filter services by type (baggage vs food)
- Select desired items
- Filter available seats
- Choose seat
- Build
dich_vuandcho_ngoiarrays for booking request
Step 5: Hold Booking
Doc: 05-HOLD-BOOKING.md
API: POST /api/v1/services/booking/v1/dat-chuyen
Purpose: Reserve booking with payment deadline (~4 hours)
Request:
{
"api": "VJ",
"chuyen_bay": [{"uuid": "flight-uuid", "index_hang_cho": 0, ...}],
"hanh_khach": [{"ho": "NGUYEN VAN", "ten": "TEST", ...}],
"thong_tin_booker": {"ho": "TRAN THI", "ten": "BOOKER", ...},
"dich_vu": [...],
"cho_ngoi": [...],
"trang_thai": 0
}
Key Field: "trang_thai": 0 (HOLD)
Response:
{
"data": {
"ve": {
"id": 82,
"pnr": "AAMJ52",
"trang_thai": 0,
"ngay_het_han": "22-10-2025 06:03:00"
}
}
}
Save: ve_id, pnr, hanh_khach_id for later operations
Step 6: Issue Ticket
Doc: 06-ISSUE-TICKET.md
API: POST /api/v1/services/booking/v1/dat-chuyen (same as HOLD)
Purpose: Book and issue ticket immediately (pay now)
Difference from HOLD:
{
"trang_thai": 1
}
Key Field: "trang_thai": 1 (ISSUED)
Result:
- Ticket issued immediately
- F2 wallet charged
- No payment deadline
- Cannot cancel (only VOID/REFUND)
🔀 Decision Flow
💰 Pricing Calculation
Total Amount = Ticket + Services + Seats
Example:
Ticket (SGN-HAN, Economy): 1,086,300 VND
Baggage (30kg): 324,000 VND
Food (Combo 1): 60,000 VND
Seat (12C): 54,000 VND
----------------------------------------
TOTAL: 1,524,300 VND
⚠️ Important Rules
1. UUID Expiration
- Flight UUIDs: Valid ~15-20 minutes after search
- Service/Seat UUIDs: Valid ~5-10 minutes after fetch
- Always get fresh UUIDs before booking
2. Indexing Convention
| Field | Indexing | Example |
|---|---|---|
Passenger (index_hanh_khach) | 1-based | 1, 2, 3 |
Segment (index_chang_bay) | 1-based | 1, 2 |
Flight (index_chuyen_bay) | 0-based | 0, 1 |
3. Name Format
// ✅ Correct
"ho": "NGUYEN VAN"
"ten": "TEST"
// ❌ Wrong
"ho": "Nguyen Van"
"ten": "test"
4. Date Format
DD-MM-YYYY (e.g., "01-11-2025")
🎯 Complete Example
Full Flow in One Request (Issue Direct)
curl -X POST "{{base_url}}/api/v1/services/booking/v1/dat-chuyen" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {access_token}" \
-d '{
"api": "VJ",
"nhieu_chang": 0,
"chuyen_bay": [{
"index": 1,
"uuid": "852ffde2-aeb0-11f0-8b81-86d224031662",
"phi_dai_ly": 0,
"index_hang_cho": 0,
"loai_phi_dai_ly": "Cơ bản"
}],
"hanh_khach": [{
"index": 1,
"ho": "NGUYEN VAN",
"ten": "TEST",
"email": "test@example.com",
"so_dien_thoai": "0987654321",
"ngay_sinh": "01-01-1990",
"the_thanh_vien": "",
"loai_hanh_khach": 1,
"danh_xung": 1,
"cha_me_id": null,
"ho_chieu": null
}],
"thong_tin_booker": {
"ho": "TRAN THI",
"ten": "BOOKER",
"email": "booker@example.com",
"so_dien_thoai": "0912345678"
},
"dich_vu": [
{
"uuid_dich_vu": "7a3251f4-aee4-11f0-bd8c-86d224031662",
"index_hanh_khach": 1,
"index_chang_bay": 1
}
],
"cho_ngoi": [
{
"ma_cho_ngoi": "nfRUyZla4RiDI5HExo49eN...",
"uuid_cho_ngoi": "7a5a7b02-aee4-11f0-bd8c-86d224031662",
"index_hanh_khach": 1,
"index_chang_bay": 1,
"index_chuyen_bay": 0
}
],
"ma_khuyen_mai": "",
"trang_thai": 1,
"xac_nhan_ve_dup": "",
"xac_nhan_thay_doi_gia": "",
"tong_gia_ve": 0,
"xac_nhan_cho_du_bi": ""
}'
Result:
{
"message": "Đặt chỗ thành công. ",
"data": {
"ve": {
"id": 85,
"pnr": "XYZ123",
"trang_thai": 1,
"gia_net": 1524300
}
},
"status": "success"
}
📊 Status Codes
| trang_thai | Status | Description |
|---|---|---|
0 | HOLD | Giữ chỗ - Cần thanh toán trong ~4 giờ |
1 | ISSUED | Đã xuất vé - Đã thanh toán |
🔗 Related Documentation
Authentication (Prerequisites)
../authentication/README.md- Complete auth guide../authentication/01-AUTHENTICATION.md- OAuth 2.0 login../authentication/02-GET-PROFILE.md- User & agency info
Booking Flows
01-GET-AIRPORTS.md- Airport list02-SEARCH-FLIGHTS.md- Search with Socket.IO02a-BOOKING-SESSION.md- Create booking session (save flight selection)03-GET-SERVICES.md- Get baggage, food, seats04-SELECT-SERVICES-SEATS.md- Data preparation05-HOLD-BOOKING.md- Reserve booking06-ISSUE-TICKET.md- Book & issue immediately
Manage My Booking (MMB)
../manage-booking/01-LIST-BOOKINGS.md- List all bookings../manage-booking/02-GET-DETAILS.md- Booking details../manage-booking/PAYMENT.md- Pay HOLD booking../manage-booking/UPDATE-SERVICES.md- Add services after booking../manage-booking/BUY-SEAT.md- Add seat after booking
🎓 Best Practices
1. Always Get Fresh Data
// ✅ Good
const airports = await getAirports();
const flights = await searchFlights(route);
const services = await getServices();
await bookWithServices(); // Immediate
// ❌ Bad
const services = await getServices();
await sleep(600000); // 10 minutes wait
await bookWithServices(); // UUIDs expired!
2. Handle Errors Gracefully
try {
const booking = await issueTicket(data);
} catch (error) {
if (error.message.includes("UUID không tồn tại")) {
// Re-fetch services with fresh UUIDs
const freshServices = await getServices();
const booking = await issueTicket(dataWithFreshUUIDs);
}
}
3. Validate Before Booking
// Check wallet balance first
const balance = await getWalletBalance();
const totalAmount = calculateTotal(ticket, services, seats);
if (balance < totalAmount) {
alert("Insufficient balance");
// Use HOLD instead of ISSUE
}
4. Save Important IDs
// After successful booking
const bookingData = {
ve_id: response.data.ve.id,
pnr: response.data.ve.pnr,
hanh_khach_id: response.data.hanh_khach[0].id,
key_chang_bay: response.data.chang_bay[0].key_chang_bay,
};
// Save to database/storage for MMB operations
📝 Summary
Complete Flow:
Auth Module → 1. Airports → 2. Search → 2a. Booking Session →
3. Get Services → 4. Select → 5. Hold/Issue → Done
Time: ~30-60 seconds
Result: Booking with PNR, ticket, services, and seats
Next: Manage booking via MMB APIs