Fixes: - Negotiate route: added auth check (only shipper or bidder can negotiate) - Negotiate route: added notification to other party - All payment views: removed /100 division (amounts stored in rupees, not paise) - Migration 006: updated platform_config seed values to rupees - Migration 007: added current_lat/current_lng columns to vehicles table - Added bulk-parser route to marketplace.js - Added Bulk WhatsApp Parser link to portal sidebar Seed Data: - scripts/seed-demo.js: 5 shippers, 5 drivers, 8 loads, sample bids - Idempotent: skips if data already exists
122 lines
5.3 KiB
SQL
122 lines
5.3 KiB
SQL
-- ============================================================
|
|
-- FreightDesk — Migration 006: Payment Escrow System
|
|
-- Escrow payments, transactions, settlements, payouts
|
|
-- ============================================================
|
|
|
|
-- ============================================================
|
|
-- 1. ESCROW ACCOUNTS (one per user — shipper or driver)
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS escrow_accounts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL,
|
|
role TEXT NOT NULL CHECK (role IN ('shipper', 'driver')),
|
|
balance INTEGER DEFAULT 0, -- available balance in paise
|
|
held_balance INTEGER DEFAULT 0, -- funds in escrow (in paise)
|
|
total_deposited INTEGER DEFAULT 0,
|
|
total_withdrawn INTEGER DEFAULT 0,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
UNIQUE(user_id, role)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_escrow_user ON escrow_accounts(user_id);
|
|
|
|
-- ============================================================
|
|
-- 2. ESCROW TRANSACTIONS
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS escrow_transactions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
escrow_account_id UUID REFERENCES escrow_accounts(id),
|
|
load_id UUID REFERENCES loads(id),
|
|
bid_id UUID REFERENCES bids(id),
|
|
type TEXT NOT NULL CHECK (type IN (
|
|
'deposit', -- shipper deposits funds
|
|
'hold', -- funds moved to escrow hold
|
|
'release', -- funds released to driver
|
|
'refund', -- funds refunded to shipper
|
|
'payout', -- driver withdraws to bank
|
|
'platform_fee', -- FreightDesk commission
|
|
'adjustment' -- manual admin adjustment
|
|
)),
|
|
amount INTEGER NOT NULL, -- in paise
|
|
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'completed', 'failed', 'reversed')),
|
|
reference_id TEXT, -- external payment reference
|
|
metadata JSONB DEFAULT '{}',
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
completed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_escrow_tx_account ON escrow_transactions(escrow_account_id);
|
|
CREATE INDEX IF NOT EXISTS idx_escrow_tx_load ON escrow_transactions(load_id);
|
|
CREATE INDEX IF NOT EXISTS idx_escrow_tx_type ON escrow_transactions(type);
|
|
CREATE INDEX IF NOT EXISTS idx_escrow_tx_status ON escrow_transactions(status);
|
|
|
|
-- ============================================================
|
|
-- 3. PAYOUT REQUESTS (driver withdrawal requests)
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS payout_requests (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL,
|
|
driver_id UUID REFERENCES vehicles(id),
|
|
amount INTEGER NOT NULL,
|
|
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected', 'processed')),
|
|
bank_name TEXT,
|
|
account_number TEXT,
|
|
ifsc_code TEXT,
|
|
upi_id TEXT,
|
|
processed_by UUID,
|
|
processed_at TIMESTAMP WITH TIME ZONE,
|
|
notes TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_payout_driver ON payout_requests(driver_id);
|
|
CREATE INDEX IF NOT EXISTS idx_payout_status ON payout_requests(status);
|
|
|
|
-- ============================================================
|
|
-- 4. PLATFORM CONFIG (fee settings)
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS platform_config (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL,
|
|
description TEXT,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
-- Default fee settings (amounts in rupees)
|
|
INSERT INTO platform_config (key, value, description) VALUES
|
|
('escrow.platform_fee_percent', '5', 'Platform commission percentage'),
|
|
('escrow.min_deposit_amount', '100', 'Minimum deposit in rupees'),
|
|
('escrow.hold_period_hours', '72', 'Hours to hold funds after delivery before auto-release'),
|
|
('escrow.payout_min_amount', '500', 'Minimum payout request in rupees'),
|
|
('escrow.payout_fee', '0', 'Payout processing fee in rupees')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
-- ============================================================
|
|
-- 5. LOAD PAYMENT STATUS (tracks payment state per load)
|
|
-- ============================================================
|
|
ALTER TABLE loads ADD COLUMN IF NOT EXISTS payment_status TEXT DEFAULT 'none'
|
|
CHECK (payment_status IN ('none', 'deposited', 'in_escrow', 'released', 'refunded', 'disputed'));
|
|
|
|
ALTER TABLE loads ADD COLUMN IF NOT EXISTS escrow_amount INTEGER;
|
|
ALTER TABLE loads ADD COLUMN IF NOT EXISTS platform_fee INTEGER;
|
|
ALTER TABLE loads ADD COLUMN IF NOT EXISTS settled_at TIMESTAMP WITH TIME ZONE;
|
|
|
|
-- ============================================================
|
|
-- 6. DISPUTES TABLE
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS disputes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
load_id UUID NOT NULL REFERENCES loads(id),
|
|
raised_by UUID NOT NULL,
|
|
raised_against UUID NOT NULL,
|
|
reason TEXT NOT NULL,
|
|
status TEXT DEFAULT 'open' CHECK (status IN ('open', 'under_review', 'resolved', 'closed')),
|
|
resolution TEXT,
|
|
resolved_by UUID,
|
|
resolved_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_disputes_load ON disputes(load_id);
|
|
CREATE INDEX IF NOT EXISTS idx_disputes_status ON disputes(status);
|