new-site/api/migrations/007_refunds.sql
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

78 lines
3.8 KiB
PL/PgSQL

-- 007_refunds.sql
-- Refund tracking for failed filings that were not the customer's fault.
-- Covers: state portal errors, payment processing failures, name collisions
-- discovered after payment, state rejections, etc.
BEGIN;
CREATE TABLE IF NOT EXISTS refunds (
id SERIAL PRIMARY KEY,
refund_number TEXT UNIQUE NOT NULL, -- REF-2026-XXXXXX
-- What's being refunded
order_type TEXT NOT NULL CHECK (order_type IN ('formation', 'service')),
order_id INTEGER, -- FK to formation_orders.id or orders.id
order_number TEXT NOT NULL, -- PW-2026-XXXX or ORD-2026-XXXX
-- Who
customer_name TEXT NOT NULL,
customer_email TEXT NOT NULL,
-- Amount
original_amount_cents INTEGER NOT NULL, -- what was originally charged
refund_amount_cents INTEGER NOT NULL, -- what we're refunding (may be partial)
refund_type TEXT NOT NULL CHECK (refund_type IN ('full', 'partial', 'state_fee_only', 'service_fee_only')),
-- Why
reason_category TEXT NOT NULL CHECK (reason_category IN (
'state_portal_error', -- state portal down, rejected filing incorrectly
'payment_failed', -- card charged but filing didn't go through
'name_collision', -- name became unavailable between search and filing
'state_rejected', -- state rejected the filing after payment
'automation_error', -- our automation failed and couldn't recover
'duplicate_charge', -- accidentally charged twice
'customer_request', -- customer changed mind (discretionary)
'other'
)),
reason_detail TEXT, -- free-text explanation
-- State fee recovery
state_fee_recoverable BOOLEAN DEFAULT FALSE, -- can we get the state fee back?
state_fee_recovered BOOLEAN DEFAULT FALSE,
state_fee_recovery_notes TEXT,
-- Processing
status TEXT DEFAULT 'pending' CHECK (status IN (
'pending', -- refund requested, not yet approved
'approved', -- admin approved, ready to process
'processing', -- refund being sent via Relay
'sent', -- ACH/card refund sent
'confirmed', -- customer confirmed receipt
'denied', -- refund request denied (with reason)
'cancelled'
)),
-- Approval
requested_by TEXT, -- 'system', 'admin', 'customer'
requested_at TIMESTAMPTZ DEFAULT now(),
approved_by INTEGER, -- admin_users.id
approved_at TIMESTAMPTZ,
denied_reason TEXT,
-- Payment
refund_method TEXT CHECK (refund_method IN ('relay_ach', 'card_reversal', 'check', 'credit')),
relay_transaction_id TEXT,
sent_at TIMESTAMPTZ,
confirmed_at TIMESTAMPTZ,
-- Metadata
admin_notes TEXT,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_refunds_order ON refunds(order_type, order_id);
CREATE INDEX IF NOT EXISTS idx_refunds_status ON refunds(status);
CREATE INDEX IF NOT EXISTS idx_refunds_number ON refunds(refund_number);
CREATE INDEX IF NOT EXISTS idx_refunds_email ON refunds(customer_email);
-- Add refund tracking to formation_orders
ALTER TABLE formation_orders ADD COLUMN IF NOT EXISTS refund_id INTEGER REFERENCES refunds(id);
ALTER TABLE formation_orders ADD COLUMN IF NOT EXISTS refunded BOOLEAN DEFAULT FALSE;
-- Add refund tracking to general orders
ALTER TABLE orders ADD COLUMN IF NOT EXISTS refund_id INTEGER REFERENCES refunds(id);
ALTER TABLE orders ADD COLUMN IF NOT EXISTS refunded BOOLEAN DEFAULT FALSE;
COMMIT;