T24 core banking
Tung Bui Thanh (TungDaDev)13 mins read

T24, hay Temenos Transact, là core banking system của Temenos, một công ty Thuỵ Sĩ và là một trong những vendor phần mềm ngân hàng lớn nhất thế giới. Theo Temenos, hơn 3.000 ngân hàng ở trên 150 quốc gia đang dùng sản phẩm của hãng, riêng core banking (Transact) có khoảng 950 khách hàng, trong đó có nhiều ngân hàng lớn ở Việt Nam và Đông Nam Á.
Vậy core banking là gì? Nói gọn, đó là hệ thống xử lý mọi giao dịch tài chính chính của ngân hàng: mở tài khoản, gửi/rút tiền, chuyển khoản, cho vay, tính lãi, quản lý sổ cái. Nó là "source of truth" cho mọi data tài chính: channel nào (mobile app, internet banking, ATM, teller) cũng đọc/ghi vào core banking.
Dev cần hiểu T24 để làm gì?
- Integrate digital channels (mobile banking, web) với T24
- Build middleware/adapter giữa modern stack và T24
- Hiểu data model và business logic mà core banking enforce
- Debug production issues khi data flow qua T24
# kiến trúc t24
T24 ra đời từ thập niên 90, kiến trúc khác xa modern microservices. Biết hoàn cảnh nó sinh ra thì mình sẽ thôi "judge" nó bằng modern standards, mà tập trung vào chuyện làm việc được với nó.

# đặc điểm kiến trúc
| Aspect | T24 | Modern comparison |
|---|---|---|
| Language | Infobasic/jBC, chạy trên TAFJ (biên dịch sang Java bytecode) | Spring Boot Java |
| Database | jBASE (NoSQL hash-file DB) hoặc Oracle/SQL Server | PostgreSQL/MongoDB |
| Data model | Multi-value fields (1 field chứa nhiều giá trị) | Normalized tables |
| API | OFS messages (text-based) + REST (newer) | REST/gRPC |
| Batch | COB (Close of Business), chạy batch mỗi đêm | Event-driven/streaming |
| Customization | "Local routines" + "VERSION overrides" | Configuration + custom code |
# core concepts developer cần biết
# applications & tables
T24 tổ chức data theo "Applications", tương đương bảng/collection trong mấy database mình quen dùng.
| Application | Mô tả | Ví dụ ID |
|---|---|---|
| CUSTOMER | Thông tin khách hàng | CUS1234567 |
| ACCOUNT | Tài khoản (tiết kiệm, vãng lai) | VN0010001234567 |
| FUNDS.TRANSFER | Giao dịch chuyển khoản | FT24010ABCDE |
| TELLER | Giao dịch teller (gửi/rút) | TT24010ABCDE |
| LIMIT | Hạn mức tín dụng | LI000001234 |
| AA.ARRANGEMENT | Arrangement Architecture (sản phẩm) | AA24010ABCDE |
| STMT.ENTRY | Statement entries (sổ phụ) | — |
# record id pattern

Mỗi record có một ID unique, trong ID thường nhét luôn metadata:
FUNDS.TRANSFER ID: FT24010ABCDE
- FT: Prefix (application type)
- 24: Year
- 010: Julian day (Jan 10)
- ABCDE: Sequential/random suffix
ACCOUNT ID: VN0010001234567
- VN: Country
- 001: Branch code
- 0001234567: Sequential number
ID giao dịch (FT, TT, AA...) theo khuôn khá cố định: tiền tố, ngày dạng Julian 5 số (năm 2 số + ngày thứ mấy trong năm), rồi chuỗi định danh. Còn ID của account và customer ở trên chỉ là ví dụ minh hoạ: định dạng thật do từng ngân hàng cấu hình (nhiều nơi dùng số thuần, có check digit), nên hỏi team T24 của ngân hàng trước khi viết code parse hay validate ID.
# multi-value fields
T24 dùng jBASE, một NoSQL database có multi-value fields. Một field có thể chứa cả mảng giá trị, ngăn cách bằng các ký tự phân cách đặc biệt: field mark (FM, CHAR(254)) ngăn các field, value mark (VM, CHAR(253)) ngăn các giá trị trong một field, sub-value mark (SM, CHAR(252)) ngăn tiếp bên trong một giá trị. Khi T24 chạy trên Oracle hay SQL Server thì record vẫn giữ cấu trúc này (thường lưu dưới dạng XML trong một cột), chứ không được tách thành bảng chuẩn hoá. Với dev quen relational DB thì đây là thứ lạ lẫm nhất.
CUSTOMER record (simplified):
- ID: CUS1234567
- MNEMONIC: NGUYENVANA
- SHORT.NAME: "NGUYEN VAN A"
- NAME.1: "NGUYEN VAN A" | "NGUYEN VAN A (Mr.)" ← Multi-value: 2 values
- STREET: "123 Le Loi" | "Ward 1" ← Multi-value: 2 lines
- PHONE.1: "0901234567" | "0281234567" ← Multi-value: 2 phones
- RELATION.CODE: "SPOUSE" | "PARENT" ← Multi-value
- REL.CUSTOMER: "CUS7654321" | "CUS9876543" ← Associated values
Mapping to SQL equivalent:
- SQL: bảng CUSTOMER + bảng CUSTOMER_PHONES + bảng CUSTOMER_RELATIONS
- (normalized), còn T24 thì gom hết vào 1 record với multi-values
# cob (close of business): batch chạy mỗi đêm
Đêm nào T24 cũng chạy COB: tính lãi, apply charges, aging loans, generate statements, update balances. Quá trình này rất NẶNG, thường mất vài tiếng, tuỳ khối lượng dữ liệu và cấu hình. Trong lúc COB chạy, giao dịch online bị hạn chế; mức độ hạn chế (chỉ đọc, chặn hẳn, hay cho một số giao dịch đi qua) tuỳ phiên bản T24 và cách ngân hàng cấu hình.
Ngày làm việc T24 (ví dụ, giờ cụ thể mỗi ngân hàng một khác):
- 08:00 - 17:00: Online processing (real-time transactions)
- 17:00 - 18:00: Pre-COB (preparation, cut-off)
- 18:00 - 02:00: COB running (batch: interest calc, aging, statements)
- 02:00 - 08:00: Post-COB (system ready for next day)
Business Date vs System Date:
- T24 có khái niệm "TODAY" = business date (có thể khác system date)
- COB chạy xong thì TODAY nhảy sang ngày làm việc kế tiếp
- Cuối tuần: COB tối thứ Sáu đưa TODAY nhảy thẳng sang thứ Hai (ngày làm việc kế tiếp theo lịch nghỉ của ngân hàng), phần lãi, phí của thứ Bảy, Chủ nhật được xử lý luôn trong lần COB đó. Nên giao dịch phát sinh vào cuối tuần qua kênh số thường mang business date của thứ Hai
# integration - cách kết nối với t24
# ofs (open financial service) - legacy but still dominant
OFS là protocol nhắn tin dạng text. OFS message → T24 parse → execute → trả response. Người đọc được, nhưng dài dòng.
Một request gồm năm phần ngăn bằng dấu phẩy: application, options (version/function/process), user/password/company, ID của record (để trống nếu tạo mới để T24 tự sinh), rồi tới danh sách field. FIELD::=VALUE là cách viết tắt của FIELD:1:1=VALUE (multi-value 1, sub-value 1). Ví dụ dưới là message minh hoạ (số tài khoản, mã công ty, tên version đều là giả), xuống dòng cho dễ đọc, còn message thật nằm trên một dòng.
// OFS Request: Tạo chuyển khoản
FUNDS.TRANSFER,CSP.INPUT/I/PROCESS,OFSUSER/PASSWORD/VN0010001,,
TRANSACTION.TYPE::=AC,
DEBIT.ACCT.NO::=VN0010001234567,
DEBIT.AMOUNT::=5000000,
CREDIT.ACCT.NO::=VN0010009876543,
DEBIT.CURRENCY::=VND
// OFS Response (success): <ID>/<message ID>/1, rồi tới các field của record
FT24010ABCDE/OFS2401000001/1,TRANSACTION.TYPE:1:1=AC,DEBIT.ACCT.NO:1:1=VN0010001234567,...
// OFS Response (error): trạng thái -1, field lỗi kèm thông báo
/OFS2401000001/-1/NO,DEBIT.ACCT.NO:1:1=INSUFFICIENT BALANCE
Chỗ cần nhìn trong response là phần trạng thái sau dấu / thứ hai: 1 là thành công, -1 là lỗi. Định dạng chi tiết (message ID, cách ghi lỗi, có kèm override hay không) thay đổi theo release và cấu hình OFS.SOURCE, nên lấy response thật từ môi trường test của ngân hàng làm mẫu khi viết parser. Lưu ý thêm, /I/PROCESS là commit thẳng vào DB; muốn kiểm tra trước mà không ghi thì dùng /I/VALIDATE.
# temenos connect / rest apis (modern)
Các bản Temenos Transact mới có REST API (Temenos gọi là Transact API, chạy trên framework IRIS). Đường dẫn và schema dưới đây chỉ minh hoạ, bản thật tuỳ release và API mà ngân hàng mở:
// Modern integration: REST call to T24
@Service
@RequiredArgsConstructor
@Slf4j
public class T24AccountService {
private final WebClient t24Client;
public AccountBalance getBalance(String accountId) {
return t24Client.get()
.uri("/api/v1/holdings/accounts/{accountId}", accountId)
.header("Authorization", "Bearer " + getServiceToken())
.retrieve()
.bodyToMono(T24AccountResponse.class)
.map(this::mapToBalance)
.block();
}
public TransferResult createTransfer(TransferRequest request) {
T24TransferPayload payload = T24TransferPayload.builder()
.debitAccountId(request.getFromAccount())
.creditAccountId(request.getToAccount())
.amount(request.getAmount())
.currency("VND")
.transactionType("AC")
.build();
return t24Client.post()
.uri("/api/v1/order/payments/transfers")
.bodyValue(payload)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, response ->
response.bodyToMono(T24ErrorResponse.class)
.flatMap(error -> Mono.error(
new T24BusinessException(error.getErrorCode(), error.getMessage()))))
.bodyToMono(T24TransferResponse.class)
.map(this::mapToResult)
.block();
}
}Lệnh chuyển tiền thì đừng tự động retry khi timeout. Timeout không có nghĩa là T24 chưa ghi: có thể giao dịch đã vào sổ, chỉ có response là không về kịp. Retry mù quáng là chuyển tiền hai lần. Gắn một mã tham chiếu duy nhất từ phía kênh vào giao dịch, và khi timeout thì tra cứu theo mã đó trước khi quyết định gửi lại.
# tafj (t24 application framework for java)
TAFJ là runtime Java của T24: code Infobasic/jBC được biên dịch sang Java bytecode và chạy trên JVM. Nhờ vậy các bản mới cho phép viết routine tuỳ biến (local routine, gắn vào VERSION hay các điểm hook) bằng Java thay vì Infobasic. Dev muốn chỉnh hành vi của T24 thì viết Java class rồi deploy vào T24 runtime.
Đoạn dưới là pseudo-code để thấy ý tưởng. API Java thật của Temenos (các lớp hook như RecordLifecycle, đối tượng record có kiểu sinh từ định nghĩa application) có tên lớp và chữ ký khác, tra trong tài liệu của đúng release bạn dùng.
// T24 Local Routine (runs INSIDE T24 engine)
public class LR_ValidateTransfer extends LocalRoutine {
@Override
public void execute() {
String debitAccount = getFieldValue("DEBIT.ACCT.NO");
BigDecimal amount = new BigDecimal(getFieldValue("DEBIT.AMOUNT"));
// Custom business validation
if (amount.compareTo(new BigDecimal("10000000000")) > 0) {
setError("DEBIT.AMOUNT", "Amount exceeds single transaction limit");
return;
}
// Check sanctions
if (sanctionService.isBlocked(debitAccount)) {
setError("DEBIT.ACCT.NO", "Account is blocked by compliance");
}
}
}# data model: t24 lưu data thế nào
# account structure
ACCOUNT (tài khoản)
├── ID: VN0010001234567
├── CUSTOMER: CUS1234567 (link to customer)
├── CATEGORY: 6001 (savings) / 1001 (current)
├── CURRENCY: VND
├── WORKING.BALANCE: 50,000,000 (available balance)
├── ONLINE.ACTUAL.BAL: 55,000,000 (ledger balance, including holds)
├── OPENING.DATE: 20200115
├── ACCOUNT.TITLE.1: "NGUYEN VAN A - SAVINGS"
└── INTEREST.RATE: 4.5 (annual %)
Balances in T24:
- WORKING.BALANCE = Available for withdrawal (after holds, blocks)
(phong toả thường nằm riêng ở LOCKED.AMOUNT; công thức số dư khả dụng tuỳ ngân hàng)
- ONLINE.ACTUAL.BAL = Ledger balance (all posted transactions)
- ONLINE.CLEARED.BAL = Cleared funds only (excluding uncleared cheques)
# transaction flow

STMT.ENTRY (Statement Entry): bản ghi hạch toán chi tiết. Số dư ACCOUNT thay đổi là T24 tự sinh
STMT.ENTRYtương ứng, sau này dùng để in sao kê và đối soát (Reconciliation).Toàn vẹn dữ liệu (No state change): trong T24 Core Engine, chỉ cần một bước kiểm tra fail hoặc có lỗi hệ thống lúc ghi (giai đoạn PASS) là cả giao dịch bị
ROLLBACK, tài khoản khách không bị trừ tiền oan.
# challenges khi integrate với t24
| Challenge | Vấn đề | Cách giải quyết |
|---|---|---|
| Latency | OFS calls: 200ms-2s per request | Caching layer, batch queries |
| COB downtime | Vài tiếng bị hạn chế mỗi đêm | Queue requests, retry after COB |
| Multi-value mapping | 1 field = N values, không type-safe | Thư viện parse chắc chắn, có validation |
| Error messages | Cryptic codes ("EB-ACCT.BAL.INSUF") | Error code mapping table |
| Idempotency | OFS không native idempotent | Generate unique refs, check before submit |
| Testing | T24 environment limited, expensive | Mock T24 responses, contract testing |
| Performance | Concurrent OFS sessions limited | Connection pooling, throttling |
| Data freshness | Balances update after COB steps | Real-time fields vs batch fields |
Mấy con số latency trong bảng chỉ để hình dung bậc độ lớn; thực tế phụ thuộc phần cứng, tải và loại giao dịch.
# middleware pattern (recommended)
Giả sử có một mobile app cần lấy thông tin tài khoản từ T24. Đừng gọi thẳng T24 (OFS/REST), nên dựng một middleware layer theo pattern:
Mobile App → API Gateway → [Middleware] → T24
Middleware này gồm:
- Cache (Redis): account info, rates
- Queue (RabbitMQ): async operations
- Transform: modern JSON ↔ OFS format
- Retry: handle T24 timeouts
- Circuit Breaker: handle T24 down
- Audit: log all T24 interactions
// Middleware service: abstracts T24 complexity from digital channels
@Service
@RequiredArgsConstructor
@Slf4j
public class AccountMiddlewareService {
private final T24Client t24Client;
private final RedisTemplate<String, AccountDTO> cache;
private final CircuitBreakerFactory circuitBreakerFactory;
private static final Duration CACHE_TTL = Duration.ofMinutes(5);
public AccountDTO getAccount(String accountId) {
// 1. Check cache first
AccountDTO cached = cache.opsForValue().get("account:" + accountId);
if (cached != null) return cached;
// 2. Call T24 with circuit breaker
CircuitBreaker cb = circuitBreakerFactory.create("t24-accounts");
AccountDTO account = cb.run(
() -> t24Client.getAccount(accountId),
throwable -> {
log.error("T24 unavailable for account: {}", accountId, throwable);
throw new CoreBankingUnavailableException("Cannot retrieve account");
}
);
// 3. Cache result
cache.opsForValue().set("account:" + accountId, account, CACHE_TTL);
return account;
}
}Cache 5 phút hợp với thông tin ít đổi (tên chủ tài khoản, loại tài khoản, tỷ giá tham khảo). Số dư thì cẩn thận: hiển thị cho khách xem thì cache ngắn còn chấp nhận được, nhưng mọi quyết định liên quan tiền (đủ tiền để chuyển không) phải hỏi thẳng T24. Nhớ xoá cache của tài khoản sau mỗi giao dịch đi qua middleware, không thì khách vừa chuyển tiền xong vẫn thấy số dư cũ.
# t24 trong context modern banking
T24 là kiểu "old but gold": ngày nào cũng xử lý một lượng giao dịch khổng lồ và đã chạy ổn định qua mấy chục năm. Nhưng nó KHÔNG phải nền tảng để làm trải nghiệm số. Stack hiện đại sẽ bọc bên ngoài T24:
User Experience Layer: Mobile/Web apps (React, Angular, Flutter)
│
Engagement Layer: BFF, personalization, analytics (Spring Boot, Node.js)
│
Integration Layer: API management, transformation, caching (Kong, Middleware)
│
Core Banking: T24 (source of truth, transaction processing, compliance)
Xu hướng: ngân hàng giữ T24 làm system of record (sổ cái, compliance, batch processing), còn phần customer-facing features thì build bằng các layer hiện đại đặt lên trên. Dần dần một số module sẽ được tách ra, như payments chuyển sang payment hub mới, lending chuyển sang origination platform mới, nhưng T24 vẫn sẽ là xương sống trong nhiều chục năm nữa.
Dev nào làm cho ngân hàng cũng nên hiểu T24, vì nó là "the brain of banking". Nắm chắc T24 thì cửa vào mảng fintech và core banking sẽ rộng hơn nhiều.
Chỉ là những ghi chép cá nhân với hy vọng mang lại chút giá trị. Nếu thấy hữu ích, đừng ngại chia sẻ cho bạn bè & đồng nghiệp nhé!
Happy coding 😎 👍🏻 🚀 🔥.