new-site/scripts/workers/crypto_offramp/sizer.py
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

474 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Sizer — compute vendor obligations per order.
Called by ``crypto_payment_worker`` when transitioning received → sizing.
Returns the list of vendor_obligations rows to insert plus the total USD
that needs to land at RelayFi (``needed_usd_cents``).
Two categories of obligation:
1. filing_fee — paid via Relay debit card via Playwright flow.
Includes a +10% buffer for card-decline retries + slippage.
2. commission — paid via Relay ACH 14 days post-delivery. Exact amount.
The sizer reads order data from the relevant table (compliance_orders,
formation_orders, canada_crtc_orders, bundle_orders) + existing
config tables (state_filing_fees, sales_agents) + a small static map
for vendors that don't live in a table (NECA OCN, BC Registry, etc.).
"""
from __future__ import annotations
import json
import logging
import os
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import Optional
import psycopg2
import psycopg2.extras
logger = logging.getLogger(__name__)
# ── Static vendor fee map (single source of truth for non-table vendors) ──
#
# These are vendors whose fees are fixed + don't live in a queryable
# table. Keep in sync with the COMPLIANCE_SERVICES catalog in
# api/src/routes/compliance-orders.ts when their pricing changes.
STATIC_VENDOR_FEES_CENTS: dict[str, int] = {
"nwra": 12500, # Northwest Registered Agent $125/yr
"neca_ocn": 55000, # NECA OCN registration $550 standard
"neca_ocn_expedited": 67500, # NECA OCN expedited $675
"bc_registry": 27700, # BC Registry incorporation $277 CAD ≈ rounded
"cores_frn": 0, # FCC CORES FRN — no filing fee
"usac_499_initial": 0, # USAC 499 Initial — no filing fee
"usac_499a": 0, # USAC 499-A — no filing fee (contributions separate)
"fcc_cpni": 0, # FCC CPNI cert — no filing fee
"fcc_rmd": 10000, # FCC RMD — $100 filing fee (effective 2025)
"fcc_bdc": 0, # FCC BDC — no filing fee
"fcc_stir_shaken": 0, # FCC STIR/SHAKEN — no filing fee
"fcc_63_11": 0, # FCC Foreign Carrier Affiliation — no filing fee
"fcc_calea_ssi": 0, # CALEA SSI — no filing fee (internal plan)
"dc_agent": 0, # DC Agent — our DC agent service has no external fee
}
# Default filing-fee buffer in basis points (1000 bps = 10%)
DEFAULT_FILING_BUFFER_BPS = 1000
# Commission payout happens 14 days post-delivery per commission_ledger
# lifecycle (migration 011); reserve the cash at offramp time.
COMMISSION_DUE_DAYS = 14
@dataclass
class VendorObligation:
"""Matches the shape inserted into vendor_obligations."""
order_id: str
order_type: str
obligation_kind: str # 'filing_fee' | 'commission'
vendor: str
vendor_detail: Optional[str]
amount_usd_cents: int
fx_buffer_bps: int
due_by: Optional[datetime] = None
commission_ledger_id: Optional[int] = None
@dataclass
class SizingResult:
obligations: list[VendorObligation]
filing_total_cents: int # raw filing-fee sum, pre-buffer
filing_total_with_buffer_cents: int
commission_total_cents: int
needed_usd_cents: int # what we need at Relay
notes: list[str] = field(default_factory=list)
def _db_connect():
return psycopg2.connect(os.environ.get("DATABASE_URL", ""))
def compute_obligations(
*,
order_id: str,
order_type: str,
conn=None,
) -> SizingResult:
"""Compute vendor obligations for the given order.
``order_type`` is one of: 'compliance' | 'formation' | 'canada_crtc' | 'bundle'.
"""
close_after = False
if conn is None:
conn = _db_connect()
close_after = True
try:
if order_type == "compliance":
return _size_compliance(order_id, conn)
if order_type == "formation":
return _size_formation(order_id, conn)
if order_type == "canada_crtc":
return _size_canada_crtc(order_id, conn)
if order_type == "bundle":
return _size_bundle(order_id, conn)
logger.warning("sizer: unknown order_type=%s for %s", order_type, order_id)
return SizingResult([], 0, 0, 0, 0, ["unknown order_type — zero obligations"])
finally:
if close_after:
conn.close()
# ── Compliance orders (FCC filings + OCN + DC agent + bundles) ──────────
def _size_compliance(order_id: str, conn) -> SizingResult:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute(
"""
SELECT co.*, te.id AS entity_id,
(co.intake_data->>'expedited')::boolean AS expedited
FROM compliance_orders co
LEFT JOIN telecom_entities te ON te.id = co.telecom_entity_id
WHERE co.order_number = %s
""",
(order_id,),
)
order = cur.fetchone()
if not order:
return SizingResult([], 0, 0, 0, 0, [f"compliance order {order_id} not found"])
slug = order.get("service_slug") or ""
obligations: list[VendorObligation] = []
# OCN registration has real money to NECA; optional expedited upcharge.
if slug == "ocn-registration":
fee_key = "neca_ocn_expedited" if order.get("expedited") else "neca_ocn"
obligations.append(_filing_vo(
order_id, "compliance", "neca_ocn", None,
STATIC_VENDOR_FEES_CENTS[fee_key],
))
# Any service that includes NRA registered-agent coverage in its bundle.
# Customer opts in via intake_data.include_nwra_ra or similar; read the flag.
if (order.get("intake_data") or {}).get("include_nwra_ra"):
obligations.append(_filing_vo(
order_id, "compliance", "nwra", None,
STATIC_VENDOR_FEES_CENTS["nwra"],
))
# FCC 499-family, CPNI, RMD, BDC, STIR/SHAKEN, 63-11, CALEA SSI, DC agent,
# compliance checkup, CDR analysis, CORES FRN — no vendor filing fee.
# (Our service fee is pure markup; nothing to reserve for vendors.)
# Commission reserve if a sales agent is attached
commission = _compute_commission_obligation(
order_id, "compliance", order, slug, conn,
)
if commission:
obligations.append(commission)
filing = [o for o in obligations if o.obligation_kind == "filing_fee"]
comms = [o for o in obligations if o.obligation_kind == "commission"]
filing_total = sum(o.amount_usd_cents for o in filing)
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
comm_total = sum(o.amount_usd_cents for o in comms)
return SizingResult(
obligations=obligations,
filing_total_cents=filing_total,
filing_total_with_buffer_cents=filing_buf,
commission_total_cents=comm_total,
needed_usd_cents=filing_buf + comm_total,
)
# ── Formation orders (LLC / Corp state filings) ─────────────────────────
def _size_formation(order_id: str, conn) -> SizingResult:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute("SELECT * FROM formation_orders WHERE order_number = %s", (order_id,))
order = cur.fetchone()
if not order:
return SizingResult([], 0, 0, 0, 0, [f"formation order {order_id} not found"])
state = order.get("state_code") or order.get("state")
entity_type = (order.get("entity_type") or "llc").lower()
obligations: list[VendorObligation] = []
if state:
# state_filing_fees UNIT CONVENTIONS per column (confirmed by
# spot-checking 15+ states against real SOS fee schedules):
# llc_formation_fee / corp_formation_fee — CENTS (e.g., 7000 = $70 CA LLC)
# foreign_llc_fee / foreign_corp_fee — CENTS
# expedited_fee — DOLLARS × 10000
# (e.g., 7500000 = $750 CA same-day)
# llc_annual_fee / corp_annual_fee — mixed (some rows are franchise-tax
# amounts; verify per-state before trust)
# name_reservation_fee, franchise_tax_min — DOLLARS × 10000 in most rows
#
# This sizer only reads formation + foreign + expedited and
# normalizes expedited to cents.
formation_col = "corp_formation_fee" if entity_type.startswith("corp") else "llc_formation_fee"
foreign_col = "foreign_corp_fee" if entity_type.startswith("corp") else "foreign_llc_fee"
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute(
f"""
SELECT {formation_col} AS formation_cents,
{foreign_col} AS foreign_cents,
expedited_fee AS expedited_raw
FROM state_filing_fees
WHERE state_code = %s
LIMIT 1
""",
(state,),
)
fee = cur.fetchone()
if fee:
is_foreign = bool(order.get("foreign_filing") or order.get("is_foreign_entity"))
base_cents = int((fee.get("foreign_cents") or 0) if is_foreign else (fee.get("formation_cents") or 0))
expedited_cents = 0
if order.get("expedited") and fee.get("expedited_raw"):
# Divide by 100 to convert (dollars × 10000) → cents
expedited_cents = int(fee["expedited_raw"]) // 100
total = base_cents + expedited_cents
if total > 0:
obligations.append(_filing_vo(
order_id, "formation", "state_sos", state, total,
))
if order.get("include_ra_service") or order.get("include_registered_agent"):
obligations.append(_filing_vo(
order_id, "formation", "nwra", state,
STATIC_VENDOR_FEES_CENTS["nwra"],
))
commission = _compute_commission_obligation(
order_id, "formation", order, f"formation-{entity_type}", conn,
)
if commission:
obligations.append(commission)
filing = [o for o in obligations if o.obligation_kind == "filing_fee"]
comms = [o for o in obligations if o.obligation_kind == "commission"]
filing_total = sum(o.amount_usd_cents for o in filing)
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
comm_total = sum(o.amount_usd_cents for o in comms)
return SizingResult(
obligations=obligations,
filing_total_cents=filing_total,
filing_total_with_buffer_cents=filing_buf,
commission_total_cents=comm_total,
needed_usd_cents=filing_buf + comm_total,
)
# ── Canada CRTC orders ──────────────────────────────────────────────────
def _size_canada_crtc(order_id: str, conn) -> SizingResult:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute("SELECT * FROM canada_crtc_orders WHERE order_number = %s", (order_id,))
order = cur.fetchone()
if not order:
return SizingResult([], 0, 0, 0, 0, [f"canada_crtc order {order_id} not found"])
obligations: list[VendorObligation] = []
obligations.append(_filing_vo(
order_id, "canada_crtc", "bc_registry", "BC",
STATIC_VENDOR_FEES_CENTS["bc_registry"],
))
if order.get("amb_annual_price_cents"):
obligations.append(_filing_vo(
order_id, "canada_crtc", "amb_mailbox", None,
int(order["amb_annual_price_cents"]),
))
commission = _compute_commission_obligation(
order_id, "canada_crtc", order, "canada_crtc", conn,
)
if commission:
obligations.append(commission)
filing = [o for o in obligations if o.obligation_kind == "filing_fee"]
comms = [o for o in obligations if o.obligation_kind == "commission"]
filing_total = sum(o.amount_usd_cents for o in filing)
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
comm_total = sum(o.amount_usd_cents for o in comms)
return SizingResult(
obligations=obligations,
filing_total_cents=filing_total,
filing_total_with_buffer_cents=filing_buf,
commission_total_cents=comm_total,
needed_usd_cents=filing_buf + comm_total,
)
# ── Bundle orders (sum of sub-orders) ───────────────────────────────────
def _size_bundle(order_id: str, conn) -> SizingResult:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute("SELECT * FROM bundle_orders WHERE order_number = %s", (order_id,))
order = cur.fetchone()
if not order:
return SizingResult([], 0, 0, 0, 0, [f"bundle order {order_id} not found"])
# bundle_orders.child_order_numbers is a TEXT[] of the sub-order numbers
# (verify schema during implementation — may be JSONB with types).
sub_ids = order.get("child_order_numbers") or []
sub_types = order.get("child_order_types") or []
all_obligations: list[VendorObligation] = []
for sub_id, sub_type in zip(sub_ids, sub_types):
sub_res = compute_obligations(order_id=sub_id, order_type=sub_type, conn=conn)
all_obligations.extend(sub_res.obligations)
# Bundle-level commission (override the per-sub commissions if present)
commission = _compute_commission_obligation(
order_id, "bundle", order, "bundle", conn,
)
if commission:
# Drop sub-level commissions; keep only the bundle-level one
all_obligations = [o for o in all_obligations if o.obligation_kind != "commission"]
all_obligations.append(commission)
filing = [o for o in all_obligations if o.obligation_kind == "filing_fee"]
comms = [o for o in all_obligations if o.obligation_kind == "commission"]
filing_total = sum(o.amount_usd_cents for o in filing)
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
comm_total = sum(o.amount_usd_cents for o in comms)
return SizingResult(
obligations=all_obligations,
filing_total_cents=filing_total,
filing_total_with_buffer_cents=filing_buf,
commission_total_cents=comm_total,
needed_usd_cents=filing_buf + comm_total,
)
# ── Commission obligation (shared across all order types) ───────────────
def _compute_commission_obligation(
order_id: str, order_type: str, order: dict, slug_for_override: str, conn,
) -> Optional[VendorObligation]:
"""If the order is attributed to a sales agent, reserve their commission."""
agent_id = order.get("sales_agent_id") or order.get("referral_agent_id")
if not agent_id:
return None
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute(
"""
SELECT id, commission_type, commission_default_cents,
commission_pct, commission_overrides, active
FROM sales_agents
WHERE id = %s
""",
(agent_id,),
)
agent = cur.fetchone()
if not agent or not agent.get("active"):
return None
overrides = agent.get("commission_overrides") or {}
if isinstance(overrides, str):
try: overrides = json.loads(overrides)
except (TypeError, ValueError): overrides = {}
override = overrides.get(slug_for_override) or {}
ctype = override.get("type") or agent.get("commission_type", "flat")
amount_cents = 0
if ctype == "flat":
amount_cents = int(
override.get("flat_cents")
or agent.get("commission_default_cents")
or 0
)
elif ctype == "percent":
pct = float(override.get("pct") or agent.get("commission_pct") or 0)
# Use subtotal (pre-surcharge) as the basis; fall back to service_fee_cents
subtotal = int(
order.get("subtotal_cents")
or order.get("service_fee_cents")
or order.get("total_cents")
or 0
)
amount_cents = int(subtotal * pct / 100)
if amount_cents <= 0:
return None
# Due 14 days post-delivery. If we don't know delivery date, estimate
# from created_at + 7 days (typical service turnaround) + 14.
created = order.get("created_at") or datetime.utcnow()
due_by = created + timedelta(days=7 + COMMISSION_DUE_DAYS)
return VendorObligation(
order_id=order_id,
order_type=order_type,
obligation_kind="commission",
vendor="agent_commission",
vendor_detail=str(agent_id),
amount_usd_cents=amount_cents,
fx_buffer_bps=0, # commissions are exact, no buffer
due_by=due_by,
)
# ── Constructors ────────────────────────────────────────────────────────
def _filing_vo(
order_id: str, order_type: str, vendor: str,
vendor_detail: Optional[str], amount_cents: int,
) -> VendorObligation:
return VendorObligation(
order_id=order_id,
order_type=order_type,
obligation_kind="filing_fee",
vendor=vendor,
vendor_detail=vendor_detail,
amount_usd_cents=amount_cents,
fx_buffer_bps=DEFAULT_FILING_BUFFER_BPS,
)
# ── Persistence helpers ─────────────────────────────────────────────────
def persist_obligations(order_id: str, result: SizingResult, conn=None) -> None:
"""Upsert the SizingResult into vendor_obligations.
Idempotent: deletes existing pending rows for this order then re-inserts.
We never overwrite rows already marked reserved/paid/waived.
"""
close_after = False
if conn is None:
conn = _db_connect()
close_after = True
try:
with conn.cursor() as cur:
cur.execute(
"DELETE FROM vendor_obligations "
"WHERE order_id = %s AND status = 'pending'",
(order_id,),
)
for o in result.obligations:
cur.execute(
"""
INSERT INTO vendor_obligations (
order_id, order_type, obligation_kind, vendor, vendor_detail,
amount_usd_cents, fx_buffer_bps, due_by, status,
commission_ledger_id
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,'pending',%s)
""",
(
o.order_id, o.order_type, o.obligation_kind,
o.vendor, o.vendor_detail,
o.amount_usd_cents, o.fx_buffer_bps, o.due_by,
o.commission_ledger_id,
),
)
conn.commit()
finally:
if close_after:
conn.close()