Bối cảnh
CreditMate là app iPhone và iPad mình tự làm, không phải việc nhận từ khách. Nó gom thẻ tín dụng, khoản vay tiêu dùng và ví trả sau của nhiều ngân hàng vào một màn hình, và trả lời bốn câu hỏi hằng ngày: đang nợ bao nhiêu, kỳ nào sắp đến hạn, hạn mức còn lại thật sự là bao nhiêu, và mua món này nên quẹt thẻ nào.
App chưa có trên App Store hay TestFlight. Bản 0.3.0 ra ngày 08/10/2026 là bản phát triển nội bộ, bản 0.4.0 đang hoàn thiện. Mình kể nó ở đây vì phần khó nhất của nó không nằm ở giao diện, mà ở chỗ tính tiền cho đúng.
Phân vai: mình lên kế hoạch, định hướng, thiết kế kiến trúc và kiểm thử. Claude viết code theo kế hoạch và kiến trúc đó, Gemini (qua Antigravity) chỉnh giao diện, Codex review code và góp ý về lỗi.
Thách thức
Một chủ thẻ ở Việt Nam thường giữ nhiều thẻ ở nhiều ngân hàng, thêm vài khoản trả góp, một khoản vay và một ví trả sau. Mỗi app ngân hàng chỉ thấy thẻ của mình. Ngày chốt sao kê và ngày đến hạn mỗi nơi một kiểu. Ưu đãi đổi theo tuần. Nên câu hỏi đơn giản như "tháng này tổng cộng phải trả bao nhiêu" lại khó trả lời.
Bảng tính làm được, nhưng sai đúng ở những chỗ quan trọng. Chỗ hay sai nhất là hạn mức dùng chung: ba thẻ cùng một hạn mức 100 triệu mà cộng lại thành 300 triệu. Với tiền, một con số sai nhìn vẫn rất đúng, nên không ai phát hiện.

Ràng buộc có hai. Dữ liệu thẻ là loại dữ liệu không ai muốn đưa lên máy chủ lạ, nên mình đặt mục tiêu không có máy chủ nào của mình. Và một người làm, nên mọi quy tắc phải kiểm tra được bằng máy chứ không bằng mắt.
Cách làm
1. Viết luật về tiền trước khi vẽ giao diện
Mình viết ra các luật và ghi vào bộ quy tắc của dự án để mọi đoạn code phải theo. Tiền là số nguyên ở đơn vị nhỏ nhất kèm mã tiền tệ, không dùng số thực. Tỷ lệ tính bằng phần triệu, nên 6,8% lưu là 68.000. Thiếu số liệu thì hiện "chưa biết", không ngầm tính thành 0. Ngày mua, ngày hạch toán, ngày chốt sao kê và ngày đến hạn là bốn ngày khác nhau, không trộn. Và hạn mức dùng chung lưu một lần ở nhóm, không nhân theo số thẻ.
2. Tách logic khỏi giao diện
Toàn bộ logic nằm trong một gói riêng tên CreditMateKit, viết bằng Swift thuần. Phần tiền và sổ không được nhìn thấy giao diện, cơ sở dữ liệu hay mạng. App chỉ còn lo hiển thị và phần nối. Nhờ vậy các phép tính tiền kiểm thử riêng được, lặp lại được, và mình viết kiểm thử cho chúng trước, dựa trên các tình huống có số liệu giả.

3. Nhập dữ liệu mà không ghi bừa
Sao kê PDF, tin nhắn biến động số dư, ảnh chụp app ngân hàng và báo cáo tín dụng đều được đọc ngay trên máy thành bản nháp. Chỉ khi mình xác nhận thì sổ mới được ghi, kèm một khoá để nhập lại không tạo giao dịch trùng. Mã OTP trong tin nhắn chỉ được đếm, không lưu nội dung. Mật khẩu của file PDF chỉ dùng một lần.
Luật này áp cho cả trợ lý AI. Trợ lý trả lời bằng số liệu trong app và chỉ được tạo bản nháp, không ghi thẳng vào sổ.
4. Giữ riêng tư từ đầu
App khoá bằng Face ID hoặc mã PIN 6 số riêng của CreditMate. Số thẻ đầy đủ và CVV, nếu người dùng tự lưu, nằm trong một két mã hoá AES-GCM trên Keychain, chỉ dùng được trên máy này. Bản sao lưu cũng mã hoá. Số thẻ đầy đủ không bao giờ đến trợ lý hay bản sao lưu.
Trợ lý AI là phần duy nhất có thể gọi ra ngoài, và chỉ khi người dùng bật, bằng khoá API của chính họ cất trong Keychain. Có thể chọn AI chạy trên máy, và có ngân sách chi phí theo tháng.
5. Kiểm thử như đang giữ tiền thật
Gói logic có bộ kiểm thử riêng, chạy bằng một dòng lệnh. Giao diện có kiểm thử chạy trên cả iPhone và iPad. Có kiểm thử riêng cho quy tắc ghi dư nợ, chống ghi đôi khi nhập lại, tổng không biến "chưa biết" thành 0, và tiền tệ khác VND.
Lỗi thật vẫn xảy ra, và mình ghi hết vào nhật ký phiên bản. Có lần nhập tay giao dịch trên thẻ ngoại tệ bị đọc số tiền theo VND. Sao kê PDF từng cố định tiền tệ là VND. Trên iPad, Ví thẻ từng mở nhầm thẻ sau khi chuyển nhóm. Cả ba đều đã được sửa.
Kết quả
Các số dưới đây là số đếm trong mã nguồn, ghi trên trang dự án (cập nhật 23/10/2026). Chúng mô tả công sức kiểm thử, không phải kết quả từ người dùng, vì app chưa phát hành.
Có 1.864 kiểm thử cho phần tiền, sổ giao dịch, trả góp và bộ đọc sao kê, cùng 110 kiểm thử giao diện chạy trên iPhone và iPad. Kho ưu đãi của 27 ngân hàng có hiệu lực theo ngày, và được ghi rõ là chưa xác minh. Số máy chủ là 0: dữ liệu chỉ nằm trên máy.
Con số cuối nghe như khẩu hiệu, nên mình nói rõ nghĩa của nó: không có máy chủ nào của mình giữ dữ liệu của bạn. Riêng trợ lý AI, nếu bạn bật nguồn AI trên đám mây thì app gọi nhà cung cấp đó bằng khoá của bạn.
Có vài điều mình chưa làm được. App chưa lên App Store hay TestFlight. Số liệu do người dùng nhập hoặc đọc từ chứng từ, không lấy trực tiếp từ ngân hàng. Dữ liệu ưu đãi chưa được xác minh. Và đồng bộ iCloud chưa bật, nên hiện dữ liệu chỉ ở một máy cộng bản sao lưu mã hoá.
Bài học
Viết luật trước, vẽ sau. Phần tiền là phần không được sai, nên nó phải có luật ghi ra giấy trước khi có màn hình đầu tiên.
"Chưa biết" khác 0. Một ô trống bị tính thành 0 làm tổng nợ nhìn rất gọn và sai. Cho nó một trạng thái riêng ngay từ đầu rẻ hơn nhiều so với vá về sau.
Bản nháp rồi mới ghi sổ. Khi mọi nguồn nhập, kể cả AI, chỉ tạo bản nháp, người dùng luôn là người bấm xác nhận cuối cùng, và lỗi đọc sao kê không âm thầm làm hỏng sổ.
Tách logic ra khỏi giao diện là thứ giúp một người kiểm soát được 1.864 kiểm thử. Phần tính tiền không biết gì về màn hình nên kiểm thử nhanh và ít vỡ.
Và nói rõ giới hạn. Với một app tài chính, câu "chưa xác minh" và "chưa phát hành" quan trọng hơn một câu quảng cáo nữa.
Cần việc tương tự?
Nếu bạn cần một app mà dữ liệu nhạy cảm nằm lại trên máy người dùng, hoặc một phần mềm có nhiều quy tắc tính toán không được phép sai, gửi mình vài dòng. Mình sẽ cùng bạn ghi ra các quy tắc đó trước, rồi mới nói nên làm gì và kiểm thử thế nào.






