Accounting System Overview
The Keshless Accounting System is a production-ready, ACID-compliant double-entry bookkeeping system designed specifically for fintech applications.
What is Double-Entry Bookkeeping?
Double-entry bookkeeping is a method where every financial transaction affects at least two accounts. For every debit entry, there must be a corresponding credit entry of equal value.
The Golden Rule
Debits = Credits (always!)
Every transaction creates balanced pairs:
User A sends E100 to User B
Debit: User A's Wallet -E100
Credit: User B's Wallet +E100
Total Debits: E100
Total Credits: E100 ✅Why Double-Entry?
1. Regulatory Compliance
Financial institutions and payment processors are required by regulators to maintain proper accounting records following Generally Accepted Accounting Principles (GAAP).
2. Audit Trail
Every transaction is recorded with:
- Who initiated it (actorId)
- When it happened (timestamp)
- What accounts were affected
- Why it happened (description/reference)
- Hash-chained proof of integrity
3. Automatic Reconciliation
With double-entry records, the system can:
- Verify wallet balances match ledger entries
- Generate trial balances to ensure books are balanced
- Detect and alert on discrepancies automatically
4. Financial Reporting
Generate standard financial reports:
- Balance Sheet
- Income Statement (P&L)
- Cash Flow Statement
- Trial Balance
- Account Statements
Core Concepts
Accounts
An account is a container for tracking financial activity. Accounts are organized into 5 main types:
Assets (1000-1999) - What the company owns
- User Wallets (cash in user accounts)
- Vendor Wallets (cash in vendor accounts)
- Cash in Transit
Liabilities (2000-2999) - What the company owes
- Pending Settlements
- User Liabilities
- Vendor Payables
Equity (3000-3999) - Owner's stake in the company
- Retained Earnings
- Owner's Capital
Revenue (4000-4999) - Money earned
- Transaction Fee Revenue
- Withdrawal Fee Revenue
- Commission Income
Expenses (5000-5999) - Money spent
- Processing Costs
- Refund Expenses
- Operating Expenses
Normal Balances
Each account type has a normal balance:
| Account Type | Normal Balance | Increases by | Decreases by |
|---|---|---|---|
| Asset | DEBIT | Debit | Credit |
| Liability | CREDIT | Credit | Debit |
| Equity | CREDIT | Credit | Debit |
| Revenue | CREDIT | Credit | Debit |
| Expense | DEBIT | Debit | Credit |
Journal Entries
A journal entry groups related ledger entries representing a single business transaction.
Example: User pays vendor E95 with E5 fee
Journal Entry #JE-000123
Description: Payment to vendor
Date: 2026-01-22
Status: POSTED
Ledger Entries:
1. DEBIT 1110 (User Wallets) - E100
2. CREDIT 1120 (Vendor Wallets) - E95
3. CREDIT 4110 (Transaction Fee Revenue) - E5
Total Debits: E100
Total Credits: E100 ✅Ledger Entries
Individual debit or credit records that make up a journal entry.
Entry ID: LE-000456
Journal: JE-000123
Account: 1110 (User Wallets)
Type: DEBIT
Amount: E100
Entity: User #68e3ba...
Status: POSTEDImmutability
Once a ledger entry is POSTED, it cannot be modified or deleted. This is enforced at the database level.
To correct a posted entry, you must create a reversing entry:
Original Entry:
DEBIT: User A Wallet -E100
CREDIT: User B Wallet +E100
Reversing Entry:
CREDIT: User A Wallet +E100
DEBIT: User B Wallet -E100
Net Effect: Transaction cancelledTransaction Workflow
1. Pending State
New transactions start as PENDING for a validation period (default: 30 minutes).
Purpose:
- Allow time for fraud detection
- Enable AML (Anti-Money Laundering) checks
- Review high-value transactions
- Detect duplicate submissions
2. Posted State
After validation, entries are POSTED and become immutable.
Triggers:
- Manual approval by admin
- Automatic after 30-minute window
- Instant posting for low-value transactions (autoPost: true)
3. Reversed State
Posted entries can be REVERSED if correction is needed.
Process:
- Original entry remains intact (for audit)
- Reversing entry created with opposite debits/credits
- Both entries linked via
reversalOfandreversedByfields
System Architecture
┌───────────────────────────────────────┐
│ Application Layer │
│ (Wallet APIs, Payment Controllers) │
└─────────────┬─────────────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Service Layer │
│ ┌─────────────────────────────────┐ │
│ │ Transaction Templates Service │ │
│ │ (Pre-built double-entry patterns)│ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Ledger Service │ │
│ │ (Create/Reverse journal entries)│ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Posting Service │ │
│ │ (Pending → Posted workflow) │ │
│ └─────────────────────────────────┘ │
└─────────────┬─────────────────────────┘
│
▼
┌───────────────────────────────────────┐
│ Data Layer │
│ ┌──────────┐ ┌─────────────┐ │
│ │ Journal │ │ Ledger │ │
│ │ Entry │→ │ Entry │ │
│ └──────────┘ └─────────────┘ │
│ ┌──────────┐ ┌─────────────┐ │
│ │ Account │ │ Audit │ │
│ │ Balance │ │ Log │ │
│ └──────────┘ └─────────────┘ │
└───────────────────────────────────────┘ACID Compliance
All operations use PostgreSQL transactions to ensure ACID properties:
- Atomicity: All operations succeed or all fail
- Consistency: Database remains in valid state
- Isolation: Concurrent transactions don't interfere
- Durability: Committed transactions persist
await prisma.$transaction(async (tx) => {
// All operations within the transaction
await createJournal(tx);
await createLedgerEntries(tx);
await updateAccountBalances(tx);
await createAuditLog(tx);
// ✅ All committed on success
// ❌ All rolled back on error
});Security Features
1. Hash Chaining
Audit logs use SHA-256 hash chaining to detect tampering:
Entry 1: hash(data1)
Entry 2: hash(data2 + hash1)
Entry 3: hash(data3 + hash2)If any entry is modified, the chain breaks immediately.
2. Immutable Ledger
Database-level middleware prevents updates/deletes on posted entries:
LedgerEntrySchema.pre('findOneAndUpdate', function() {
if (this.getQuery().status === 'POSTED') {
throw new Error('Cannot modify posted ledger entry');
}
});3. Complete Audit Trail
Every action is logged with:
- Actor ID (who)
- Timestamp (when)
- IP Address (from where)
- Request ID (for tracing)
- Changes (what changed)
Currency
The system uses E (Emalageni) exclusively. No multi-currency support.
All amounts are stored as numbers with 2 decimal precision:
- E100.00 → stored as 100.00
- E5.50 → stored as 5.50
Next Steps
Explore the accounting system in depth:
- Chart of Accounts - Account structure
- Double-Entry Principles - How it works
- Workflows - Pending/Posted lifecycle
- Reconciliation - Balance verification