4.4 KiB
4.4 KiB
Account & Ledger Specification
Overview
This document specifies the customer ledger system using double-entry bookkeeping principles for banking operations.
Double-Entry Ledger Schema
Ledger Entry
CREATE TABLE ledger_entries (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
transaction_id UUID NOT NULL,
entry_type VARCHAR(20) NOT NULL, -- 'debit', 'credit'
account_type VARCHAR(50) NOT NULL, -- 'cash', 'crypto', 'fiat', etc.
amount NUMERIC(78, 0) NOT NULL,
currency VARCHAR(10) NOT NULL,
description TEXT,
reference_id VARCHAR(255), -- External transaction reference
created_at TIMESTAMP DEFAULT NOW(),
FOREIGN KEY (customer_id) REFERENCES customers(id),
FOREIGN KEY (transaction_id) REFERENCES transactions(id)
);
Transaction Table
CREATE TABLE transactions (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
transaction_type VARCHAR(50) NOT NULL, -- 'deposit', 'withdrawal', 'transfer', etc.
status VARCHAR(20) NOT NULL, -- 'pending', 'completed', 'failed'
amount NUMERIC(78, 0) NOT NULL,
currency VARCHAR(10) NOT NULL,
from_account UUID,
to_account UUID,
blockchain_tx_hash VARCHAR(66), -- If blockchain transaction
created_at TIMESTAMP DEFAULT NOW(),
completed_at TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
Double-Entry Rule
Constraint: Sum of debits = Sum of credits for each transaction
Implementation: Two entries per transaction (debit and credit)
Wallet Mapping
Customer-Wallet Mapping
CREATE TABLE customer_wallets (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
chain_id INTEGER NOT NULL,
address VARCHAR(42) NOT NULL,
wallet_type VARCHAR(20) NOT NULL, -- 'external', 'custodial'
verified BOOLEAN DEFAULT false,
verified_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE (customer_id, chain_id, address),
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
Wallet Verification
Process:
- Customer provides address
- Request signature verification
- Verify signature matches address
- Link address to customer account
- Mark as verified
Reconciliation Jobs
Reconciliation Process
Purpose: Ensure ledger matches actual blockchain/custodial balances
Frequency: Daily or real-time (depending on account type)
Steps:
- Query actual balances (blockchain or custodial wallet)
- Calculate expected balance from ledger
- Compare and identify discrepancies
- Create reconciliation entries if needed
- Alert on discrepancies
Reconciliation Schema
CREATE TABLE reconciliations (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
account_type VARCHAR(50) NOT NULL,
currency VARCHAR(10) NOT NULL,
expected_balance NUMERIC(78, 0) NOT NULL,
actual_balance NUMERIC(78, 0) NOT NULL,
discrepancy NUMERIC(78, 0) NOT NULL,
status VARCHAR(20) NOT NULL, -- 'matched', 'discrepancy', 'resolved'
reconciled_at TIMESTAMP DEFAULT NOW(),
resolved_at TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
Audit Trails
Audit Log Schema
CREATE TABLE audit_logs (
id UUID PRIMARY KEY,
customer_id UUID,
action VARCHAR(100) NOT NULL,
resource_type VARCHAR(50) NOT NULL,
resource_id VARCHAR(255),
old_value JSONB,
new_value JSONB,
actor_id UUID NOT NULL, -- User/system that performed action
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
Immutable Log Storage
Storage: Append-only log (immutable) Retention: Per regulatory requirements (typically 7 years) Access: Audit team only Integrity: Cryptographic hashing for tamper detection
Balance Calculations
Account Balance
Formula: Sum of credits - Sum of debits for account
Implementation:
SELECT
SUM(CASE WHEN entry_type = 'credit' THEN amount ELSE 0 END) -
SUM(CASE WHEN entry_type = 'debit' THEN amount ELSE 0 END) AS balance
FROM ledger_entries
WHERE customer_id = ? AND account_type = ? AND currency = ?;
Real-Time Balance
Caching: Cache balances with TTL Updates: Invalidate on new ledger entries Accuracy: Reconcile with actual balances periodically
References
- Identity & Compliance: See
identity-compliance.md - Payment Rails: See
payment-rails.md - Database Schema: See
../database/postgres-schema.md