CREATE TABLE IF NOT EXISTS schema_migrations ( version bigint PRIMARY KEY, applied_at timestamptz NOT NULL DEFAULT now() ); -- statement-break CREATE TABLE IF NOT EXISTS import_runs ( id bigserial PRIMARY KEY, source_sha256 text NOT NULL UNIQUE, source_path text NOT NULL, status text NOT NULL CHECK (status IN ('RUNNING','COMPLETED','FAILED')), source_rows bigint NOT NULL DEFAULT 0, session_rows bigint NOT NULL DEFAULT 0, attempt_rows bigint NOT NULL DEFAULT 0, started_at timestamptz NOT NULL DEFAULT now(), completed_at timestamptz ); -- statement-break CREATE TABLE IF NOT EXISTS merchants ( id bigserial PRIMARY KEY, source_key text NOT NULL UNIQUE, category_id bigint NOT NULL, category_title text NOT NULL, display_name text NOT NULL, profile_is_synthetic boolean NOT NULL DEFAULT true ); -- statement-break CREATE TABLE IF NOT EXISTS terminals ( id bigserial PRIMARY KEY, merchant_id bigint NOT NULL REFERENCES merchants(id), source_key text NOT NULL, verify_type text NOT NULL, UNIQUE (merchant_id, source_key) ); -- statement-break CREATE TABLE IF NOT EXISTS payment_sessions ( id bigserial PRIMARY KEY, import_run_id bigint NOT NULL REFERENCES import_runs(id), merchant_id bigint NOT NULL REFERENCES merchants(id), terminal_id bigint NOT NULL REFERENCES terminals(id), source_key text NOT NULL, status text NOT NULL, amount_rial bigint NOT NULL CHECK (amount_rial >= 0), fee_amount_rial bigint NOT NULL CHECK (fee_amount_rial >= 0), payer_card_key text, created_at timestamptz NOT NULL, expires_at timestamptz, verified_at timestamptz, settled_at timestamptz, UNIQUE (merchant_id, source_key) ); -- statement-break CREATE TABLE IF NOT EXISTS payment_attempts ( id bigserial PRIMARY KEY, session_id bigint NOT NULL REFERENCES payment_sessions(id) ON DELETE CASCADE, sequence integer NOT NULL CHECK (sequence > 0), status text NOT NULL, created_at timestamptz, init_time_ms integer, verify_time_ms integer, UNIQUE (session_id, sequence) ); -- statement-break CREATE INDEX IF NOT EXISTS payment_sessions_merchant_created_idx ON payment_sessions (merchant_id, created_at DESC, id DESC); -- statement-break CREATE INDEX IF NOT EXISTS payment_sessions_status_idx ON payment_sessions (merchant_id, status, created_at DESC); -- statement-break CREATE INDEX IF NOT EXISTS payment_attempts_session_idx ON payment_attempts (session_id, sequence); -- statement-break CREATE TABLE IF NOT EXISTS merchant_users ( id bigserial PRIMARY KEY, merchant_id bigint NOT NULL REFERENCES merchants(id), username text NOT NULL UNIQUE, password_hash text NOT NULL, is_active boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now() ); -- statement-break CREATE TABLE IF NOT EXISTS auth_sessions ( id bigserial PRIMARY KEY, user_id bigint NOT NULL REFERENCES merchant_users(id), merchant_id bigint NOT NULL REFERENCES merchants(id), token_hash bytea NOT NULL UNIQUE, csrf_token text NOT NULL, created_at timestamptz NOT NULL, idle_expires_at timestamptz NOT NULL, absolute_expires_at timestamptz NOT NULL, revoked_at timestamptz, last_seen_at timestamptz NOT NULL ); -- statement-break CREATE INDEX IF NOT EXISTS auth_sessions_token_idx ON auth_sessions (token_hash) WHERE revoked_at IS NULL; -- statement-break INSERT INTO schema_migrations(version) VALUES (1) ON CONFLICT DO NOTHING;