- Nhà phát triển
- SDK TypeScript chính thức
SDK TypeScript chính thức
Ferry có SDK chính thức viết bằng TypeScript để gọi API đối tác mà không phải tự dựng client HTTP. SDK nằm trong repo mã nguồn mở ferry-vn/ferry-sdk cùng với công cụ dòng lệnh CLI và máy chủ MCP cho AI agent.
SDK gồm những gì
Phần tiêu đề “SDK gồm những gì”Một client FerryClient có các nhóm lệnh theo nghiệp vụ: bến và tuyến, chuyến, giá theo tuyến, sơ đồ ghế, booking, đơn hàng, yêu cầu huỷ vé, khách hàng, tổ chức, giao dịch, công nợ, voucher và thông báo gián đoạn. Kiểu dữ liệu được sinh từ đặc tả OpenAPI của API đối tác (backend v3.2.16); CI của repo báo lỗi khi kiểu bị lệch đặc tả, và một bộ kiểm thử hợp đồng chỉ-đọc chạy hằng đêm trên UAT để phát hiện backend đổi hành vi.
SDK không thay thế các quy tắc nghiệp vụ trong tài liệu này: luồng đặt vé, xuất vé và tiền vẫn đúng như mô tả, SDK chỉ bọc lại cho dễ gọi.
Cài đặt
Phần tiêu đề “Cài đặt”SDK có trên npm. Node.js cần từ 20 trở lên; Bun, Deno và Cloudflare Workers cũng dùng được:
npm install @ferryvn/sdkSau khi cài, import { FerryClient } from '@ferryvn/sdk' (hoặc require) và gọi locations.find() chạy được ngay, kể cả khi chưa có API key vì danh mục bến là dữ liệu công khai.
Trước khi bắt đầu
Phần tiêu đề “Trước khi bắt đầu”API key do quản trị viên tổ chức tạo trong Dashboard, chỉ hiển thị một lần, không tự hết hạn và mang quyền quản trị tổ chức: tạo booking, xuất vé trừ vào số dư, đổi cài đặt. Vì vậy hãy giữ key ở máy chủ. SDK từ chối khởi động trong trình duyệt khi có apiKey, trừ khi bạn truyền dangerouslyAllowBrowser: true; đừng bật cờ này cho ứng dụng phía khách. Các lệnh không cần key (tra bến, tuyến, hãng tàu và bookings.getPublic) chạy được trong trình duyệt. Cách tạo key: xem Xác thực bằng API key.
| Môi trường | Địa chỉ SDK dùng | Chọn bằng |
|---|---|---|
| Production (mặc định) | https://api.ferry.vn | không cần cấu hình |
| UAT | https://api.ferryvn.net | environment: 'uat' với key UAT |
Muốn trỏ tới địa chỉ khác (ví dụ base URL UAT mà Ferry cấp cho bạn), dùng tuỳ chọn baseUrl; SDK tự thêm /api/v1.
Bắt đầu nhanh
Phần tiêu đề “Bắt đầu nhanh”Ví dụ dưới tra bến, tìm chuyến, tìm khách hàng và tạo một booking nháp. Chưa có bước nào trừ tiền:
import { FerryClient } from '@ferryvn/sdk';
const ferry = new FerryClient({ apiKey: process.env.FERRY_API_KEY, environment: 'uat' });
const [from] = await ferry.locations.find('Rạch Giá');const [to] = await ferry.locations.find('phu quoc');const { data: voyages } = await ferry.voyages.search({ date: '2026-11-01', departureId: from.id, destinationId: to.id });
const [customer] = await ferry.customers.search('0901234567');const { booking, draft } = await ferry.bookingDrafts.create({ customerId: customer.id, ordererName: 'Nguyễn Văn A', contactEmail: 'a@example.com', depart: { voyageId: voyages[0].voyage.id, seats: ['A1'], passengers: [{ name: 'Nguyen Van A', dateOfBirth: '1990-05-01', socialId: '079090000001', homeTown: 'Cần Thơ' }], },});draft.estimatedTotalVnd; // tiền vé + phí bến − voucherDựng booking đã kiểm tra trước
Phần tiêu đề “Dựng booking đã kiểm tra trước”Tự dựng một booking nghĩa là điền 17 trường cho mỗi vé, lấy từ hai cấu trúc khác nhau, trong đó loại vé phải khớp tuổi hành khách và dòng giá phải khớp loại ghế. Nhóm bookingDrafts làm việc này giúp bạn: áp dụng quy tắc của backend ngay trên máy và báo mọi vấn đề trước khi gửi gì đi.
preparechỉ kiểm tra và báo giá; không tạo gì.createkiểm tra rồi tạo booking.- Bạn đưa vào chuyến, tên ghế (hoặc bỏ trống để SDK tự chọn ghế liền kề, hạng ghế hành khách rẻ nhất còn đủ chỗ) và danh sách hành khách.
- SDK chọn loại vé theo tuổi và quốc tịch theo quy tắc của từng hãng, khớp giá, kiểm tra ghế còn trống và bán được, số CCCD hoặc hộ chiếu trùng nhau, và quy tắc độ dài thông tin hành khách của hãng. Quốc tịch mặc định là Việt Nam.
- Báo giá gồm tiền vé đã gồm VAT cộng phí bến của hãng, trừ voucher. Với 2 booking thật trên production, tổng server tính khớp báo giá của SDK đến từng đồng.
- Khi có vấn đề, SDK ném
FerryBookingDraftErrorvới danh sáchissues. HàmisVoyageBookablecho biết kết quả tìm chuyến nào bán được, vì API cũng trả cả chuyến đã khoá, hết chỗ hoặc đã khởi hành.
Tạo booking gắn ghế đã chọn và hành khách vào booking, không trừ tiền; booking bắt đầu ở trạng thái Requested, chưa thanh toán, kèm hạn thanh toán. Ghế chỉ được hãng xác nhận khi xuất vé thành công; xem Đặt vé.
Giá theo tuyến
Phần tiêu đề “Giá theo tuyến”prices.forRoute trả giá bán của một tuyến theo từng chuyến, hạng ghế và loại vé, kèm phí bến mỗi vé và quy tắc tuổi. Đây là giá bán đã gồm VAT, sau khuyến mãi của hãng và các khoản cộng giá của tổ chức bạn, tức số tiền server thu; giá vốn của bạn không bao giờ có trong kết quả (xem Giá và markup).
Nếu không truyền live, giá lấy từ dữ liệu chuyến đã đồng bộ và có thể trễ so với hãng. Truyền live: n (tối đa 10) để tính lại giá n chuyến đầu từ sơ đồ ghế; mỗi chuyến có thể phải hỏi hãng.
Phân trang
Phần tiêu đề “Phân trang”Mọi lệnh danh sách trả { data, next_cursor }. Dùng iterate() để SDK tự đi theo con trỏ; hàm collect(...) của SDK gom kết quả iterate() vào một mảng (chỉ nên dùng cho script nhỏ vì tốn bộ nhớ). Giới hạn mỗi trang của danh sách đối tác là 50 (tuyến là 100); xem thêm Quy ước chung.
Thử lại và không có idempotency
Phần tiêu đề “Thử lại và không có idempotency”SDK chỉ tự thử lại lệnh GET và HEAD khi lỗi mạng hoặc gặp mã 408, 429, 502, 503, 504, tối đa 2 lần. Lệnh ghi (POST, PATCH…) không bao giờ bị thử lại, vì API của Ferry không có khoá idempotency và gửi lại có thể tạo đơn thứ hai.
Sau lỗi mạng hoặc timeout trên lệnh ghi, hãy tra lại bằng bookings.list với bộ lọc namePhoneEmail trước khi gửi lại. Mỗi lệnh gọi còn nhận tuỳ chọn riêng: signal, timeoutMs, maxRetries, headers, requestId.
Mọi lỗi SDK ném ra đều kế thừa FerryError:
| Lớp lỗi | Khi nào |
|---|---|
FerryApiError | Mọi phản hồi không thành công; có status, errorCode, errorName, thông điệp tiếng Việt userMessage và requestId |
FerryAuthError (401), FerryPermissionError (403) | Key sai hoặc bị thu hồi; không đủ quyền |
FerryNotFoundError (404), FerryConflictError (409), FerryValidationError (422) | Lỗi nghiệp vụ theo mã HTTP |
FerryRateLimitError (429) | Chạm giới hạn; có retryAfterMs |
FerryServerError (5xx) | Lỗi phía Ferry hoặc hãng |
FerryConnectionError | Không có phản hồi: timeout, lỗi mạng hoặc bạn tự huỷ bằng AbortSignal |
FerryConfigError, FerryBookingDraftError | Cấu hình sai (trước khi gửi gì); booking dựng không hợp lệ |
Lưu ý FerryPermissionError: Ferry cũng trả 403 cho id không tồn tại hoặc thuộc tổ chức khác, nên đừng coi 403 là key hỏng. Mã nghiệp vụ có sẵn dạng hằng số, ví dụ FerryErrorCode.OrganizationBalanceNotEnough (120120) và FerryErrorCode.UnavailableSeats (150201); hàm errorCodeName(code) trả tên mã. Bảng mã đầy đủ ở Lỗi và cách xử lý.
Mỗi yêu cầu mang header x-request-id; backend ghi lại nó, nên hãy gửi giá trị này cho hỗ trợ Ferry khi cần tra sự cố.
Webhook
Phần tiêu đề “Webhook”Webhook của Ferry không có chữ ký, chỉ gửi một lần (hạn phản hồi 2 giây, không thử lại); xem Webhooks. SDK có hai hàm giúp nhận an toàn hơn:
parseWebhook({ headers, body })đọc gói tin và gán kiểu theo headerx-event-type.safeEqual(a, b)so sánh chuỗi bí mật nằm trong URL theo thời gian không đổi.
Vẫn phải đặt chuỗi bí mật khó đoán trong URL, phản hồi nhanh và chạy đối soát bằng polling, vì sự kiện mất sẽ không được gửi lại.
Ngày giờ và tiền
Phần tiêu đề “Ngày giờ và tiền”Lịch chạy tính theo giờ Việt Nam. toYmd(date) đổi một Date thành ngày dương lịch theo giờ Việt Nam, nên 00:30 ngày 20 ở Việt Nam luôn ra 2026-10-20 bất kể múi giờ máy chủ; các hàm liên quan là todayInVietnam(), addDays() và isValidYmd(). Tiền là số nguyên đồng: formatVnd(1250000) trả 1.250.000 ₫.
Lệnh làm di chuyển tiền
Phần tiêu đề “Lệnh làm di chuyển tiền”Ba nhóm lệnh sau có hậu quả tài chính. SDK không bao giờ tự thử lại chúng.
| Việc | Lệnh SDK | Tác động |
|---|---|---|
| Tạo booking | bookings.create, bookingDrafts.create | Gắn ghế và hành khách vào booking, không trừ tiền; booking Requested, chưa thanh toán, có hạn thanh toán |
| Xác nhận xuất vé | bookings.confirmToIssue | Trừ số dư và hạn mức tín dụng của tổ chức, rồi đặt vé với hãng; có thể mất vài phút; tối đa 5 lần mỗi booking mỗi phút |
| Gửi yêu cầu huỷ vé | cancelTicketRequests.create | Giữ ngay phí huỷ, đánh dấu vé đã huỷ tức thì và khởi động chuỗi duyệt |
bookings.confirmToIssue không trả dữ liệu: đọc kết quả bằng bookings.get. Với hãng xuất vé tự động, vé được phát hành ngay trong lệnh gọi và thời gian chờ tối thiểu của lệnh này là 10 phút. Khách thiếu tiền sẽ bị ghi nợ, nên booking thường kết thúc ở trạng thái đã trả có nợ; xem Xuất vé và Tiền.
Nếu cần gọi một route SDK chưa bọc, dùng ferry.request với method, đường dẫn /api/v1/…, query và body; lệnh này dùng chung xác thực, thử lại, hook và lỗi của SDK.
Sai lầm thường gặp
Phần tiêu đề “Sai lầm thường gặp”| Hiểu nhầm | Thực tế | Cách làm đúng |
|---|---|---|
| Nhúng API key vào ứng dụng web cho tiện | Key mang quyền quản trị tổ chức | Giữ key ở máy chủ; gọi qua backend của bạn |
| Gửi lại lệnh ghi sau timeout | API không có khoá chống lặp, gửi lại có thể tạo đơn thứ hai | Tra bookings.list theo tên, số điện thoại hoặc email trước |
| Lỗi 403 nghĩa là key hỏng | Ferry cũng trả 403 cho id không tồn tại hoặc của tổ chức khác | Kiểm tra id và tổ chức trước khi đổi key |
| UAT an toàn như sandbox | Xuất vé trên UAT đặt vé thật, trừ tiền thật | Chỉ xuất vé trên chuyến đã thoả thuận với Ferry |
prices.forRoute luôn là giá tức thời | Không có live, giá lấy từ dữ liệu đồng bộ và có thể trễ | Dùng live khi cần giá sát thực tế, và kiểm tra lại trước khi chốt |
| Tính ngày bằng múi giờ máy chủ | Lịch chạy theo giờ Việt Nam | Dùng toYmd và todayInVietnam |
Tra ở đâu
Phần tiêu đề “Tra ở đâu”Mô tả đầy đủ từng lệnh, tuỳ chọn và hướng dẫn chuyển từ bản 0.x nằm ở README của gói SDK trên GitHub. Tham chiếu HTTP gốc nằm ở Scalar UI. Lỗi của gói đăng ở GitHub Issues; lỗ hổng bảo mật báo riêng theo SECURITY.md trong repo, không đăng công khai; câu hỏi về tài khoản, API key và booking gửi hi@ferry.vn.
| Việc cần tra | Nơi tra |
|---|---|
| Lệnh, tuỳ chọn và kiểu dữ liệu của SDK | README gói SDK trên GitHub |
| Route, tham số và schema HTTP | Scalar, nhóm Bookings, Voyages, Customers |
| Mã lỗi nghiệp vụ | Lỗi và cách xử lý |
Đọc tiếp
Phần tiêu đề “Đọc tiếp”| Trang | Khi nào đọc |
|---|---|
CLI ferry | Thao tác tay hoặc dựng booking từ file CSV mà không viết code |
| MCP cho AI agent | Cho Claude tra chuyến, giá và booking với giới hạn an toàn |
| Webhooks | Hiểu vì sao webhook chỉ là tín hiệu |
| Luồng đặt vé | Hiểu vòng đời booking mà SDK đang bọc |
| Quy ước chung | Phân trang, định dạng ngày và tiền |