-- Wallet entry points over the existing customer-account ledger: -- simulated recharge records and withdrawal applications awaiting review. -- Balances themselves stay in customer_accounts; these tables are the -- business records whose lifecycle drives ledger entries. CREATE TYPE wallet_recharge_status AS ENUM ('credited', 'failed'); CREATE TABLE wallet_recharges ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE, currency CHAR(3) NOT NULL REFERENCES currencies (code), amount_minor BIGINT NOT NULL CHECK (amount_minor > 0), status wallet_recharge_status NOT NULL DEFAULT 'credited', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX wallet_recharges_user_idx ON wallet_recharges (user_id, created_at DESC); CREATE TYPE wallet_withdrawal_status AS ENUM ('pending', 'approved', 'rejected'); CREATE TABLE wallet_withdrawals ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE, currency CHAR(3) NOT NULL REFERENCES currencies (code), amount_minor BIGINT NOT NULL CHECK (amount_minor > 0), -- Free-form payout destination captured at application time. account_details JSONB NOT NULL DEFAULT '{}'::jsonb, status wallet_withdrawal_status NOT NULL DEFAULT 'pending', reviewed_by UUID REFERENCES users (id), reviewed_at TIMESTAMPTZ, review_note TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- A reviewed row always records who and when; a pending row never does. CONSTRAINT wallet_withdrawals_review_consistent CHECK ( (status = 'pending' AND reviewed_by IS NULL AND reviewed_at IS NULL) OR (status <> 'pending' AND reviewed_by IS NOT NULL AND reviewed_at IS NOT NULL) ) ); CREATE INDEX wallet_withdrawals_user_idx ON wallet_withdrawals (user_id, created_at DESC); CREATE INDEX wallet_withdrawals_status_idx ON wallet_withdrawals (status, created_at DESC);