SFI External API

Tài liệu tích hợp — Xác thực ngoài luồng (OOB) & dữ liệu giao dịch
https://a.sfi.com.vnChính
https://b.sfi.com.vnDự phòng
Về kết nối: nếu endpoint chính phản hồi chậm hơn 5 giây, hãy chuyển sang URL dự phòng. Dùng GET {base_url}/ekyc/tools/check_connection để kiểm tra trước khi bắt đầu luồng xác thực.

Tổng quan

External API (/ekyc/ex_api) cho phép ứng dụng của đối tác xác thực khách hàng SFI và truy cập dữ liệu giao dịch của họ.

Việc xác thực dùng luồng Out-of-Band (OOB) — khách hàng không nhập thông tin đăng nhập vào ứng dụng của đối tác. Thay vào đó, ứng dụng SFI CRM trên điện thoại khách hàng đóng vai trò thiết bị xác thực. Khách hàng duyệt hoặc từ chối yêu cầu đăng nhập, kết quả được trả về theo thời gian thực qua sự kiện Pusher WebSocket.

1
Yêu cầu đăng nhậpỨng dụng đối tác gửi yêu cầu đăng nhập tới SFI. SFI đẩy thông báo về điện thoại của khách hàng.
2
Xác thực kênh PusherỨng dụng đối tác đăng ký (subscribe) kênh Pusher riêng tư bằng temp_token.
3
Khách hàng duyệt / từ chốiKhách hàng bấm Duyệt hoặc Từ chối trong ứng dụng SFI CRM.
4
Nhận access tokenKhi được duyệt, ứng dụng đối tác nhận JWT access_token qua sự kiện Pusher, hiệu lực 24 giờ.

Sơ đồ luồng xác thực

Xem mô tả chi tiết luồng kết nối tại liên kết này
Sơ đồ luồng xác thực SFI x ứng dụng ngoài

Bước 1 — Yêu cầu đăng nhập

Tạo một phiên đăng nhập cho tài khoản khách hàng. SFI đẩy thông báo tới ứng dụng SFI CRM của khách hàng để duyệt hoặc từ chối.

POST

{base_url}/ekyc/ex_api

Headers

HeaderGiá trị
X-Api-KeyAPI key của bạn (do SFI cấp)
Content-Typeapplication/x-www-form-urlencoded

Body

TrườngKiểuBắt buộcMô tả
guestcmdstringCóPhải là login_request
accountstringCóSố tài khoản SFI của khách hàng (ví dụ 002C0012345)
app_namestringCóTên ứng dụng của bạn — được hiển thị cho khách hàng trong thông báo duyệt

Ví dụ request

POST /ekyc/ex_api
X-Api-Key: your_api_key
Content-Type: application/x-www-form-urlencoded

guestcmd=login_request&account=002C0012345&app_name=Your+App+Name

Phản hồi thành công

{
  "success": true,
  "message": "Thành công!",
  "data": {
    "session_id": "550e8400-e29b-41d4-a716-446655440000",
    "temp_token": "a3f1c2d4e5b6...",
    "expires_in": 300
  }
}
TrườngMô tả
session_idUUID định danh phiên này — dùng để subscribe kênh Pusher
temp_tokenToken hex 64 ký tự — chỉ dùng để xác thực kênh Pusher (Bước 2)
expires_inThời gian sống của phiên, tính bằng giây (300 = 5 phút)
Tái sử dụng phiên: nếu đã có một phiên đang chờ duyệt của cùng tài khoản + API key và còn hơn 2 phút hiệu lực, hệ thống trả về chính phiên đó và không gửi thông báo mới.

Bước 2 — Xác thực kênh Pusher

Kết nối tới Pusher và subscribe kênh private-auth.{session_id}. Pusher SDK sẽ tự động gọi endpoint xác thực của bạn bằng temp_token.

POST

{base_url}/ekyc/ex_api

Headers

HeaderGiá trị
AuthorizationBearer {temp_token}

Body (do Pusher SDK gửi)

TrườngMô tả
guestcmdPhải là broadcasting_auth
socket_idPusher SDK tự động điền
channel_nameprivate-auth.{session_id}

Phản hồi

{
  "auth": "PUSHER_APP_KEY:hmac_signature"
}

Ví dụ JavaScript

const pusher = new Pusher('PUSHER_APP_KEY', {
    cluster: 'ap1',
    authorizer: (channel) => ({
        authorize: (socketId, callback) => {
            const body = new URLSearchParams({
                socket_id: socketId,
                channel_name: channel.name,
                guestcmd: 'broadcasting_auth',
            });
            fetch(BASE_URL + '/ekyc/ex_api', {
                method: 'POST',
                headers: { 'Authorization': `Bearer ${temp_token}` },
                body: body,
            })
            .then(res => res.json())
            .then(data => callback(null, data))
            .catch(err => callback(err, null));
        },
    }),
});

const channel = pusher.subscribe(`private-auth.${session_id}`);

Bước 3 — Chờ sự kiện duyệt

Sau khi subscribe, hãy lắng nghe các sự kiện trên kênh Pusher private-auth.{session_id}.

LoginApproved

Khách hàng đã duyệt yêu cầu. Dùng access_token cho mọi request tiếp theo. Token có hiệu lực 24 giờ.

{
  "access_token": "eyJ..."
}

LoginRejected

Khách hàng đã từ chối yêu cầu.

{
  "reason": "User rejected"
}

Ví dụ JavaScript

channel.bind('LoginApproved', (data) => {
    const accessToken = data.access_token;
    // lưu lại và dùng cho các request cần xác thực
});

channel.bind('LoginRejected', (data) => {
    console.log('Bị từ chối:', data.reason);
});

Các request cần xác thực

Mọi endpoint cần xác thực đều dùng access_token nhận được từ sự kiện LoginApproved.

HeaderGiá trị
AuthorizationBearer {access_token}
GET

cmd=get_user_info

Trả về thông tin cơ bản của khách hàng đang đăng nhập.

GET /ekyc/ex_api?cmd=get_user_info
Authorization: Bearer {access_token}

Phản hồi

{
  "success": true,
  "data": {
    "user_id": 123,
    "account": "002C0012345",
    "email": "002C0012345@sfi.com.vn",
    "phone": "0901234567",
    "name": "Nguyen Van A",
    "is_staff": 0
  }
}
GET

cmd=open_position

Trả về các vị thế đang mở của khách hàng. Không cần tham số bổ sung.

GET /ekyc/ex_api?cmd=open_position
Authorization: Bearer {access_token}
GET

cmd=final_position

Trả về các vị thế đã tất toán (đóng) trong khoảng thời gian chỉ định.

GET /ekyc/ex_api?cmd=final_position&from={epoch_ms}&to={epoch_ms}
Authorization: Bearer {access_token}

Tham số

Tham sốKiểuMô tả
fromepoch msĐầu khoảng thời gian
toepoch msCuối khoảng thời gian
GET

cmd=account_status

Trả về trạng thái tài khoản và ký quỹ. Không cần tham số bổ sung.

GET /ekyc/ex_api?cmd=account_status
Authorization: Bearer {access_token}
GET

cmd=account_transaction

Trả về danh sách giao dịch của tài khoản, có phân trang.

GET /ekyc/ex_api?cmd=account_transaction&from={epoch_ms}&to={epoch_ms}&page=1&size=20
Authorization: Bearer {access_token}

Tham số

Tham sốKiểuBắt buộcMô tả
fromepoch msKhôngĐầu khoảng thời gian
toepoch msKhôngCuối khoảng thời gian
pageintegerKhôngSố trang (mặc định: 1)
sizeintegerKhôngSố bản ghi mỗi trang (mặc định: 20)
searchstringKhôngTừ khoá tìm kiếm
GET

cmd=account_order

Trả về danh sách lệnh, có phân trang.

GET /ekyc/ex_api?cmd=account_order&from={epoch_ms}&to={epoch_ms}&page=1&size=20
Authorization: Bearer {access_token}

Tham số

Tham sốKiểuBắt buộcMô tả
fromepoch msKhôngĐầu khoảng thời gian
toepoch msKhôngCuối khoảng thời gian
pageintegerKhôngSố trang (mặc định: 1)
sizeintegerKhôngSố bản ghi mỗi trang (mặc định: 20)
statusstringKhôngDanh sách trạng thái cách nhau bởi dấu phẩy, ví dụ FILLED,WORKING,CANCELLED
status_condstringKhôngin — chỉ lấy các trạng thái khớp  |  nin — loại bỏ các trạng thái khớp
searchstringKhôngTìm theo mã lệnh CQG
GET

cmd=money_transaction

Trả về lịch sử nạp / rút tiền trong khoảng thời gian chỉ định.

GET /ekyc/ex_api?cmd=money_transaction&from={epoch_ms}&to={epoch_ms}
Authorization: Bearer {access_token}

Tham số

Tham sốKiểuMô tả
fromepoch msĐầu khoảng thời gian
toepoch msCuối khoảng thời gian

Định dạng phản hồi chuẩn

Mọi phản hồi API đều theo khung sau:

{
  "success": true,
  "message": "Thành công!",
  "data": { ... },
  "svr": "server-identifier"
}

Khi có lỗi:

{
  "success": false,
  "message": "Mô tả lỗi",
  "data": []
}

Bảng mã lỗi

Tình huốngHTTPsuccessMô tả
API key sai hoặc thiếu200falseKhông nhận dạng được X-Api-Key
Không tìm thấy tài khoản / tài khoản không hoạt động200falseTài khoản không tồn tại hoặc đang bị khoá
temp_token không hợp lệ200falseToken đã hết hạn hoặc không khớp phiên
Phiên đã hết hạn200falseĐã quá cửa sổ 5 phút
access_token sai / hết hạn401—JWT bị từ chối — cần xác thực lại
Lệnh không tồn tại404falseKhông nhận dạng được cmd

Kiểm tra kết nối

Dùng endpoint này để kiểm tra server có truy cập được không, trước khi bắt đầu luồng xác thực. Không cần xác thực.

GET

{base_url}/ekyc/tools/check_connection

Nếu phản hồi mất hơn 5 giây, hãy chuyển sang URL dự phòng (https://b.sfi.com.vn).
Trang test OOB →