Double-Entry Principles
Double-entry bookkeeping is a method where every transaction affects at least two accounts. For every debit, there must be a corresponding credit of equal value.
The Golden Rule
Total Debits = Total Credits (always!)
This fundamental principle ensures:
- Books always balance
- No money appears or disappears
- Every transaction is complete and traceable
How It Works
Basic Example: P2P Transfer
User A sends E100 to User B
Entry 1: DEBIT User A's Wallet -E100
Entry 2: CREDIT User B's Wallet +E100
Verification:
Total Debits: E100
Total Credits: E100
Balanced: ✅What happened:
- User A's asset (wallet) decreased by E100 (debit)
- User B's asset (wallet) increased by E100 (credit)
- Net effect on total assets: 0 (internal transfer)
Complex Example: Vendor Payment with Fee
User pays vendor E95, Keshless charges E5 fee
Entry 1: DEBIT User Wallet -E100
Entry 2: CREDIT Vendor Wallet +E95
Entry 3: CREDIT Transaction Fee Revenue +E5
Verification:
Total Debits: E100
Total Credits: E95 + E5 = E100
Balanced: ✅What happened:
- User's asset decreased by E100
- Vendor's asset increased by E95
- Keshless revenue increased by E5
- Net effect: Assets -E5, Revenue +E5 (profit!)
Debit vs Credit
Not "Addition" and "Subtraction"
Debit and Credit don't mean "add" or "subtract". They mean:
| Account Type | Debit Effect | Credit Effect |
|---|---|---|
| Asset | Increase | Decrease |
| Liability | Decrease | Increase |
| Equity | Decrease | Increase |
| Revenue | Decrease | Increase |
| Expense | Increase | Decrease |
Why This Matters
Example: User tops up wallet E100
DEBIT User Wallet +E100 (Asset increases)
CREDIT Cash in Transit +E100 (Liability increases)
Both accounts increase, but one is debit and one is credit.
This is correct because:
- Asset increase = Debit
- Liability increase = CreditT-Account Visualization
Accountants use "T-accounts" to visualize transactions:
User Wallet (Asset)
DEBIT | CREDIT
-------------------------
+Topup | -Payment
+Received | -Sent
+Refund | -Withdrawal
Transaction Fee Revenue (Revenue)
DEBIT | CREDIT
-------------------------
-Refund | +Fee earned
-Reversal | +CommissionCommon Transaction Patterns
1. Asset to Asset Exchange
Transfer between two asset accounts (P2P transfer):
DEBIT Asset A -100 (decrease)
CREDIT Asset B +100 (increase)
Example:
DEBIT User A Wallet -100
CREDIT User B Wallet +1002. Asset Increase with Liability Increase
Receiving external funds (topup):
DEBIT Asset +100 (increase)
CREDIT Liability +100 (increase)
Example:
DEBIT User Wallet +100
CREDIT Cash in Transit +1003. Asset Decrease with Revenue Increase
Earning revenue from operations:
DEBIT Asset A -100 (decrease)
CREDIT Asset B +95 (increase)
CREDIT Revenue +5 (increase)
Example:
DEBIT User Wallet -100
CREDIT Vendor Wallet +95
CREDIT Transaction Fee Revenue +54. Expense Increase with Asset Decrease
Paying for operational costs:
DEBIT Expense +50 (increase)
CREDIT Asset -50 (decrease)
Example:
DEBIT Processing Costs +50
CREDIT Cash in Transit -505. Revenue Decrease with Asset Increase
Refunding previously earned revenue:
DEBIT Revenue -5 (decrease)
DEBIT Asset A -95 (decrease)
CREDIT Asset B +100 (increase)
Example:
DEBIT Transaction Fee Revenue -5
DEBIT Vendor Wallet -95
CREDIT User Wallet +100Transaction Templates
The system provides pre-built templates that handle double-entry logic automatically:
// You write:
const template = transactionTemplatesService.getUserTransferTemplate(
senderId,
recipientId,
100
);
// System creates:
{
entries: [
{ accountCode: '1110', type: 'DEBIT', amount: 100, entityId: senderId },
{ accountCode: '1110', type: 'CREDIT', amount: 100, entityId: recipientId }
]
}
// Verified automatically: ✅ Debits (100) = Credits (100)Balance Verification
Trial Balance
A trial balance lists all accounts with their debit and credit balances:
Account Code | Account Name | Debit | Credit
---------------------------------------------------------
1110 | User Wallets | 10000 | 0
1120 | Vendor Wallets | 5000 | 0
2110 | Pending Settlements | 0 | 500
4110 | Transaction Fee Revenue | 0 | 1500
5110 | Processing Costs | 200 | 0
---------------------------------------------------------
TOTAL 15200 2000Wait, that doesn't balance! Let me fix:
Account Code | Account Name | Debit | Credit
---------------------------------------------------------
1110 | User Wallets | 10000 | 0
1120 | Vendor Wallets | 5000 | 0
2110 | Pending Settlements | 0 | 500
3100 | Retained Earnings | 0 | 13200
4110 | Transaction Fee Revenue | 0 | 1500
5110 | Processing Costs | 200 | 0
---------------------------------------------------------
TOTAL 15200 15200 ✅If total debits ≠ total credits, there's an error in the system.
Account Reconciliation
Each account should reconcile with source data:
User Wallet Balance (from User model): E1,250.00
User Wallet Ledger Total:
Total Debits: E3,500.00
Total Credits: E2,250.00
Balance: E1,250.00 ✅
Match! The wallet balance matches the ledger.If they don't match, reconciliation flags a discrepancy.
Error Prevention
Template Validation
Templates are validated before creation:
// ❌ This would fail:
{
entries: [
{ accountCode: '1110', type: 'DEBIT', amount: 100 },
{ accountCode: '1120', type: 'CREDIT', amount: 95 }
]
}
// Error: Debits (100) ≠ Credits (95)
// ✅ This succeeds:
{
entries: [
{ accountCode: '1110', type: 'DEBIT', amount: 100 },
{ accountCode: '1120', type: 'CREDIT', amount: 95 },
{ accountCode: '4110', type: 'CREDIT', amount: 5 }
]
}
// Valid: Debits (100) = Credits (95 + 5)Journal Entry Validation
Before committing to database:
const totalDebits = entries
.filter(e => e.type === 'DEBIT')
.reduce((sum, e) => sum + e.amount, 0);
const totalCredits = entries
.filter(e => e.type === 'CREDIT')
.reduce((sum, e) => sum + e.amount, 0);
if (Math.abs(totalDebits - totalCredits) > 0.01) {
throw new Error('Journal entry not balanced');
}Accounting Equation
The accounting equation must always hold:
Assets = Liabilities + Equity + Revenue - ExpensesOr rearranged:
Assets + Expenses = Liabilities + Equity + RevenueEvery transaction maintains this balance:
Example: User pays E95, Keshless earns E5 fee
Before:
Assets: 10000
Revenue: 1000
Total Right: 11000
Transaction:
Assets: -100 (user) +95 (vendor) = -5
Revenue: +5
After:
Assets: 9995
Revenue: 1005
Total Right: 11000 ✅
Equation holds!Reversals
To correct a posted transaction, create an offsetting entry:
Original Transaction:
DEBIT User A -100
CREDIT User B +100
Reversal Transaction:
CREDIT User A +100
DEBIT User B -100
Net Effect:
User A: -100 +100 = 0
User B: +100 -100 = 0
Transaction cancelled ✅The original entry remains in the ledger for audit purposes.
Best Practices
- Always use templates - They ensure balanced entries
- Never create manual entries - Risk of imbalance
- Verify trial balance daily - Catches errors early
- Use reversals, not edits - Maintains audit trail
- Reconcile regularly - Wallet balances vs ledger
Next Steps
- Chart of Accounts - Account structure
- Workflows - Pending/Posted lifecycle
- Transaction Templates - Pre-built patterns