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>
104 lines
4.5 KiB
PL/PgSQL
104 lines
4.5 KiB
PL/PgSQL
-- 017_identity_verifications.sql
|
|
-- Stripe Identity verification sessions for director KYC.
|
|
--
|
|
-- Every CRTC order director is verified via Stripe Identity BEFORE the order is
|
|
-- saved and before any payment is collected. The verification session extracts
|
|
-- name and date of birth from the ID document, which are compared against what
|
|
-- the customer entered on the order form.
|
|
--
|
|
-- Applies to ALL payment methods (card, ACH, Klarna, crypto).
|
|
--
|
|
-- Match result tiers:
|
|
-- name_match: exact | fuzzy_pass | fuzzy_warn | mismatch
|
|
-- dob_match: exact | no_dob_on_id | mismatch
|
|
-- overall: verified | pending | needs_review | failed
|
|
--
|
|
-- 'needs_review' orders are held in a manual queue — payment is NOT collected
|
|
-- until an admin clears them. This is a hard gate.
|
|
|
|
BEGIN;
|
|
|
|
CREATE TABLE IF NOT EXISTS identity_verifications (
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
-- Stripe Identity
|
|
stripe_session_id TEXT NOT NULL UNIQUE,
|
|
stripe_report_id TEXT, -- verification_report id once complete
|
|
stripe_status TEXT, -- 'requires_input' | 'processing' | 'verified' | 'canceled'
|
|
|
|
-- What customer typed on the form
|
|
form_director_name TEXT NOT NULL,
|
|
form_director_dob DATE, -- null if customer didn't enter DOB
|
|
|
|
-- What Stripe extracted from the ID document
|
|
id_first_name TEXT,
|
|
id_last_name TEXT,
|
|
id_full_name_extracted TEXT, -- first + last concatenated
|
|
id_dob_year INTEGER,
|
|
id_dob_month INTEGER,
|
|
id_dob_day INTEGER,
|
|
id_doc_type TEXT, -- 'driving_license' | 'passport' | 'id_card'
|
|
id_issuing_country TEXT,
|
|
id_expiry_year INTEGER,
|
|
id_expiry_month INTEGER,
|
|
id_expiry_day INTEGER,
|
|
id_number TEXT, -- redacted after comparison
|
|
|
|
-- Comparison results
|
|
name_match_score NUMERIC(5,2), -- 0-100 fuzzy score
|
|
name_match TEXT -- 'exact' | 'fuzzy_pass' | 'fuzzy_warn' | 'mismatch' | 'pending'
|
|
CHECK (name_match IN ('exact','fuzzy_pass','fuzzy_warn','mismatch','pending')),
|
|
dob_match TEXT -- 'exact' | 'no_dob_on_id' | 'mismatch' | 'pending'
|
|
CHECK (dob_match IN ('exact','no_dob_on_id','mismatch','pending')),
|
|
doc_expired BOOLEAN DEFAULT FALSE,
|
|
|
|
-- Overall gate result
|
|
overall_result TEXT NOT NULL DEFAULT 'pending'
|
|
CHECK (overall_result IN ('pending','verified','needs_review','failed')),
|
|
|
|
-- Admin review
|
|
reviewed_by TEXT,
|
|
review_notes TEXT,
|
|
reviewed_at TIMESTAMPTZ,
|
|
admin_override BOOLEAN DEFAULT FALSE, -- admin manually cleared needs_review
|
|
|
|
-- Linkage
|
|
order_number TEXT, -- set once order is created
|
|
order_type TEXT DEFAULT 'canada_crtc',
|
|
customer_email TEXT,
|
|
|
|
-- Audit
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
verified_at TIMESTAMPTZ,
|
|
ip_address TEXT,
|
|
user_agent TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_iv_session ON identity_verifications (stripe_session_id);
|
|
CREATE INDEX IF NOT EXISTS idx_iv_order ON identity_verifications (order_number);
|
|
CREATE INDEX IF NOT EXISTS idx_iv_result ON identity_verifications (overall_result, created_at);
|
|
CREATE INDEX IF NOT EXISTS idx_iv_needs_review ON identity_verifications (overall_result)
|
|
WHERE overall_result = 'needs_review' AND admin_override = FALSE;
|
|
|
|
-- Add identity_session_id to CRTC orders so the order route can gate on it
|
|
ALTER TABLE canada_crtc_orders
|
|
ADD COLUMN IF NOT EXISTS identity_session_id TEXT
|
|
REFERENCES identity_verifications(stripe_session_id) ON DELETE SET NULL;
|
|
|
|
ALTER TABLE canada_crtc_orders
|
|
ADD COLUMN IF NOT EXISTS identity_result TEXT
|
|
CHECK (identity_result IN ('verified','needs_review','failed','pending'));
|
|
|
|
-- Admin view: sessions awaiting review
|
|
CREATE OR REPLACE VIEW identity_pending_review AS
|
|
SELECT
|
|
iv.*,
|
|
o.order_number AS linked_order,
|
|
o.customer_email AS order_email
|
|
FROM identity_verifications iv
|
|
LEFT JOIN canada_crtc_orders o ON o.identity_session_id = iv.stripe_session_id
|
|
WHERE iv.overall_result = 'needs_review'
|
|
AND iv.admin_override = FALSE
|
|
ORDER BY iv.created_at DESC;
|
|
|
|
COMMIT;
|