1. Bối cảnh nghiệp vụ: KYC theo tầng, hạn mức, AML và SLA
Một ví điện tử fintech không chỉ là màn hình đăng ký đẹp. Phía sau nó là một bộ máy tuân thủ nghiêm ngặt: xác minh danh tính khách hàng (KYC) chia theo tầng, hạn mức giao dịch gắn với từng tầng, quy trình chống rửa tiền (AML) phải ghi vết mọi quyết định, và cam kết chất lượng dịch vụ (SLA) buộc hệ thống phản hồi trong vài giây. Người kiểm thử bước vào bối cảnh này không thể chỉ hỏi 'nút bấm có chạy không', mà phải hỏi 'quy tắc nghiệp vụ và ràng buộc pháp lý có được giữ vững dưới áp lực và dưới tấn công không'. Đây là lý do một bài kiểm thử fintech tốt bắt đầu từ hiểu nghiệp vụ, không phải từ công cụ.
Cụ thể, hãy hình dung ba tầng KYC. Tầng T0 là tài khoản chỉ có email và số điện thoại, hạn mức rất thấp, ví dụ nạp tối đa một triệu đồng và không được rút. Tầng T1 yêu cầu giấy tờ tùy thân và selfie đối chiếu, mở hạn mức trung bình. Tầng T2 yêu cầu xác minh địa chỉ và nguồn tiền, mở hạn mức cao và cho phép chuyển khoản quốc tế. Mỗi tầng là một hợp đồng: nâng tầng thì mở quyền, nhưng mọi quyền đều bị chặn trên bởi hạn mức. Bài toán kiểm thử cốt lõi là chứng minh rằng không có con đường nào — kể cả con đường mà kẻ gian cố tình tạo ra — vượt được hạn mức của tầng hiện tại.
2. Oracle: bất biến hạn mức, quy tắc quyết định tất định, idempotency
Oracle là câu trả lời cho câu hỏi 'làm sao biết kết quả đúng'. Với flow KYC và fraud, oracle không thể là 'màn hình hiện chữ Thành công'. Nó phải là tập bất biến nghiệp vụ có thể kiểm chứng độc lập với giao diện. Bất biến thứ nhất: tổng giao dịch trong một cửa sổ thời gian của một tài khoản không bao giờ vượt hạn mức của tầng đó. Bất biến thứ hai: quy tắc quyết định phải tất định — cùng một bộ tín hiệu đầu vào luôn cho ra cùng một quyết định approve, hold hoặc deny, không phụ thuộc thời điểm hay may rủi. Bất biến thứ ba: idempotency — cùng một yêu cầu gửi lại nhiều lần chỉ tạo đúng một quyết định và một bút toán, không nhân đôi.
// oracle/kyc-invariants.ts — bất biến kiểm chứng độc lập UI
export interface AccountState {
tier: 'T0' | 'T1' | 'T2';
windowTotal: number; // tổng đã dùng trong cửa sổ
decisions: Decision[];
}
export type Decision = 'approve' | 'hold' | 'deny';
const LIMIT: Record<AccountState['tier'], number> = {
T0: 1_000_000, T1: 50_000_000, T2: 500_000_000,
};
// I1: không bao giờ vượt hạn mức tầng
export const limitHeld = (s: AccountState) => s.windowTotal <= LIMIT[s.tier];
// I3: idempotency — 1 key ⇒ đúng 1 quyết định
export const idempotent = (ledger: { key: string }[]) =>
new Set(ledger.map(l => l.key)).size === ledger.length;Điểm mấu chốt: oracle phải neo vào nguồn sự thật là ledger, cơ sở dữ liệu và AML audit log, không neo vào toast hay animation trên màn hình. Một hệ thống fintech có thể hiện 'Thành công' cho người dùng trong khi bút toán thực bị lệch, hoặc quyết định approve bị ghi hai lần vì lỗi idempotency. Nếu test chỉ đọc UI, nó sẽ báo xanh trong khi tiền thật đang sai. Vì thế mọi assertion quan trọng đều truy vấn trạng thái sau cùng ở tầng dữ liệu — đó là nơi sự thật cư trú.
3. AI sinh danh tính tổng hợp và dữ liệu đối kháng
Đây là chỗ AI phát huy giá trị lớn nhất trong kiểm thử fintech: sinh dữ liệu. Con người viết được vài chục case biên, nhưng một mô hình có thể sinh hàng nghìn biến thể danh tính tổng hợp và payload đối kháng trong vài phút. 'Tổng hợp' nghĩa là dữ liệu giả nhưng có cấu trúc hợp lệ — không dùng danh tính thật của ai, tránh rủi ro quyền riêng tư, nhưng vẫn đủ giống thật để đẩy hệ thống vào các nhánh hiếm. 'Đối kháng' nghĩa là dữ liệu được cố tình thiết kế để phá: giấy tờ có tên lệch một dấu so với selfie, ngày sinh mâu thuẫn giữa hai trường, số tiền sát ngưỡng hạn mức đúng một đồng, hay payload lặp lại với cùng idempotencyKey.
// gen/adversarial.ts — sinh biến thể đối kháng có kiểm soát (KHÔNG dùng PII thật)
import { faker } from '@faker-js/faker';
export function synthIdentity(tier: 'T0'|'T1'|'T2') {
const name = faker.person.fullName();
return { tier, name, dob: faker.date.birthdate(), doc: { name } };
}
// Biến thể đối kháng: cố tình lệch/ giả mạo để ép hệ thống lộ lỗ hổng
export function adversarialVariants(base: ReturnType<typeof synthIdentity>) {
return [
{ ...base, doc: { ...base.doc, name: base.name.replace('a', 'á') } }, // lệch dấu
{ ...base, amount: 1_000_001 }, // vượt T0 đúng 1đ
{ ...base, amount: -5000 }, // số âm
{ ...base, amount: Number.MAX_SAFE_INTEGER }, // tràn số
{ ...base, replay: true, idempotencyKey: 'FIXED-KEY-42' }, // replay
{ ...base, doc: { name: base.name, mismatchDob: '1900-01-01' } }, // mismatch
];
}Nhưng — và đây là điều then chốt — AI chỉ sinh input, tuyệt đối không được sinh oracle. Nếu bạn để mô hình vừa tạo dữ liệu vừa tự quyết định 'kết quả nào là đúng', bạn đã đưa ảo giác (hallucination) vào chính thước đo của mình. Con người định nghĩa oracle một lần, cứng và rõ; AI đổ hàng nghìn biến thể qua oracle đó. Vai trò chia rất sạch: máy lo bề rộng của không gian input, người lo tính đúng của phán quyết. Đảo vai trò này là sai lầm nguy hiểm nhất khi dùng AI trong kiểm thử phòng thủ.
💬 Bình luận (0)
Chưa có bình luận nào. Hãy là người đầu tiên!