240 lines
10 KiB
PL/PgSQL
240 lines
10 KiB
PL/PgSQL
-- DBIS Core Lite Database Schema
|
|
-- PostgreSQL 14+
|
|
|
|
-- Operators (Terminal Users)
|
|
CREATE TABLE IF NOT EXISTS operators (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
operator_id VARCHAR(50) UNIQUE NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
email VARCHAR(255) UNIQUE,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
role VARCHAR(50) NOT NULL CHECK (role IN ('MAKER', 'CHECKER', 'ADMIN')),
|
|
active BOOLEAN DEFAULT TRUE,
|
|
last_login_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_operators_operator_id ON operators(operator_id);
|
|
CREATE INDEX idx_operators_role ON operators(role);
|
|
|
|
-- Payments (Payment Transactions)
|
|
CREATE TABLE IF NOT EXISTS payments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
payment_id VARCHAR(100) UNIQUE NOT NULL,
|
|
type VARCHAR(50) NOT NULL CHECK (type IN ('CUSTOMER_CREDIT_TRANSFER', 'FI_TO_FI')),
|
|
amount DECIMAL(18, 2) NOT NULL,
|
|
currency VARCHAR(3) NOT NULL,
|
|
sender_account VARCHAR(100) NOT NULL,
|
|
sender_bic VARCHAR(11) NOT NULL,
|
|
receiver_account VARCHAR(100) NOT NULL,
|
|
receiver_bic VARCHAR(11) NOT NULL,
|
|
beneficiary_name VARCHAR(255) NOT NULL,
|
|
purpose TEXT,
|
|
remittance_info TEXT,
|
|
maker_operator_id UUID NOT NULL REFERENCES operators(id),
|
|
checker_operator_id UUID REFERENCES operators(id),
|
|
status VARCHAR(50) NOT NULL,
|
|
internal_transaction_id VARCHAR(100),
|
|
compliance_screening_id VARCHAR(100),
|
|
compliance_status VARCHAR(20) CHECK (compliance_status IN ('PASS', 'FAIL', 'PENDING')),
|
|
uetr UUID,
|
|
iso_message_id VARCHAR(100),
|
|
iso_message_hash VARCHAR(64),
|
|
transport_session_id VARCHAR(100),
|
|
ack_received BOOLEAN DEFAULT FALSE,
|
|
nack_reason TEXT,
|
|
settlement_confirmed BOOLEAN DEFAULT FALSE,
|
|
settlement_date TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_payments_payment_id ON payments(payment_id);
|
|
CREATE INDEX idx_payments_status ON payments(status);
|
|
CREATE INDEX idx_payments_uetr ON payments(uetr);
|
|
CREATE INDEX idx_payments_maker ON payments(maker_operator_id);
|
|
CREATE INDEX idx_payments_created_at ON payments(created_at);
|
|
|
|
-- Ledger Postings (Core Banking Transactions)
|
|
CREATE TABLE IF NOT EXISTS ledger_postings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
internal_transaction_id VARCHAR(100) UNIQUE NOT NULL,
|
|
payment_id UUID NOT NULL REFERENCES payments(id),
|
|
account_number VARCHAR(100) NOT NULL,
|
|
transaction_type VARCHAR(20) NOT NULL CHECK (transaction_type IN ('DEBIT', 'CREDIT', 'RESERVE', 'RELEASE')),
|
|
amount DECIMAL(18, 2) NOT NULL,
|
|
currency VARCHAR(3) NOT NULL,
|
|
status VARCHAR(20) NOT NULL CHECK (status IN ('PENDING', 'POSTED', 'FAILED', 'REVERSED')),
|
|
posting_timestamp TIMESTAMP WITH TIME ZONE,
|
|
reference TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_ledger_postings_transaction_id ON ledger_postings(internal_transaction_id);
|
|
CREATE INDEX idx_ledger_postings_payment_id ON ledger_postings(payment_id);
|
|
CREATE INDEX idx_ledger_postings_account ON ledger_postings(account_number);
|
|
CREATE INDEX idx_ledger_postings_status ON ledger_postings(status);
|
|
|
|
-- ISO Messages
|
|
CREATE TABLE IF NOT EXISTS iso_messages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
message_id VARCHAR(100) UNIQUE NOT NULL,
|
|
payment_id UUID NOT NULL REFERENCES payments(id),
|
|
message_type VARCHAR(20) NOT NULL CHECK (message_type IN ('pacs.008', 'pacs.009')),
|
|
uetr UUID NOT NULL,
|
|
msg_id VARCHAR(100) NOT NULL,
|
|
xml_content TEXT NOT NULL,
|
|
xml_hash VARCHAR(64) NOT NULL,
|
|
status VARCHAR(20) NOT NULL CHECK (status IN ('GENERATED', 'VALIDATED', 'TRANSMITTED', 'ACK_RECEIVED', 'NACK_RECEIVED')),
|
|
transmitted_at TIMESTAMP WITH TIME ZONE,
|
|
ack_received_at TIMESTAMP WITH TIME ZONE,
|
|
nack_reason TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_iso_messages_message_id ON iso_messages(message_id);
|
|
CREATE INDEX idx_iso_messages_payment_id ON iso_messages(payment_id);
|
|
CREATE INDEX idx_iso_messages_uetr ON iso_messages(uetr);
|
|
CREATE INDEX idx_iso_messages_status ON iso_messages(status);
|
|
|
|
-- Transport Sessions (TLS Connections)
|
|
CREATE TABLE IF NOT EXISTS transport_sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id VARCHAR(100) UNIQUE NOT NULL,
|
|
receiver_ip VARCHAR(45) NOT NULL,
|
|
receiver_port INTEGER NOT NULL,
|
|
tls_version VARCHAR(10),
|
|
session_fingerprint VARCHAR(64),
|
|
connected_at TIMESTAMP WITH TIME ZONE,
|
|
disconnected_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_transport_sessions_session_id ON transport_sessions(session_id);
|
|
CREATE INDEX idx_transport_sessions_connected_at ON transport_sessions(connected_at);
|
|
|
|
-- ACK/NACK Logs
|
|
CREATE TABLE IF NOT EXISTS ack_nack_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
message_id UUID NOT NULL REFERENCES iso_messages(id),
|
|
payment_id UUID NOT NULL REFERENCES payments(id),
|
|
uetr UUID NOT NULL,
|
|
msg_id VARCHAR(100) NOT NULL,
|
|
type VARCHAR(4) NOT NULL CHECK (type IN ('ACK', 'NACK')),
|
|
payload TEXT NOT NULL,
|
|
reason TEXT,
|
|
received_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_ack_nack_logs_message_id ON ack_nack_logs(message_id);
|
|
CREATE INDEX idx_ack_nack_logs_payment_id ON ack_nack_logs(payment_id);
|
|
CREATE INDEX idx_ack_nack_logs_uetr ON ack_nack_logs(uetr);
|
|
CREATE INDEX idx_ack_nack_logs_received_at ON ack_nack_logs(received_at);
|
|
|
|
-- Settlement Records
|
|
CREATE TABLE IF NOT EXISTS settlement_records (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
payment_id UUID NOT NULL REFERENCES payments(id),
|
|
uetr UUID NOT NULL,
|
|
status VARCHAR(30) NOT NULL CHECK (status IN ('PENDING', 'ACK_RECEIVED', 'CREDIT_CONFIRMED', 'SETTLED', 'FAILED')),
|
|
ack_received BOOLEAN DEFAULT FALSE,
|
|
ack_received_at TIMESTAMP WITH TIME ZONE,
|
|
credit_confirmed BOOLEAN DEFAULT FALSE,
|
|
credit_confirmed_at TIMESTAMP WITH TIME ZONE,
|
|
credit_confirmation_reference VARCHAR(100),
|
|
settled_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_settlement_records_payment_id ON settlement_records(payment_id);
|
|
CREATE INDEX idx_settlement_records_uetr ON settlement_records(uetr);
|
|
CREATE INDEX idx_settlement_records_status ON settlement_records(status);
|
|
|
|
-- Reconciliation Runs
|
|
CREATE TABLE IF NOT EXISTS reconciliation_runs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
run_date DATE NOT NULL,
|
|
run_timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
total_payments INTEGER DEFAULT 0,
|
|
matched_payments INTEGER DEFAULT 0,
|
|
unmatched_payments INTEGER DEFAULT 0,
|
|
exceptions INTEGER DEFAULT 0,
|
|
status VARCHAR(20) NOT NULL CHECK (status IN ('RUNNING', 'COMPLETED', 'FAILED')),
|
|
completed_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_reconciliation_runs_run_date ON reconciliation_runs(run_date);
|
|
CREATE INDEX idx_reconciliation_runs_status ON reconciliation_runs(status);
|
|
|
|
-- Audit Logs (Tamper-evident)
|
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
event_type VARCHAR(100) NOT NULL,
|
|
entity_type VARCHAR(50),
|
|
entity_id VARCHAR(100),
|
|
operator_id VARCHAR(50),
|
|
terminal_id VARCHAR(100),
|
|
action VARCHAR(100) NOT NULL,
|
|
details JSONB,
|
|
checksum VARCHAR(64) NOT NULL, -- SHA-256 of previous row + current row
|
|
timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_audit_logs_event_type ON audit_logs(event_type);
|
|
CREATE INDEX idx_audit_logs_entity_type ON audit_logs(entity_type, entity_id);
|
|
CREATE INDEX idx_audit_logs_operator_id ON audit_logs(operator_id);
|
|
CREATE INDEX idx_audit_logs_timestamp ON audit_logs(timestamp);
|
|
|
|
-- Export History
|
|
CREATE TABLE IF NOT EXISTS export_history (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
format VARCHAR(20) NOT NULL CHECK (format IN ('rje', 'xmlv2', 'raw-iso', 'json')),
|
|
scope VARCHAR(20) NOT NULL CHECK (scope IN ('messages', 'ledger', 'full')),
|
|
record_count INTEGER NOT NULL DEFAULT 0,
|
|
file_size BIGINT NOT NULL DEFAULT 0,
|
|
filename VARCHAR(255) NOT NULL,
|
|
start_date TIMESTAMP WITH TIME ZONE,
|
|
end_date TIMESTAMP WITH TIME ZONE,
|
|
account_number VARCHAR(100),
|
|
uetr UUID,
|
|
payment_id UUID,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_export_history_format ON export_history(format);
|
|
CREATE INDEX idx_export_history_scope ON export_history(scope);
|
|
CREATE INDEX idx_export_history_created_at ON export_history(created_at);
|
|
CREATE INDEX idx_export_history_uetr ON export_history(uetr);
|
|
CREATE INDEX idx_export_history_payment_id ON export_history(payment_id);
|
|
|
|
-- Function to update updated_at timestamp
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
RETURN NEW;
|
|
END;
|
|
$$ language 'plpgsql';
|
|
|
|
-- Triggers for updated_at
|
|
CREATE TRIGGER update_payments_updated_at BEFORE UPDATE ON payments
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER update_operators_updated_at BEFORE UPDATE ON operators
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER update_ledger_postings_updated_at BEFORE UPDATE ON ledger_postings
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER update_iso_messages_updated_at BEFORE UPDATE ON iso_messages
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER update_settlement_records_updated_at BEFORE UPDATE ON settlement_records
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|