Bỏ qua để đến nội dung

Huỷ vé và hoàn tiền

13 phút đọc

Quy trình huỷ vé và hoàn tiền xử lý trường hợp hành khách không tiếp tục chuyến đi sau khi vé đã được xuất thành công. Đối tác tích hợp qua API chỉ có quyền tạo yêu cầu huỷ và xem danh sách yêu cầu của tổ chức mình; các bước duyệt thuộc quyền của các cấp tổ chức phía trên và tổ chức gốc.

Khách đã có vé nhưng không đi nữa. Bề mặt của đối tác ở luồng này chỉ có hai việc: tạo một yêu cầu huỷ, và xem danh sách yêu cầu huỷ của tổ chức mình. Bạn không duyệt được, không từ chối được, và không thu hồi được. Ba thao tác đó nằm ở tầng nội bộ của Ferry và của các tổ chức cấp trên bạn.

Hệ quả thiết kế: đừng vẽ màn hình duyệt huỷ vé trong sản phẩm của bạn. Màn hình bạn cần dựng là màn hình theo dõi tiến độ: yêu cầu đang nằm ở cấp nào, đã được duyệt tới đâu, và tiền đã hoàn về chưa.

Điều kiện cầnVì saoĐọc ở đâu
Booking đang ở trạng thái đã có véChỉ vé đã xuất mới huỷ đượcĐặt vé: vòng đời một booking
Biết chuỗi tổ chức phía trên dài bao nhiêu cấpSố cấp quyết định thời gian duyệtTổng quan bề mặt tích hợp
Tổ chức của bạn còn đủ số dưYêu cầu huỷ bị từ chối ngay nếu không đủ tiền trả phíTiền: số dư, hạn mức và công nợ
Đã khai địa chỉ nhận sự kiệnLuồng này phát nhiều sự kiện trong hệ thốngWebhooks
Hiểu ba con số giáSố tiền hoàn tính trên giá NET ở góc nhìn từng cấpGiá vé và markup nhiều cấp

Sơ đồ dưới đây cho thấy chuỗi duyệt đi ngược lên từ bạn tới tổ chức gốc, kèm nhánh bị từ chối:

sequenceDiagram
autonumber
participant P as "Hệ thống của bạn"
participant F as "Ferry API"
participant U as "Đại lý cấp trên"
participant R as "Tổ chức gốc"
participant C as "Tác vụ ghi có"
participant W as "Địa chỉ nhận webhook"
P->>F: "Gửi yêu cầu huỷ vé kèm lý do"
F->>F: "Dựng danh sách toàn chuỗi tổ chức"
F->>F: "Kiểm tra số dư của bạn"
alt Không đủ tiền trả phí huỷ
F-->>P: "Yêu cầu bị từ chối ngay, không vào chuỗi duyệt"
else Đủ tiền
F-->>P: "Yêu cầu ở trạng thái đang xử lý"
F--)W: "Sự kiện yêu cầu huỷ đã được tạo"
U->>F: "Cấp trên trực tiếp duyệt"
Note over F: "Trạng thái riêng của cấp đổi,<br/>trạng thái chung vẫn đang xử lý"
alt Một cấp bất kỳ từ chối
U->>F: "Từ chối kèm lý do"
F--)W: "Sự kiện bị từ chối"
F-->>P: "Cả yêu cầu bị từ chối, vé còn nguyên"
else Duyệt hết lên tới gốc
R->>F: "Tổ chức gốc duyệt"
F->>F: "Nhả ghế và voucher"
F->>C: "Ghi có cho từng tổ chức con trong chuỗi"
C-->>F: "Ghi có xong hoặc chờ lượt sau"
F--)W: "Sự kiện đã hoàn tiền"
F-->>P: "Trạng thái chung chuyển sang đã hoàn tiền"
end
end
loop Theo dõi tiến độ duyệt
P->>F: "Tra cứu trạng thái yêu cầu huỷ"
F-->>P: "Trạng thái chung và trạng thái riêng từng cấp"
end
BướcHệ thống của bạn làm gìFerry làm gìDấu hiệu thành côngLỗi thường gặp
1Kiểm tra booking đang ở trạng thái đã xuất vé—Booking ở trạng thái đã có véTạo yêu cầu cho booking chưa xuất vé
2Gửi lệnh tạo yêu cầu huỷ kèm vé và lý doDựng danh sách toàn chuỗi tổ chức, kiểm tra số dưNhận yêu cầu ở trạng thái đang xử lýSố dư không đủ khiến yêu cầu bị từ chối ngay
3Lưu mã yêu cầu huỷ vào cơ sở dữ liệu của bạn—Mã yêu cầu đã lưu trong hệ thống của bạnChỉ giữ trong bộ nhớ tạm, mất dấu khi tra cứu
4Thông báo khách rằng yêu cầu đang chờ duyệt nhiều cấpChờ từng cấp thao tácKhách hiểu tiến độ cần duyệt qua từng cấpHứa hẹn mốc thời gian hoàn tiền cố định
5Theo dõi trạng thái chung và trạng thái từng cấpCập nhật trạng thái riêng của cấp vừa duyệtNhìn rõ đang chờ cấp nào duyệtChỉ đọc trạng thái chung rồi nghĩ hệ thống bị treo
6Khi thấy đã hoàn tiền, đối chiếu nhật ký giao dịchNhả ghế, nhả voucher, ghi có cho tổ chức conTìm thấy giao dịch hoàn tiền tương ứngBáo khách đã hoàn xong trước khi tiền thật về
7Nếu bị từ chối, đọc lý do và xử lý lại với kháchGhi nhận lý do từ chối vào yêu cầuCó lý do cụ thể để giải thích cho kháchTạo lại yêu cầu mới khi chưa giải quyết nguyên nhân

Sơ đồ dưới đây cho thấy vòng đời chuyển đổi trạng thái của một yêu cầu huỷ vé:

stateDiagram-v2
direction LR
state "Đang xử lý" as processing
state "Đã duyệt" as accept
state "Bị từ chối" as reject
state "Đã thu hồi" as recall
state "Đã hoàn tiền" as refunded
[*] --> processing : Tạo yêu cầu thành công và đủ số dư
processing --> accept : Một cấp trong chuỗi duyệt
accept --> processing : Vẫn còn cấp phía trên chưa duyệt
accept --> refunded : Tổ chức gốc duyệt, hệ thống nhả ghế
processing --> reject : Bất kỳ cấp nào từ chối
processing --> recall : Khách thu hồi yêu cầu
accept --> recall : Khách thu hồi khi gốc chưa duyệt
NhãnÝ nghĩaBạn làm gì tiếp
Đang xử lýYêu cầu đã vào chuỗi duyệt, chưa hoàn tấtTheo dõi tiến độ, không thao tác gì thêm
Đã duyệtMột hoặc vài cấp đã duyệt, chưa tới tổ chức gốcTiếp tục theo dõi, đọc trạng thái riêng để biết đang chờ cấp nào
Bị từ chốiMột cấp bất kỳ đã từ chối, có kèm lý doĐọc lý do, làm việc lại với khách, vé vẫn còn nguyên
Đã thu hồiKhách đã rút lại yêu cầuKhông thao tác được nữa, mọi lệnh khác bị chặn
Đã hoàn tiềnTổ chức gốc đã duyệt, ghế và voucher đã nhảĐối chiếu nhật ký giao dịch, báo khách sau khi thấy tiền về

Ngoài trạng thái chung ở bảng trên, mỗi tổ chức trong chuỗi còn mang trạng thái riêng trong danh sách tổ chức liên quan của yêu cầu.

Đây là điểm mấu chốt: khi cấp trên trực tiếp của bạn đã duyệt mà cấp cao hơn chưa duyệt, trạng thái chung vẫn hiển thị là đang xử lý. Nhìn vào trạng thái chung sẽ không thấy chuyển động, trong khi thực tế yêu cầu đã đi lên được một bậc.

Để biết chính xác đang chờ cấp nào thao tác, bạn cần đọc chi tiết yêu cầu và xem trạng thái riêng của từng cấp. Hãy hiển thị thông tin này trên màn hình chăm sóc khách hàng của bạn.

Một quy tắc chặn: khi yêu cầu ở trạng thái Đã thu hồi, mọi thao tác can thiệp khác đều bị chặn hoàn toàn. Không thể duyệt, không thể từ chối, và không thể chỉnh sửa.

BướcDiễn biến trong hệ thống
1Tạo yêu cầu: hệ thống dựng danh sách toàn chuỗi tổ chức từ bạn lên tới gốc, mỗi cấp có trạng thái riêng. Hệ thống kiểm tra số dư — thiếu tiền thì yêu cầu bị từ chối ngay.
2Từng cấp duyệt từ dưới lên. Cấp cha chỉ duyệt được khi cấp con trực tiếp đã duyệt.
3Cấp nào duyệt thì trạng thái riêng của cấp đó đổi, trạng thái chung toàn yêu cầu vẫn là đang xử lý.
4Tổ chức gốc duyệt: trạng thái chung chuyển sang đã hoàn tiền, hệ thống nhả ghế và nhả voucher.
5Bất kỳ cấp nào từ chối: toàn bộ yêu cầu chuyển sang bị từ chối, kèm lý do cụ thể.
6Khách thu hồi: trạng thái chuyển thành đã thu hồi. Không thể thu hồi nếu tổ chức gốc đã duyệt xong.

Bạn tạo yêu cầu xong thì không thể can thiệp thêm vào quá trình duyệt. Thời gian hoàn tất phụ thuộc vào số cấp tổ chức phía trên và tốc độ thao tác của từng cấp. Hệ thống không có hạn chót tự động cho từng bước duyệt và không có cơ chế tự duyệt khi quá hạn.

Lệnh tạo yêu cầu huỷ và lệnh liệt kê yêu cầu huỷ chỉ kiểm tra tính hợp lệ của khoá API, không phân quyền chi tiết theo vai trò nội bộ. Mọi khoá API hợp lệ của tổ chức bạn đều có thể tạo yêu cầu huỷ vé.

Về mặt an toàn, nếu khoá API bị lộ, người cầm khoá có thể tạo yêu cầu huỷ cho bất kỳ vé nào của tổ chức bạn. Hãy tự xây dựng lớp kiểm soát phân quyền ở phía hệ thống của bạn trước khi gọi API này.

ChặngCơ chếThời gian
Ghi có cho tổ chức conHệ thống thử ghi có ngay khi tổ chức gốc duyệtTức thì nếu xử lý thành công
Lần ghi có thất bạiTác vụ định kỳ quét lại các khoản chờ xử lýMỗi 15 phút một lượt
Bản ghi treo giữa chừngThu hồi về hàng chờ để thử lạiSau 10 phút
Thất bại sau nhiều lượtHệ thống phát cảnh báo tài chínhSau 3 lần thất bại liên tiếp
Tiền của khách hàng cuốiGhi bằng loại giao dịch hoàn tiền riêngCùng lúc với bước tổ chức gốc duyệt

Thao tác ghi có an toàn khi lặp lại, được khoá theo mã yêu cầu huỷ. Tác vụ định kỳ chạy lại sẽ không cộng tiền hai lần, bạn không cần tự chống trùng cho khoản tiền hoàn này.

Tuy nhiên, lệnh tạo yêu cầu huỷ không có khoá chống lặp. Khi gặp timeout, hãy tra cứu danh sách yêu cầu trước thay vì gửi lệnh tạo mới ngay lập tức.

Trạng thái thanh toán trước huỷSau khi huỷ xong
Chưa trảĐã huỷ
Trả thiếuĐã huỷ — hệ thống không có trạng thái hoàn một phần
Đã trảĐã hoàn
Đã trả bằng nợĐã hoàn — phần tiền mặt được hoàn lại, phần nợ được xoá

Lưu ý: hệ thống không có trạng thái “hoàn tiền một phần”. Booking trả thiếu bị huỷ sẽ mang trạng thái đã huỷ, và phần tiền đã thu được đối soát qua nhật ký giao dịch chứ không thể hiện riêng ở trạng thái đơn.

Để không phải liên tục gọi API tra cứu trạng thái, bạn nên đăng ký các sự kiện sau:

  • Yêu cầu huỷ được tạo
  • Yêu cầu huỷ được tạo bởi tổ chức con
  • Yêu cầu huỷ được cập nhật
  • Yêu cầu huỷ được duyệt
  • Yêu cầu huỷ bị từ chối
  • Yêu cầu huỷ được duyệt kèm hoàn tiền
  • Yêu cầu huỷ đã hoàn tiền
  • Yêu cầu huỷ bị thu hồi
  • Vé đã được nhả

Chi tiết về cách khai báo địa chỉ nhận và các lưu ý vận hành xem tại Webhooks.

Triệu chứngNguyên nhânCách xử lýCó nên gọi lại không
Yêu cầu bị từ chối ngay khi vừa tạoSố dư không đủ để trả phí huỷ (mã lỗi 160105)Nạp tiền vào tài khoản tổ chức rồi tạo lạiKhông — gọi lại khi chưa nạp tiền vẫn thất bại
Trạng thái chung đứng yên nhiều giờChuỗi duyệt đang chờ một cấp phía trên thao tácĐọc trạng thái riêng từng cấp để biết đang chờ ai, sau đó liên hệ cấp đóKhông — gọi lại API không đẩy nhanh tiến độ duyệt
Không tìm thấy lệnh duyệt yêu cầuĐối tác không có quyền duyệt, đây là thiết kế hệ thốngCấp trên trong chuỗi tổ chức sẽ duyệt trên giao diện quản trịKhông áp dụng
Yêu cầu chuyển sang đã thu hồiKhách đã rút lại yêu cầu qua kênh khácChấp nhận trạng thái này; mọi thao tác khác bị khoáKhông
Trạng thái đã hoàn tiền nhưng tiền chưa vềTác vụ ghi có chạy theo chu kỳ 15 phútChờ hết chu kỳ, kiểm tra nhật ký giao dịch để xác nhậnKhông
Tiền không về sau nhiều chu kỳGhi có thất bại nhiều lần, hệ thống đã phát cảnh báoLiên hệ Ferry hỗ trợ kèm mã yêu cầu huỷKhông
Không nhận được sự kiện webhook nàoĐiều kiện lọc của địa chỉ nhận sai cú phápGỡ bỏ hoặc sửa điều kiện lọc, thử lại bằng thao tác thậtKhông
Hai yêu cầu huỷ trùng nhau cho cùng một véĐã gọi lại lệnh tạo sau khi gặp sự cố timeoutLiên hệ Ferry để huỷ bản ghi bị trùngKhông

Trang này tương ứng với nhóm Cancel Ticket Requests trên giao diện tra cứu https://api.ferry.vn/docs, cùng hai nhóm phụ trợ để đối chiếu dữ liệu:

Mục tiêu tra cứuNhóm trên giao diện API
Cấu trúc dữ liệu khi tạo yêu cầu huỷ véCancel Ticket Requests
Liệt kê danh sách yêu cầu huỷ và tham số lọcCancel Ticket Requests
Giao dịch hoàn tiền tương ứngTransactions
Trạng thái booking sau khi hoàn tất huỷBookings

Trên giao diện tài liệu API, nhóm Cancel Ticket Requests chỉ hiển thị các endpoint mà đối tác được phép gọi (tạo và xem danh sách). Các endpoint duyệt, từ chối hay thu hồi thuộc quyền quản trị nội bộ nên không xuất hiện cho đối tác bên ngoài.

TrangĐọc khi nào
Đặt vé: vòng đời một bookingCần nắm trạng thái booking thay đổi ra sao trong lúc chờ duyệt huỷ
Tiền: số dư, hạn mức và công nợCần đối chiếu giao dịch hoàn tiền và chu kỳ ghi có định kỳ
Xuất vé và đơn hàngCần xử lý huỷ booking bị lỗi xuất vé một phần
WebhooksCần đăng ký sự kiện và tránh bẫy điều kiện lọc cú pháp
Giá vé và markup nhiều cấpCần hiểu cách tính số tiền hoàn theo từng cấp tổ chức
Tổng quan bề mặt tích hợpCần hiểu cấu trúc chuỗi duyệt nhiều cấp
Câu hỏiTình trạng
Có cam kết thời hạn cho từng bước duyệt không?Hệ thống không đặt hạn chót tự động cho bước duyệt của từng cấp; thời gian thực tế phụ thuộc quy trình vận hành của các bên.
Phí huỷ vé tính theo công thức nào và ai quyết định?Phụ thuộc chính sách huỷ của từng hãng tàu và quy định của từng cấp tổ chức; hiện chưa có API mở để đối tác tự tra cứu bảng phí.
Khách thu hồi yêu cầu bằng kênh nào?Chưa xác định kênh thao tác; API key đối tác không thể thu hồi hộ khách.