Skip to content

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:

  1. Assets (1000-1999) - What the company owns

    • User Wallets (cash in user accounts)
    • Vendor Wallets (cash in vendor accounts)
    • Cash in Transit
  2. Liabilities (2000-2999) - What the company owes

    • Pending Settlements
    • User Liabilities
    • Vendor Payables
  3. Equity (3000-3999) - Owner's stake in the company

    • Retained Earnings
    • Owner's Capital
  4. Revenue (4000-4999) - Money earned

    • Transaction Fee Revenue
    • Withdrawal Fee Revenue
    • Commission Income
  5. Expenses (5000-5999) - Money spent

    • Processing Costs
    • Refund Expenses
    • Operating Expenses

Normal Balances

Each account type has a normal balance:

Account TypeNormal BalanceIncreases byDecreases by
AssetDEBITDebitCredit
LiabilityCREDITCreditDebit
EquityCREDITCreditDebit
RevenueCREDITCreditDebit
ExpenseDEBITDebitCredit

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: POSTED

Immutability

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 cancelled

Transaction 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:

  1. Original entry remains intact (for audit)
  2. Reversing entry created with opposite debits/credits
  3. Both entries linked via reversalOf and reversedBy fields

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
typescript
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:

typescript
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:

Internal use only - Keshless Payment Platform