Update Services - Thêm Hành Lý & Suất Ăn
APIs:
POST /api/v1/services/booking/v1/danh-sach-phu-tro-dat-ve- Get available servicesPOST /api/v1/services/booking/v1/cap-nhat-dich-vu- Add services to booking
📋 Overview
Thêm dịch vụ (hành lý ký gửi, suất ăn) vào booking đã tồn tại.
Flow:
1. Get available services (baggage + food)
2. Select items
3. Preview (optional)
4. Confirm & Payment
Note: Thường chỉ áp dụng cho booking HOLD (trang_thai: 0). ISSUED bookings may have restrictions.
🔌 Step 1: Get Available Services
API Endpoint
POST {{base_url}}/api/v1/services/booking/v1/danh-sach-phu-tro-dat-ve
Headers
{
"Content-Type": "application/json",
"Authorization": "Bearer {access_token}"
}
Request Body
{
"chang_bay": [
{
"index": 0,
"index_chuyen_bay": 0,
"ve_id": 81
}
]
}
Key Field: ve_id = Booking ID (not null for existing booking)
Response
{
"message": "Lấy danh sách phụ trợ thành công",
"data": {
"0": {
"dich_vu": {
"0": [
{
"uuid": "e68afba6-aeaf-11f0-8b81-86d224031662",
"ten": "Bag 30kgs",
"nhom": "Hành lý ký gửi",
"loai_dich_vu": 2,
"gia_net": 324000,
"so_kg": "30"
},
{
"uuid": "e68bfb6e-aeaf-11f0-8b81-86d224031662",
"ten": "Combo 1",
"nhom": "Suất ăn",
"loai_dich_vu": 1,
"gia_net": 115600
}
]
}
}
},
"status": "success"
}
Service Types:
loai_dich_vu: 2= Baggage (Hành lý)loai_dich_vu: 1= Food (Suất ăn)
🔌 Step 2: Confirm Services
API Endpoint
POST {{base_url}}/api/v1/services/booking/v1/cap-nhat-dich-vu
Headers
{
"Content-Type": "application/json",
"Authorization": "Bearer {access_token}"
}
Request Body
{
"ve_id": 81,
"dich_vu": [
{
"hanh_khach_id": 529,
"index_chang_bay": 1,
"uuid_dich_vu": "e68afba6-aeaf-11f0-8b81-86d224031662",
"index_hanh_trinh": 0
},
{
"hanh_khach_id": 529,
"index_chang_bay": 1,
"uuid_dich_vu": "e68bfb6e-aeaf-11f0-8b81-86d224031662",
"index_hanh_trinh": 0
}
],
"xac_nhan": "y",
"xuat_ve": "",
"tong_tien": 439600
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ve_id | number | ✅ Yes | Booking ID |
dich_vu | array | ✅ Yes | List of services to add |
dich_vu[].hanh_khach_id | number | ✅ Yes | Passenger ID (from get details) |
dich_vu[].index_chang_bay | number | ✅ Yes | Segment index (1-based) |
dich_vu[].uuid_dich_vu | string | ✅ Yes | Service UUID (from step 1) |
dich_vu[].index_hanh_trinh | number | ✅ Yes | Journey index (0-based) |
xac_nhan | string | ✅ Yes | Confirmation: "y" = yes, "" = preview |
xuat_ve | string | ✅ Yes | Issue ticket: usually "" |
tong_tien | number | ✅ Yes | Total amount to charge (VND) |
Success Response
{
"message": "Cập nhật dịch vụ thành công",
"data": null,
"status": "success",
"code": 200
}
💡 Complete Example: cURL
Step 1: Get Services
curl -X POST "{{base_url}}/api/v1/services/booking/v1/danh-sach-phu-tro-dat-ve" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {access_token}" \
-d '{
"chang_bay": [{
"index": 0,
"index_chuyen_bay": 0,
"ve_id": 81
}]
}'
Step 2: Confirm Services
curl -X POST "{{base_url}}/api/v1/services/booking/v1/cap-nhat-dich-vu" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {access_token}" \
-d '{
"ve_id": 81,
"dich_vu": [
{
"hanh_khach_id": 529,
"index_chang_bay": 1,
"uuid_dich_vu": "baggage-uuid",
"index_hanh_trinh": 0
},
{
"hanh_khach_id": 529,
"index_chang_bay": 1,
"uuid_dich_vu": "food-uuid",
"index_hanh_trinh": 0
}
],
"xac_nhan": "y",
"xuat_ve": "",
"tong_tien": 439600
}'
🎯 Implementation Steps
1. Get Booking Details First
// From 02-GET-DETAILS.md
const details = await getBookingDetails(ve_id);
const hanh_khach_id = details.data.hanh_khach[0].id;
2. Get Available Services
const servicesResponse = await fetch(
"{{base_url}}/api/v1/services/booking/v1/danh-sach-phu-tro-dat-ve",
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
chang_bay: [
{
index: 0,
index_chuyen_bay: 0,
ve_id: ve_id,
},
],
}),
},
);
const services = servicesResponse.data["0"].dich_vu["0"];
3. Filter Services
// Get baggage only
const baggage = services.filter((s) => s.loai_dich_vu === 2 && s.gia_net > 0);
// Get food only
const food = services.filter((s) => s.loai_dich_vu === 1);
4. Select Items
// Select 30kg baggage
const selectedBaggage = baggage.find((b) => b.so_kg === "30");
// Select first food combo
const selectedFood = food[0];
5. Build Request & Confirm
const dich_vu = [];
if (selectedBaggage) {
dich_vu.push({
hanh_khach_id: hanh_khach_id,
index_chang_bay: 1,
uuid_dich_vu: selectedBaggage.uuid,
index_hanh_trinh: 0,
});
}
if (selectedFood) {
dich_vu.push({
hanh_khach_id: hanh_khach_id,
index_chang_bay: 1,
uuid_dich_vu: selectedFood.uuid,
index_hanh_trinh: 0,
});
}
// Calculate total
const tong_tien =
(selectedBaggage?.gia_net || 0) + (selectedFood?.gia_net || 0);
// Confirm
const confirmResponse = await fetch(
"{{base_url}}/api/v1/services/booking/v1/cap-nhat-dich-vu",
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
ve_id: ve_id,
dich_vu: dich_vu,
xac_nhan: "y",
xuat_ve: "",
tong_tien: tong_tien,
}),
},
);
⚠️ Important Notes
1. Index Conventions
| Field | Indexing | Example |
|---|---|---|
index_chang_bay | 1-based | 1, 2 |
index_hanh_trinh | 0-based | 0, 1 |
index_chuyen_bay | 0-based | 0, 1 |
2. Preview vs Confirm
// Preview (no payment)
{ "xac_nhan": "", "tong_tien": 0 }
// Confirm (with payment)
{ "xac_nhan": "y", "tong_tien": 439600 }
3. Total Amount Must Match
const tong_tien = dich_vu.reduce((sum, item) => {
const service = services.find((s) => s.uuid === item.uuid_dich_vu);
return sum + (service?.gia_net || 0);
}, 0);
4. UUID Expiration
Get fresh services immediately before confirm:
// ❌ Bad: Get services → wait → confirm
const services = await getServices(ve_id);
await sleep(600000); // 10 minutes
await confirmServices(services); // UUIDs expired!
// ✅ Good: Get → immediately confirm
const services = await getServices(ve_id);
await confirmServices(services);
5. Multiple Passengers
For 2 passengers, add services for each:
dich_vu = [
{
hanh_khach_id: 529, // Passenger 1
uuid_dich_vu: "baggage-uuid-1",
...
},
{
hanh_khach_id: 530, // Passenger 2
uuid_dich_vu: "baggage-uuid-2",
...
}
]
🐛 Common Issues
UUID Not Exists
Error: "Phụ trợ {uuid} không tồn tại"
Cause: UUID expired
Solution: Get fresh services before confirm
Already Purchased
Error: "Passenger has already purchased an item in this category (-991)"
Cause: Service already exists in booking
Solution: Use different booking or check existing services
Total Amount Mismatch
Error: "TotalAmount is invalid"
Cause: tong_tien doesn't match sum of service prices
Solution: Calculate correctly:
const tong_tien = selectedBaggage.gia_net + selectedFood.gia_net;
📊 Pricing Example
Services:
- Baggage 30kg: 324,000 VND
- Combo 1 Food: 115,600 VND
Total: 439,600 VND
Request:
{
"tong_tien": 439600
}
🔗 Next Steps
After adding services:
- Payment →
03-PAYMENT.md(if booking is HOLD) - View Updated Details →
02-GET-DETAILS.md
📝 Notes
- Two-step process: Get services → Confirm
- Works on HOLD bookings (
trang_thai: 0) - Can add both baggage and food in one request
- UUIDs expire quickly (~5-10 minutes)
index_chang_bayis 1-based,index_hanh_trinhis 0-basedhanh_khach_idfrom booking details APItong_tienmust match total service pricesxac_nhan: "y"for confirm,""for preview- Payment charged from F2 wallet