Skip to main content

Update Services - Thêm Hành Lý & Suất Ăn

APIs:

  1. POST /api/v1/services/booking/v1/danh-sach-phu-tro-dat-ve - Get available services
  2. POST /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:

FieldTypeRequiredDescription
ve_idnumber✅ YesBooking ID
dich_vuarray✅ YesList of services to add
dich_vu[].hanh_khach_idnumber✅ YesPassenger ID (from get details)
dich_vu[].index_chang_baynumber✅ YesSegment index (1-based)
dich_vu[].uuid_dich_vustring✅ YesService UUID (from step 1)
dich_vu[].index_hanh_trinhnumber✅ YesJourney index (0-based)
xac_nhanstring✅ YesConfirmation: "y" = yes, "" = preview
xuat_vestring✅ YesIssue ticket: usually ""
tong_tiennumber✅ YesTotal 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

FieldIndexingExample
index_chang_bay1-based1, 2
index_hanh_trinh0-based0, 1
index_chuyen_bay0-based0, 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:

  1. Payment03-PAYMENT.md (if booking is HOLD)
  2. View Updated Details02-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_bay is 1-based, index_hanh_trinh is 0-based
  • hanh_khach_id from booking details API
  • tong_tien must match total service prices
  • xac_nhan: "y" for confirm, "" for preview
  • Payment charged from F2 wallet