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

Tích hợp SMS Banking

3 phút đọc

Hướng dẫn này dành cho bộ phận kỹ thuật của đối tác gửi dữ liệu giao dịch chuyển khoản (SMS Banking) đến Ferry VN. Hệ thống xác thực chữ ký và xử lý giao dịch theo nội dung chuyển khoản.

Khi khách hàng chuyển khoản thanh toán vé tàu qua ngân hàng, hệ thống đối tác tiếp nhận biến động số dư và chuyển tiếp dữ liệu đến cổng tiếp nhận (IPN) của Ferry VN.

Sơ đồ dưới đây cho thấy luồng truyền dữ liệu giao dịch từ hệ thống đối tác tới Ferry VN:

sequenceDiagram
autonumber
actor Khach as Khách hàng
participant Bank as Ngân hàng
participant Partner as Hệ thống Đối tác
participant Ferry as Hệ thống Ferry
Khach->>Bank: Chuyển khoản theo cú pháp đơn
Bank->>Partner: Thông báo biến động số dư
Partner->>Partner: Tạo chữ ký Secure Hash
Partner->>Ferry: Gửi thông báo giao dịch IPN
Ferry->>Ferry: Xác thực chữ ký và đối soát đơn
Ferry-->>Partner: Phản hồi kết quả ghi nhận

Để kết nối với hệ thống Ferry VN, đại lý cần kích hoạt tính năng và lấy các thông số xác thực bảo mật:

  • Mã truy cập (Access Code): Mã định danh tổ chức do Ferry VN cấp, dùng để định tuyến yêu cầu IPN đến đúng đại lý.
  • Secure Hash: Khóa bí mật dạng Hex dùng để tạo và xác thực chữ ký HMAC SHA256 cho mỗi giao dịch.

Các thông số này được quản lý và cấu hình trực tiếp tại trang Cài đặt thanh toán trên Dashboard đại lý.


Quy trình tạo chữ ký số (ferry_SecureHash)

Phần tiêu đề “Quy trình tạo chữ ký số (ferry_SecureHash)”

Để đảm bảo toàn vẹn dữ liệu và ngăn chặn can thiệp trên đường truyền, mọi yêu cầu IPN gửi đến Ferry VN bắt buộc phải đi kèm tham số chữ ký số ferry_SecureHash.

Các bước tạo chữ ký:

  1. Lọc tham số: Lọc tất cả các cặp khóa - giá trị có tiền tố ferry_ và có dữ liệu, ngoại trừ khóa ferry_SecureHash.
  2. Sắp xếp thứ tự: Sắp xếp danh sách các khóa đã lọc theo thứ tự bảng chữ cái ASCII tăng dần.
  3. Tạo chuỗi ký: Ghép các cặp khóa và giá trị thành chuỗi theo định dạng truy vấn key1=value1&key2=value2&... (không thực hiện mã hóa URL đối với giá trị khi ghép chuỗi).
  4. Băm dữ liệu HMAC SHA256: Sử dụng thuật toán HMAC SHA256 với khóa bí mật là chuỗi nhị phân chuyển đổi từ Hex của secure_hash, thực hiện băm chuỗi dữ liệu ở bước 3.
  5. Chuyển đổi chữ hoa: Chuyển chuỗi băm kết quả sang dạng chuỗi Hex viết hoa (uppercase) và gán vào tham số ferry_SecureHash.
import crypto from 'crypto';
/**
* Tạo mã chữ ký số bảo mật ferry_SecureHash
* @param {Record<string, string>} params - Đối tượng chứa các tham số trong yêu cầu IPN
* @param {string} secureHashKey - Khóa bảo mật dạng Hex do Ferry VN cấp
* @returns {string} Chuỗi chữ ký HMAC SHA256 viết hoa
*/
function generateSecureHash(params, secureHashKey) {
// 1. Lọc các khóa có tiền tố ferry_ (trừ ferry_SecureHash) và có giá trị
const ferryKeys = Object.keys(params).filter(
(key) => key.startsWith('ferry_') && key !== 'ferry_SecureHash' && params[key]
);
// 2. Sắp xếp các khóa theo thứ tự ASCII tăng dần
ferryKeys.sort();
// 3. Ghép thành chuỗi dữ liệu theo định dạng URL query string
const stringToHash = ferryKeys
.map((key) => `${key}=${params[key]}`)
.join('&');
// 4. Tạo chữ ký HMAC SHA256 với khóa Hex Buffer và xuất chuỗi Hex chữ hoa
return crypto
.createHmac('sha256', Buffer.from(secureHashKey, 'hex'))
.update(stringToHash)
.digest('hex')
.toUpperCase();
}
import hmac
import hashlib
def generate_secure_hash(params: dict, secure_hash_key: str) -> str:
# 1. Lọc các khóa có tiền tố ferry_ (trừ ferry_SecureHash) và có giá trị
ferry_keys = [
k for k, v in params.items()
if k.startswith('ferry_') and k != 'ferry_SecureHash' and v
]
# 2. Sắp xếp các khóa theo thứ tự ASCII tăng dần
ferry_keys.sort()
# 3. Ghép thành chuỗi dữ liệu theo định dạng URL query string
string_to_hash = '&'.join(f'{k}={params[k]}' for k in ferry_keys)
# 4. Tạo chữ ký HMAC SHA256 với khóa Hex bytes và xuất chuỗi Hex chữ hoa
key_bytes = bytes.fromhex(secure_hash_key)
signature = hmac.new(key_bytes, string_to_hash.encode('utf-8'), hashlib.sha256)
return signature.hexdigest().upper()

Các mẫu mã nhận giá trị tham số ở dạng chuỗi, kể cả số tiền. Không URL-encode giá trị khi tạo chuỗi ký. Sau khi thêm ferry_SecureHash, URL-encode các khóa và giá trị khi tạo query string để gửi yêu cầu HTTP.

Dữ liệu kết quả giao dịch được gửi qua giao thức HTTP GET đến endpoint IPN tương ứng với mã truy cập của đại lý.

Để tra cứu danh sách chi tiết các tham số bắt buộc trong yêu cầu, ý nghĩa từng trường dữ liệu và các mã trạng thái phản hồi HTTP của hệ thống, vui lòng tham khảo Tài liệu API Ferry (Scalar).


  • Tính duy nhất của mã giao dịch: Mỗi mã tham chiếu giao dịch (ferry_TxnRef) chỉ được hệ thống tiếp nhận và xử lý một lần duy nhất. Nếu đối tác gửi lại yêu cầu với mã giao dịch đã từng xử lý trước đó, hệ thống sẽ bỏ qua để tránh trùng lặp ghi nhận tài chính.

  • Quy đổi số tiền: Số tiền giao dịch phải là số không âm. Nhân số tiền với 100 rồi chuyển thành chuỗi cho ferry_TransactionAmount trước khi tính chữ ký và gửi dữ liệu (ví dụ 50.000 VNĐ thành 5000000).

  • Bảo mật khóa ký: Khóa bí mật secure_hash có quyền hạn tương đương chìa khóa xác nhận tài chính. Khóa này phải được bảo vệ trên môi trường máy chủ phụ trợ (backend) của đối tác và tuyệt đối không chia sẻ qua các kênh không mã hóa.

  • Nội dung chuyển khoản: ferry_TransactionMessage không được để trống. Với thanh toán đơn vé, nội dung phải khớp mã thanh toán còn hiệu lực do hệ thống cung cấp. Giao dịch không khớp được ghi nhận là giao dịch không xác định, không tự động thanh toán đơn.

  • Khóa bị lộ: Trong Cài đặt → Thanh toán → SMS Banking, dùng nút Làm mới cạnh Secure Hash để cấp khóa mới khi nghi ngờ khóa đã bị lộ.