- Nhà phát triển
- Webhooks
Webhooks
Ferry hỗ trợ gửi webhook tới địa chỉ tiếp nhận của đối tác khi phát sinh các sự kiện nghiệp vụ trong hệ thống. Cơ chế này được thiết kế theo nguyên tắc báo hiệu nhanh, yêu cầu đối tác xác nhận phản hồi tức thì và chủ động tra cứu ngược lại API để lấy dữ liệu chi tiết.
Luồng này giải quyết việc gì
Phần tiêu đề “Luồng này giải quyết việc gì”Ferry gửi sự kiện webhook ra ngoài tới địa chỉ mà tổ chức của bạn khai báo. Tuy nhiên, cơ chế gửi có ba đặc điểm cốt lõi bạn cần nắm rõ trước khi triển khai tích hợp:
- Không có chữ ký số: Không có HMAC, không có chuỗi bí mật chia sẻ, không có dấu thời gian và không có mã dùng một lần (nonce). Bạn không thể xác thực nguồn gốc yêu cầu chỉ dựa vào gói tin nhận được.
- Không có cơ chế thử lại: Nếu gửi thất bại hoặc kết nối lỗi, hệ thống chỉ ghi log phía máy chủ và bỏ qua. Sự kiện bị mất vĩnh viễn.
- Hạn chờ phản hồi cứng 2.000 mili-giây: Nếu địa chỉ tiếp nhận của bạn phản hồi chậm hơn 2 giây, kết nối bị ngắt và sự kiện không được gửi lại.
Ba đặc điểm này dẫn đến nguyên tắc kiến trúc nhất quán: dùng webhook làm tín hiệu, dùng polling làm chốt chặn. Webhook cho bạn biết có sự kiện vừa xảy ra, sau đó hệ thống của bạn gọi ngược lại API để lấy dữ liệu chính xác nhất.
Trước khi bắt đầu
Phần tiêu đề “Trước khi bắt đầu”| Điều kiện cần | Vì sao | Đọc ở đâu |
|---|---|---|
| Khoá API có quyền quản trị cài đặt tổ chức | Dùng để khai báo địa chỉ tiếp nhận qua API | Xác thực |
| Địa chỉ nhận chạy HTTPS công khai, phản hồi dưới 2 giây | Hạn chờ xử lý tối đa là 2.000 ms | Mục “Cơ chế gửi” bên dưới |
| Hàng đợi tiếp nhận nội bộ phía bạn | Trả mã 200 ngay, xử lý nghiệp vụ sau | Mục “Mẫu xử lý an toàn” bên dưới |
| Tác vụ đối chiếu dữ liệu định kỳ | Bù đắp cho các sự kiện có thể bị rơi mất | Mục “Mẫu xử lý an toàn” bên dưới |
| Danh sách sự kiện cần đăng ký | Chọn đúng sự kiện thực tế phát sinh | Mục “Danh sách sự kiện” bên dưới |
Sơ đồ luồng
Phần tiêu đề “Sơ đồ luồng”Sơ đồ dưới đây cho thấy đường đi của một sự kiện từ khi phát sinh bên trong Ferry đến địa chỉ tiếp nhận của bạn:
flowchart TB A["Nghiệp vụ phát sinh trong Ferry"] --> B["Hệ thống bắt sự kiện"] B --> C["Tra cứu các tổ chức đăng ký sự kiện này"] C --> D{"Tổ chức có khai báo địa chỉ nhận không"} D -->|Không| Z["Dừng, không gửi gì"] D -->|Có| E["Duyệt qua từng địa chỉ tiếp nhận"] E --> F{"Địa chỉ có điều kiện lọc không"} F -->|Không có| H["Gửi sự kiện"] F -->|Có| G{"Đánh giá điều kiện trên dữ liệu"} G -->|Không khớp| Y["Bỏ qua địa chỉ này"] G -->|Khớp| H G -->|Điều kiện sai cú pháp| X["Lỗi ngắt quãng luồng xử lý<br/>có thể làm mất sự kiện"] H --> I["Gửi dữ liệu tới địa chỉ của bạn<br/>hạn chờ 2000 ms<br/>tiêu đề x-event-type<br/>không chữ ký số"] I --> J{"Địa chỉ phản hồi trong 2 giây"} J -->|Có| K["Hoàn tất gửi webhook"] J -->|Không hoặc lỗi| L["Ghi log phía Ferry<br/>không thử lại<br/>sự kiện mất vĩnh viễn"]Sơ đồ dưới đây cho thấy mẫu xử lý sự kiện an toàn phía đối tác:
sequenceDiagram autonumber participant P as "Hệ thống của bạn" participant Q as "Hàng đợi nội bộ" participant F as "Ferry API"
F--)P: "Sự kiện webhook tới kèm tiêu đề loại sự kiện" P->>Q: "Ghi nhận vào hàng đợi, chưa xử lý nghiệp vụ" P-->>F: "Trả lời HTTP 200 trong vòng dưới 2 giây"
Q->>P: "Tiến trình nội bộ lấy dữ liệu ra xử lý" P->>P: "Khử trùng lặp theo mã tài nguyên" P->>F: "Gọi API tra cứu tài nguyên để lấy sự thật" F-->>P: "Dữ liệu trạng thái thực tế" P->>P: "Cập nhật hệ thống theo dữ liệu từ API"
alt Không nhận được sự kiện nào trong khoảng thời gian dự kiến P->>P: "Tác vụ đối chiếu định kỳ kích hoạt" P->>F: "Tra cứu danh sách tài nguyên thay đổi gần đây" F-->>P: "Danh sách thực tế" P->>P: "Bù đắp dữ liệu cho các sự kiện bị rơi mất" endCác bước thực hiện
Phần tiêu đề “Các bước thực hiện”| Bước | Hệ thống của bạn làm gì | Ferry làm gì | Dấu hiệu thành công | Lỗi thường gặp |
|---|---|---|---|---|
| 1 | Dựng địa chỉ tiếp nhận HTTPS công khai với đường dẫn khó đoán | — | Địa chỉ trả về 200 khi gọi thử | Dùng đường dẫn mặc định dễ đoán như /webhook |
| 2 | Phản hồi HTTP 200 ngay lập tức, đưa dữ liệu vào hàng đợi | — | Thời gian phản hồi luôn dưới 2 giây kể cả lúc tải cao | Xử lý nghiệp vụ đồng bộ khiến quá hạn 2 giây |
| 3 | Cấu hình địa chỉ nhận và sự kiện trong cài đặt tổ chức | Lưu cấu hình theo kênh tích hợp | Kiểm tra lại cài đặt thấy địa chỉ vừa lưu | Sợ mất cấu hình khác nên không dám cập nhật |
| 4 | Thực hiện thao tác thật để kiểm chứng luồng sự kiện | Phát sự kiện tương ứng | Địa chỉ tiếp nhận nhận được gói tin | Tin rằng đăng ký là sẽ nhận mà không kiểm thử |
| 5 | Khử trùng theo mã tài nguyên và gọi lại API lấy dữ liệu thật | Trả dữ liệu chuẩn của tài nguyên | Dữ liệu lưu trữ khớp với kết quả API | Ghi thẳng dữ liệu từ webhook vào cơ sở dữ liệu |
| 6 | Kích hoạt tác vụ đối chiếu định kỳ | — | Tác vụ phát hiện được dữ liệu lệch khi bỏ qua sự kiện | Bỏ qua bước đối chiếu định kỳ |
| 7 | Giám sát khoảng lặng của luồng sự kiện | — | Cảnh báo kích hoạt khi không có sự kiện quá lâu | Coi việc im lặng kéo dài là hệ thống ổn định |
Trạng thái
Phần tiêu đề “Trạng thái”Webhook không có vòng đời trạng thái riêng. Mỗi sự kiện chỉ có hai kết quả: đến nơi hoặc bị rơi mất. Hệ thống không lưu trữ hàng đợi gửi lại và không có giao diện lịch sử gửi webhook để tra cứu.
Trạng thái cần theo dõi nằm ở chính tài nguyên được thông báo (đơn hàng, booking, yêu cầu huỷ vé). Đối tác lấy trạng thái thực tế bằng cách gọi lại API tương ứng.
Giới hạn và bất biến
Phần tiêu đề “Giới hạn và bất biến”Cấu hình ở đâu
Phần tiêu đề “Cấu hình ở đâu”Địa chỉ nhận webhook được cấu hình trong phần tích hợp của cài đặt tổ chức. Đối tác có hai cách thiết lập:
- Giao diện Dashboard: Truy cập trang Cài đặt thông báo và tích hợp sự kiện để quản trị URL và chọn danh sách sự kiện cần nhận.
- API cài đặt tổ chức: Cập nhật trực tiếp bằng khoá API của tổ chức.
Mỗi cấu hình webhook bao gồm ba thông tin:
- Địa chỉ nhận (URL): Địa chỉ HTTPS hợp lệ tiếp nhận phương thức
POST. - Danh sách sự kiện đăng ký: Danh sách các mã sự kiện tổ chức muốn lắng nghe.
- Điều kiện lọc (tuỳ chọn): Biểu thức lọc dữ liệu trước khi gửi.
Thao tác cập nhật cài đặt được gộp theo từng kênh cấu hình, không ghi đè hay làm mất các thiết lập khác của tổ chức.
Cơ chế gửi — mục quan trọng nhất của trang
Phần tiêu đề “Cơ chế gửi — mục quan trọng nhất của trang”| Hạng mục | Thực tế vận hành | Bạn phải làm gì |
|---|---|---|
| Phương thức HTTP | Luôn gửi POST tới URL khai báo | Điểm tiếp nhận phải nhận phương thức POST |
| Nhãn sự kiện | Nằm trong tiêu đề HTTP x-event-type | Đọc giá trị tiêu đề này để điều hướng xử lý nội bộ |
| Chữ ký số | Không có. Không HMAC, không secret, không timestamp | Coi webhook là tín hiệu, gọi ngược API để lấy dữ liệu thật. Dùng URL khó đoán và lọc IP nếu khả thi |
| Hạn chờ | 2.000 mili-giây cứng | Nhận gói tin, đẩy vào hàng đợi, phản hồi 200 ngay |
| Thử lại | Không có. Gặp lỗi chỉ ghi log máy chủ rồi bỏ qua | Bắt buộc chạy tác vụ đối chiếu định kỳ |
| Thứ tự sự kiện | Không đảm bảo. Các sự kiện gửi độc lập | Không xây dựng máy trạng thái dựa trên thứ tự nhận sự kiện |
| Hàng đợi phía Ferry | Không có hàng đợi lưu trữ để gửi lại | Sự kiện không tới nơi nghĩa là đã mất hẳn |
Danh sách sự kiện
Phần tiêu đề “Danh sách sự kiện”Danh sách mã sự kiện trong hệ thống bao gồm:
- Tổ chức:
organization.created,organization.updated,organization.disabled,organization.enabled,child-organization.created - Booking:
booking.created,booking.updated,booking.issuedTicket,booking.issuedTicketError,booking.cancelOrder,booking.hold_payment,booking.canceled_payment - Đơn hàng:
order.created,order.updated,order.issuedTicket,order.childOrgIssuedTicket,order.childOrgIssuedTicketError,order.newManualIssueOrder,order.canceled,order.cancelTicket,order.forcedCanceled - Yêu cầu huỷ vé:
cancel_ticket_request.created,.child_org_created,.updated,.accepted,.rejected,.accepted_refunded,.refunded,.recalled,.release_ticket - Khách hàng:
customer.created,customer.updated,customer.deleted - Công ty:
company.created,company.updated,company.deleted - Khách hàng tiềm năng (Lead):
lead.created,lead.updated,lead.deleted - Người dùng:
user.created,user.updated,user.deleted,user.created-with-profile - Khoá API:
apiKey.created,apiKey.updated,apiKey.deleted - Chuyến:
voyage.created,voyage.created.manually,voyage.updated,voyage.disabled,voyage.has-order-voyage-disabled - Theo dõi vé:
ticketWatchlist.targetReached - Hoá đơn:
invoice.created,invoice.updated,invoice.published,invoice.failed,invoice.canceled,invoice.orphanReconcileAlert
Khai được không có nghĩa là sẽ phát sự kiện
Phần tiêu đề “Khai được không có nghĩa là sẽ phát sự kiện”Danh sách sự kiện cho phép đăng ký rộng hơn số lượng sự kiện thực tế được kích hoạt trong hệ thống. Một số mã sự kiện tuy có trong danh sách nhưng không có bộ lắng nghe nào phát ra webhook.
Quy trình kiểm chứng bắt buộc: sau khi đăng ký sự kiện, hãy thực hiện một thao tác thử nghiệm thực tế để xác nhận địa chỉ của bạn đã nhận được webhook tương ứng trước khi đưa vào nghiệp vụ chính thức.
Trong nhóm tổ chức, chỉ child-organization.created thực sự phát webhook. Bốn mã organization.created, organization.updated, organization.disabled và organization.enabled đăng ký được nhưng không phát; đừng dùng chúng làm nguồn cập nhật duy nhất.
Đối tượng nhận sự kiện theo cấu trúc tổ chức
Phần tiêu đề “Đối tượng nhận sự kiện theo cấu trúc tổ chức”| Loại sự kiện | Tổ chức nhận được |
|---|---|
| Sự kiện của booking | Tổ chức sở hữu booking |
| Sự kiện xuất vé của đơn hàng | Tổ chức tạo đơn, kèm theo một sự kiện riêng cho từng tổ chức cấp trên trong chuỗi (với dữ liệu giá NET tính theo góc nhìn cấp đó) |
| Sự kiện tạo tổ chức con | Tổ chức cha trực tiếp, không phải tổ chức mới tạo |
Đối tác đa cấp lưu ý: bạn sẽ nhận hai loại sự kiện xuất vé khác nhau — một loại cho đơn của chính mình, một loại cho đơn của tổ chức con. Hai loại này mang nhãn và giá trị khác nhau, cần xử lý ở hai nhánh riêng biệt.
Dữ liệu trong webhook khác dữ liệu API trả về
Phần tiêu đề “Dữ liệu trong webhook khác dữ liệu API trả về”Nội dung gói tin webhook không đi qua tầng lọc dữ liệu theo vai trò như các phản hồi API thông thường. Do đó, webhook có thể chứa một số trường dữ liệu mà lệnh gọi API không trả về.
- Bảo mật: Coi nội dung webhook là dữ liệu nhạy cảm, không ghi log nguyên văn hoặc chuyển tiếp tùy tiện.
- Kỹ thuật: Không xây dựng nghiệp vụ cốt lõi phụ thuộc vào các trường chỉ xuất hiện trong webhook mà không có trong hợp đồng API.
Điều kiện lọc và rủi ro cú pháp
Phần tiêu đề “Điều kiện lọc và rủi ro cú pháp”Bộ lọc điều kiện cho phép chỉ nhận các sự kiện khớp với tiêu chí mong muốn. Một số quy tắc quan trọng:
- Hỗ trợ 6 toán tử so sánh cơ bản: bằng, khác, lớn hơn, lớn hơn hoặc bằng, nhỏ hơn, nhỏ hơn hoặc bằng.
- So sánh kiểu dữ liệu nghiêm ngặt: chuỗi
'1'không khớp với số1. - Truy cập trường lồng nhau qua dấu chấm (
order.code). - Kết hợp điều kiện bằng
and,or,not(trong đónotchỉ áp dụng cho nhánh con đầu tiên). - Không khai báo điều kiện lọc nghĩa là luôn nhận toàn bộ sự kiện của loại đó.
Bộ lọc webhook khác bộ lọc động của API: webhook chỉ hỗ trợ 6 toán tử so sánh, còn bộ lọc API có 13. Không sao chép nguyên điều kiện giữa hai hệ.
Ở luồng huỷ vé, điều kiện sai cú pháp có thể làm hỏng cả vòng gửi webhook của tổ chức, không chỉ bỏ qua sự kiện không khớp. Nếu không thật sự cần lọc phía Ferry, hãy nhận toàn bộ rồi lọc tại hệ thống của bạn.
Mẫu xử lý an toàn phía đối tác
Phần tiêu đề “Mẫu xử lý an toàn phía đối tác”- Phản hồi ngay dưới 2 giây: Nhận dữ liệu, đẩy vào hàng đợi nội bộ, trả về HTTP 200. Mọi bước xử lý nghiệp vụ thực hiện bất đồng bộ sau đó.
- Khử trùng lặp theo mã tài nguyên: Dùng mã booking, mã đơn hoặc mã yêu cầu huỷ kết hợp loại sự kiện làm khoá chống trùng lặp.
- Coi webhook là tín hiệu: Lấy mã định danh từ webhook rồi gọi lại API để lấy dữ liệu thực tế.
- Chạy đối chiếu định kỳ: Quét danh sách tài nguyên thay đổi định kỳ để bù đắp các sự kiện bị rơi mất.
- Giám sát khoảng lặng: Thiết lập cảnh báo khi không nhận được sự kiện nào trong một khoảng thời gian bất thường.
Lỗi thường gặp và cách xử lý
Phần tiêu đề “Lỗi thường gặp và cách xử lý”| Triệu chứng | Nguyên nhân | Cách xử lý | Có nên gọi lại không |
|---|---|---|---|
| Không nhận được sự kiện sau khi đăng ký | Sự kiện không có bộ phát trong hệ thống | Kiểm chứng bằng thao tác thật; chọn sự kiện thay thế | Không áp dụng |
| Nhận được lúc đầu rồi mất hẳn sự kiện | Điều kiện lọc sai cú pháp gây lỗi ngắt quãng | Xoá hoặc sửa điều kiện lọc, thử lại bằng thao tác thật | Không áp dụng |
| Dữ liệu sự kiện cũ hơn thực tế | Thứ tự gửi không đảm bảo | Luôn gọi lại API để lấy dữ liệu mới nhất | Không áp dụng |
| Mất sự kiện khi hệ thống của bạn tải cao | Phản hồi chậm quá 2 giây nên Ferry ngắt kết nối | Đổi sang mô hình nhận rồi đẩy hàng đợi; bù bằng đối chiếu | Không — sự kiện đã mất không gửi lại |
| Nhận hai lần cùng một sự kiện | Webhook không mang khóa chống trùng để đối chiếu | Khử trùng lặp theo mã tài nguyên và loại sự kiện | Không áp dụng |
| Nhận sự kiện xuất vé cho đơn không phải của mình | Sự kiện dành cho tổ chức cha trong chuỗi | Tách luồng xử lý riêng cho sự kiện của đơn tổ chức con | Không áp dụng |
| Cập nhật cài đặt lo bị mất cấu hình khác | Cập nhật gộp theo kênh | Đọc lại cài đặt để tự kiểm tra | Không áp dụng |
Tra cứu trên giao diện tài liệu API
Phần tiêu đề “Tra cứu trên giao diện tài liệu API”Trang này liên quan đến nhóm Organizations trên giao diện API tại https://api.ferry.vn/docs — nơi cung cấp các lệnh đọc và cập nhật cài đặt tổ chức:
| Mục tiêu tra cứu | Nhóm trên giao diện API |
|---|---|
| Lệnh cập nhật cài đặt tổ chức (khai báo URL webhook và sự kiện) | Organizations |
| Lệnh đọc cài đặt hiện tại của tổ chức để xác nhận | Organizations |
| Cấu trúc dữ liệu sự kiện nhóm booking | Bookings |
| Cấu trúc dữ liệu sự kiện nhóm đơn hàng | Orders |
| Cấu trúc dữ liệu sự kiện nhóm huỷ vé | Cancel Ticket Requests |
| Cấu trúc dữ liệu sự kiện nhóm khách hàng | Customers |
Giao diện tài liệu API mô tả schema của từng tài nguyên tương ứng với nội dung sự kiện phát ra. Các thông tin về thời hạn phản hồi, chữ ký số hay cơ chế thử lại chỉ được đặc tả tại trang tài liệu này.
Đọc tiếp
Phần tiêu đề “Đọc tiếp”| Trang | Đọc khi nào |
|---|---|
| Cài đặt thông báo | Cần cấu hình webhook trực quan trên giao diện Dashboard |
| Đặt vé: vòng đời một booking | Cần hiểu vì sao tra cứu trạng thái booking là nguồn dữ liệu chuẩn |
| Xuất vé và đơn hàng | Cần phân biệt hai loại sự kiện xuất vé cho tổ chức cha và tổ chức con |
| Huỷ vé và hoàn tiền | Cần danh sách sự kiện cần thiết cho luồng huỷ vé |
| Tiền: số dư, hạn mức và công nợ | Cần đối chiếu sự kiện thanh toán với sổ giao dịch |
| Quy ước chung | Cần nắm quy ước chung về dữ liệu và xử lý trùng lặp |
Điểm chưa xác định
Phần tiêu đề “Điểm chưa xác định”| Câu hỏi | Tình trạng |
|---|---|
| Kế hoạch bổ sung chữ ký số cho webhook | Hiện chưa có kế hoạch hỗ trợ; đối tác cần dùng đường dẫn bí mật và gọi lại API để xác thực. |
| Kế hoạch bổ sung cơ chế thử lại khi gửi lỗi | Hiện chưa hỗ trợ; đối tác bắt buộc duy trì tác vụ đối chiếu định kỳ. |
| Danh sách dải IP máy chủ gửi webhook | Chưa công bố danh sách IP chính thức; cần liên hệ Ferry nếu cần cấu hình tường lửa. |
| Sự kiện hoá đơn phát ra nhưng chưa có API đọc hoá đơn cho đối tác | Đang chờ chính sách mở lại từ phía sản phẩm; hiện đối tác nhận được tín hiệu nhưng chưa có API đọc dữ liệu chi tiết. |