Skip to content

Tổng quan eKYC ​

eKYC là nền tảng xác minh danh tính được CAKE lưu trữ (hosted): một tập hợp API HTTP không lưu trạng thái (stateless), tải một ảnh lên một lần, sau đó chạy các kiểm tra thẻ ID (ID card) (phát hiện, sanity, OCR, giả mạo) và kiểm tra khuôn mặt (sanity, liveness, so sánh, lập chỉ mục và tìm kiếm) trên ảnh đó. Trang này trình bày xác thực, hợp đồng request/response dùng chung, và luồng xác minh đầu-cuối.

INFO

Mọi ví dụ trong tài liệu này dùng cùng một danh tính giả định — Nguyễn Văn A, CCCD 001xxxxxxxxx, user_id user_00000001, ref_id ref_00000001. Thông tin xác thực, token và key được ghi dạng <PLACEHOLDER>. Đây không phải dữ liệu thật; hãy thay toàn bộ bằng giá trị của bạn.

Điều kiện tiên quyết ​

Trước khi gọi bất kỳ endpoint nào, bạn cần một tenant được CAKE cấp phát cùng bộ thông tin xác thực đi kèm.

Thông tinMô tả
Access IDMột UUID định danh duy nhất cho Tenant của bạn. Được gửi trong header Authorization. Do CAKE cung cấp.
Access KeySecret key dùng để ký mọi request và xác minh signature của mọi response. Do CAKE cung cấp.
Base URLDo CAKE cấp cùng bộ credentials, cho cả môi trường stage và production.
Cấu hình tenantNgưỡng, collection và các kiểm tra được bật là do CAKE cấu hình riêng cho từng tenant trước khi go-live.
Client IDGiá trị X-Client-ID được cấp cùng bộ thông tin xác thực, gửi trên các request đi qua gateway.

Liên hệ CAKE Support để nhận access_id và access_key.

Các ví dụ trong tài liệu này ghi base URL dưới dạng <BASE_URL> — hãy thay bằng giá trị CAKE cấp cho bạn.

WARNING

Access Key là một bí mật phía server. Không bao giờ nhúng nó vào ứng dụng di động, một bundle trình duyệt, hay bất kỳ client nào do người dùng cuối kiểm soát — chỉ ký request từ backend của bạn. Client di động xác thực bằng một JWT token ngắn hạn thay thế (xem bên dưới).

Khái niệm cốt lõi ​

Vòng đời giao dịch ​

Mỗi phiên eKYC diễn ra bên trong một giao dịch (transaction), đi qua ba trạng thái.

TrườngKiểuMô tả
NOT_VERIFIEDstringGiao dịch đã được tạo và token đã được cấp, nhưng việc xác minh chưa hoàn tất.
VERIFIEDstringAuthentication key đã được redeem thành công.
EXPIREDstringGiao dịch không được hoàn tất trước khi token hết hạn.
Thông tinMô tả
Tạo giao dịchBackend của bạn mở một giao dịch và nhận JWT mà SDK sẽ dùng.
Lấy giao dịchĐọc trạng thái hiện tại bất kỳ lúc nào.
Xác minh khóa giao dịchRedeem authentication key và đóng giao dịch.

Giao dịch là tùy chọn với một lần kiểm tra ảnh đơn lẻ, nhưng bắt buộc khi bạn cần một kết quả có thể xác minh và kiểm toán mà backend của bạn tin cậy được.

Xác thực ​

eKYC hỗ trợ hai phương thức xác thực, được chọn theo bên gọi.

Phương án 1: Xác thực HMAC (backend đến eKYC backend) ​

Các request server-to-server được xác thực bằng HMAC-SHA256, chứng minh cả tính toàn vẹn lẫn tính xác thực của nội dung request bằng một khóa dùng chung (shared secret) giữa backend của bạn và CAKE.

Hai header là bắt buộc trên mọi request:

HeaderMô tả
X-TV-TimestampTimestamp hiện tại theo định dạng RFC3339. Ví dụ: 2019-04-21T18:00:15+07:00
AuthorizationATV <ACCESS_ID>:<SIGNATURE>

Signature là một HMAC-SHA256 mã hóa Base64 của một StringToSign chuẩn hóa:

text
StringToSign = HttpMethod + "\n" + UrlPath + "\n" + Timestamp + "\n" + HashedBody

# HttpMethod: POST, GET, PUT, PATCH hoặc DELETE
# UrlPath:    đường dẫn route tương đối, không gồm domain. Ví dụ: /v1/images
# Timestamp:  thời điểm request theo định dạng RFC3339; phải khớp với header X-TV-Timestamp
# HashedBody: hex_encode(SHA256(raw body))
#             Ví dụ (body `{}`): 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a

Signature = Base64(HmacSha256(AccessKeySecret, Utf8EncodingOf(StringToSign)))

Cách triển khai tham chiếu bằng JavaScript:

javascript
const CryptoJS = require("crypto-js");

const timestamp = new Date().toISOString(); // 2024-05-09T07:53:10.591Z
const access_key = "<ACCESS_KEY>";
const path = "/v1/create_ekyc_transaction";
const body = `{
    "user_id": "user_00000001",
    "ref_id": "ref_00000001"
}`;

function tokenize(method, path, timestamp, body, key) {
  const hashed_body = CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex);
  const formatted = `${method}\n${path}\n${timestamp}\n${hashed_body}`;
  const hmac = CryptoJS.HmacSHA256(formatted, key);
  const hashed = hmac.toString(CryptoJS.enc.Hex);
  return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(hashed));
}

const authorize_token = tokenize("POST", path, timestamp, body, access_key);
// Gửi `timestamp` làm X-TV-Timestamp và `authorize_token` làm signature — không bao giờ ghi log.

Ví dụ request đã ký:

bash
curl --location '<BASE_URL>/v1/create_ekyc_transaction' \
  --header 'Authorization: ATV <ACCESS_ID>:<SIGNATURE>' \
  --header 'X-TV-Timestamp: <TIMESTAMP>' \
  --header 'Content-Type: application/json' \
  --header 'X-Request-Id: <RANDOM_UUID>' \
  --header 'X-Client-ID: <CLIENT_ID>' \
  --data '{
    "user_id": "user_00000001",
    "ref_id": "ref_00000001"
}'

INFO

StringToSign dùng SHA256 của raw body mã hóa dạng hex. Một số tenant của CAKE được cấp phát dùng hash body mã hóa Base64 thay thế — hãy xác nhận với đội CAKE tenant của bạn đang dùng biến thể nào trước khi triển khai.

Phương án 2: JWT token (mobile SDK đến eKYC backend) ​

Một JWT token được cấp trong bước eKYC cho một user và ref_id cụ thể, và Mobile SDK trình nó khi gọi trực tiếp eKYC Server. Token ngừng hoạt động khi hết hạn.

Xem token là chuỗi mờ (opaque): chuyển nguyên trạng cho SDK, không parse, không ghi log, không lưu trữ.

Ký response ​

Mọi response từ eKYC Server đều được ký bằng HMAC-SHA256 trên method, path, timestamp và hash của response body, để backend của bạn có thể chứng minh payload thực sự đến từ CAKE.

HeaderMô tả
X-Signature-TimestampTimestamp đã ký theo định dạng RFC3339. Ví dụ: 2019-04-21T18:00:15+07:00
X-SignatureToken HMAC mã hóa Base64 từ server CAKE. Ví dụ: <SIGNATURE>
text
StringToSign = HttpMethod + "\n" + UrlPath + "\n" + Timestamp + "\n" + HashedBody

# HttpMethod: method của API đã được gọi
# UrlPath:    đường dẫn route tương đối, không gồm domain. Ví dụ: /v1/images
# Timestamp:  thời điểm ký theo định dạng RFC3339, được gửi trong X-Signature-Timestamp
# HashedBody: hex_encode(SHA256(raw response body))

Signature = Base64(HmacSha256(AccessKeySecret, Utf8EncodingOf(StringToSign)))

WARNING

Hãy tính lại và so sánh X-Signature trước khi tin tưởng một kết luận (verdict). Một response chưa được xác minh là một response chưa được xác thực, và các kết luận của eKYC là căn cứ cho các quyết định onboarding.

Cách hoạt động ​

Một phiên eKYC đầy đủ trải qua bốn actor: ứng dụng di động, backend của bạn, eKYC Backend, và các AI engine phía sau nó. Sơ đồ dưới đây thể hiện luồng thành công (happy path) chuẩn.

sequenceDiagram
    autonumber
    participant App as Mobile App / SDK
    participant BE as Backend của bạn
    participant EKYC as eKYC Backend
    participant AI as AI Engines

    Note over App,EKYC: Giai đoạn 1 — Chụp & tải lên
    App->>BE: Ảnh thẻ ID và ảnh chân dung đã chụp
    BE->>EKYC: POST /v1/images (multipart, ký HMAC)
    EKYC-->>BE: image_id

    Note over BE,AI: Giai đoạn 2 — Xác minh thẻ ID
    BE->>EKYC: POST /v1/verify_id_card_sanity_sync
    EKYC->>AI: Model sanity
    AI-->>EKYC: kết luận + score
    EKYC-->>BE: kết luận card_sanity
    BE->>EKYC: POST /v1/read_id_card_info_sync
    EKYC-->>BE: card_information (OCR / MRZ / QR / NFC)
    BE->>EKYC: POST /v1/detect_id_card_tampering_sync
    EKYC-->>BE: kết luận card_tampering + chi tiết

    Note over BE,AI: Giai đoạn 3 — Xác minh khuôn mặt
    BE->>EKYC: POST /v1/verify_portrait_sanity_sync
    EKYC-->>BE: kết luận portrait_sanity
    BE->>EKYC: POST /v1/verify_face_liveness_sync
    EKYC-->>BE: is_live + score
    BE->>EKYC: POST /v1/compare_faces_sync (chân dung so với ảnh thẻ)
    EKYC-->>BE: matched / unmatched / unsure + score

    Note over BE,EKYC: Giai đoạn 4 — Khử trùng lặp & quyết định
    BE->>EKYC: POST /v1/search_faces_sync (kiểm tra trùng lặp)
    EKYC-->>BE: các khuôn mặt tương đồng + score
    BE->>EKYC: POST /v1/index_faces_sync (đăng ký người dùng đã duyệt)
    EKYC-->>BE: status success
    BE-->>App: Quyết định onboarding

Giai đoạn 1 — Chụp và tải lên ​

  1. Ứng dụng di động hoặc Mobile SDK chụp mặt trước thẻ ID, mặt sau thẻ ID và một ảnh chân dung selfie, rồi chuyển chúng cho backend của bạn.
  2. Backend của bạn gọi POST /v1/images một lần cho mỗi ảnh với label chính xác. Mỗi lệnh gọi trả về một image_id.
  3. Backend của bạn giữ lại các giá trị image_id cho phần còn lại của phiên — mọi API downstream đều nhận một image_id thay vì tải lại dữ liệu ảnh.

Giai đoạn 2 — Xác minh thẻ ID ​

  1. POST /v1/verify_id_card_sanity_sync kiểm tra ảnh thẻ có dùng được không: đủ sắc nét, đầy đủ, không phải bản photocopy, không bị chói lóa trên các trường dữ liệu. Hãy chạy bước này trước tiên — một ảnh xấu sẽ làm sai lệch mọi kiểm tra sau nó.
  2. POST /v1/read_id_card_info_sync trích xuất các trường của thẻ qua OCR, MRZ, QR và (khi có) NFC, mỗi trường kèm một độ tin cậy.
  3. POST /v1/detect_id_card_tampering_sync tìm dấu hiệu giả mạo: ảnh chân dung bị chỉnh sửa, font chữ bất thường, sự không khớp giữa các nguồn MRZ, QR và OCR, và logic số ID hoặc ngày hết hạn không hợp lệ.

Giai đoạn 3 — Xác minh khuôn mặt ​

  1. POST /v1/verify_portrait_sanity_sync kiểm tra ảnh selfie có dùng được không: một khuôn mặt, được lấy khung tốt, mắt mở, không bị che khuất.
  2. POST /v1/verify_face_liveness_sync xác nhận có một người thật trước camera thay vì một tấm ảnh hoặc một lần phát lại màn hình (screen replay).
  3. POST /v1/compare_faces_sync so khớp ảnh selfie với khuôn mặt in trên thẻ ID, trả về matched, unmatched hoặc unsure cùng một điểm tương đồng (similarity score).

Giai đoạn 4 — Khử trùng lặp và quyết định ​

  1. POST /v1/search_faces_sync tìm kiếm trong collection khuôn mặt của bạn xem có ai đã được đăng ký với cùng khuôn mặt hay không — kiểm tra trùng tài khoản và blocklist tiêu chuẩn.
  2. POST /v1/index_faces_sync thêm khuôn mặt của người dùng đã được duyệt vào collection để các phiên sau có thể tìm thấy họ.
  3. Backend của bạn kết hợp mọi kết luận thành một quyết định onboarding duy nhất và trả về cho ứng dụng. eKYC trả về bằng chứng; chính sách chấp nhận/từ chối vẫn là của bạn.

Cấu trúc response dùng chung ​

Mọi endpoint JSON đều trả về cùng một cấu trúc bên ngoài.

json
{
  "data": {
    "request_id": "f77bf948-fce3-48f3-b61c-396e4cd9a89b",
    "status": "success"
  },
  "errors": [],
  "success": true,
  "code": 0,
  "message": "Success"
}
TrườngKiểuMô tả
dataobjectPayload riêng của từng endpoint. Rỗng khi request thất bại trước khi được xử lý.
data.statusstringsuccess hoặc failure — request có được xử lý thành công hay không.
data.request_idstringRequest ID do server tạo. Cung cấp cho CAKE Support khi cần kiểm tra log.
errorsarrayChi tiết từng lỗi. Rỗng khi thành công.
errors[].codestringMã lỗi dạng máy đọc được, ví dụ image_not_found_exception.
errors[].messagestringMô tả dạng người đọc được về lỗi.
errors[].detailobjectNgữ cảnh tùy chọn, ví dụ field nêu tên tham số không hợp lệ.
successboolBản thân cuộc gọi có thành công hay không.
codeintstatus code dạng số — 0 khi thành công.
messagestringThông báo trạng thái.

Một HTTP 200 không có nghĩa là kiểm tra đã thành công. Hãy đọc success, rồi data.status, rồi đến kết luận.

Mã số thường gặp ​

MãThông báo
0Success
2001002Xảy ra lỗi không mong đợi trong quá trình xử lý request bên ngoài.
2001003Ảnh gửi lên server không có khuôn mặt nào.
2001010Không tìm thấy Access ID.
2001014Vượt giới hạn tốc độ đăng ký.
2001015Vượt giới hạn brute-force khi đăng ký.
2001026Các ảnh gửi lên server bị trùng lặp.
401100Bạn không được phép thực hiện hành động này.
4001000Tham số đầu vào vi phạm một ràng buộc.
4001001Header X-TV-Timestamp đã hết hạn, cần một cái mới hơn.
4041000Không tìm thấy image ID trong DB.
4041001Không tìm thấy người dùng.
4091000cus_user_id đã được đăng ký.
4131000Kích thước ảnh đầu vào vượt quá giới hạn cho phép (15MB).
4151000Định dạng ảnh được cung cấp không được hỗ trợ (JPG/PNG).
4291000Số lượng request vượt quá giới hạn throughput của bạn.
5001000Xảy ra lỗi không mong đợi trong quá trình xử lý request.

Mã lỗi thường gặp ​

MãMô tả
access_id_not_foundKhông tìm thấy Access ID.
access_denied_exceptionBạn không được phép thực hiện hành động này.
invalid_parameter_exceptionTham số đầu vào vi phạm một ràng buộc.
request_time_too_skewedHeader X-TV-Timestamp đã hết hạn, cần một cái mới hơn.
image_not_found_exceptionKhông tìm thấy image ID trong DB.
image_too_large_exceptionKích thước ảnh đầu vào vượt quá giới hạn cho phép (15MB).
invalid_image_format_exceptionĐịnh dạng ảnh được cung cấp không được hỗ trợ (JPG/PNG).
duplicated_imageCác ảnh gửi lên server bị trùng lặp.
image_has_no_facesẢnh gửi lên server không có khuôn mặt nào.
request_not_found_exceptionKhông tìm thấy request ID khi poll kết quả theo id.
request_timeout_exceptionRequest mất quá nhiều thời gian để xử lý.
rate_limit_exceptionSố lượng request vượt quá giới hạn throughput của bạn.
internal_server_errorXảy ra lỗi không mong đợi trong quá trình xử lý request.

Bước tiếp theo ​

Developers