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>
This commit is contained in:
commit
f8cd37ac8c
1823 changed files with 145167 additions and 0 deletions
227
api/src/routes/foreign-qualification.ts
Normal file
227
api/src/routes/foreign-qualification.ts
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/**
|
||||
* Foreign Qualification API.
|
||||
*
|
||||
* Customer and admin endpoints for ordering a Certificate of Authority
|
||||
* (aka "foreign qualification") in one or more US states against an
|
||||
* already-formed entity. Used by:
|
||||
*
|
||||
* - Regular incorp clients expanding to additional states.
|
||||
* - FCC carriers that must be authorized to do business in every
|
||||
* state they serve (per state PUC rules, not the FCC directly).
|
||||
*
|
||||
* Endpoints:
|
||||
* GET /api/v1/foreign-qualification/jurisdictions
|
||||
* Public catalog of eligible target states + per-entity-type quotes.
|
||||
*
|
||||
* POST /api/v1/foreign-qualification/quote
|
||||
* Price a multi-state COA order before checkout.
|
||||
*
|
||||
* GET /api/v1/foreign-qualification/registrations
|
||||
* Admin list (requires admin auth).
|
||||
*
|
||||
* GET /api/v1/foreign-qualification/registrations/:id
|
||||
* Admin detail.
|
||||
*
|
||||
* Order creation flows through the regular compliance_orders checkout
|
||||
* pipeline (/api/v1/compliance-orders) with service_slug set to
|
||||
* `foreign-qualification-single` or `foreign-qualification-multi`; the
|
||||
* intake wizard pushes the selected target states into intake_data so
|
||||
* the worker can fan out per-state rows in
|
||||
* `foreign_qualification_registrations`.
|
||||
*/
|
||||
|
||||
import { Router } from "express";
|
||||
import type { Request, Response } from "express";
|
||||
import { pool } from "../db.js";
|
||||
import { requireAdmin } from "../middleware/admin-auth.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Flat service-fee schedule — mirrors COMPLIANCE_SERVICES in
|
||||
// compliance-orders.ts. Kept in sync manually; update both sides.
|
||||
const SERVICE_FEE_SINGLE_CENTS = 14900;
|
||||
const SERVICE_FEE_MULTI_CENTS = 9900; // per-state
|
||||
const NWRA_RA_DEFAULT_CENTS = 12500; // $125/yr registered agent
|
||||
|
||||
// ── GET /api/v1/foreign-qualification/jurisdictions ─────────────────
|
||||
//
|
||||
// List every US jurisdiction with foreign-qualification enabled,
|
||||
// including fee preview. Front-end uses this to render the state picker
|
||||
// with price per state.
|
||||
router.get(
|
||||
"/api/v1/foreign-qualification/jurisdictions",
|
||||
async (_req: Request, res: Response) => {
|
||||
const { rows } = await pool.query(
|
||||
`SELECT j.code,
|
||||
j.name,
|
||||
j.country,
|
||||
j.kind,
|
||||
j.portal_name,
|
||||
j.supports_foreign_qualification,
|
||||
f.foreign_llc_fee,
|
||||
f.foreign_corp_fee,
|
||||
f.expedited_fee,
|
||||
f.publication_required,
|
||||
f.typical_processing_days
|
||||
FROM jurisdictions j
|
||||
LEFT JOIN state_filing_fees f ON f.state_code = j.code
|
||||
WHERE j.country = 'US'
|
||||
AND j.supports_foreign_qualification = TRUE
|
||||
ORDER BY j.code`,
|
||||
);
|
||||
res.json({ jurisdictions: rows });
|
||||
},
|
||||
);
|
||||
|
||||
// ── POST /api/v1/foreign-qualification/quote ─────────────────────────
|
||||
//
|
||||
// Body: {
|
||||
// home_state_code: "WY",
|
||||
// entity_type: "llc",
|
||||
// target_states: ["CA", "TX", "NY"],
|
||||
// include_ra_each: true,
|
||||
// expedited: false,
|
||||
// }
|
||||
//
|
||||
// Returns per-state breakdown + grand total.
|
||||
router.post(
|
||||
"/api/v1/foreign-qualification/quote",
|
||||
async (req: Request, res: Response) => {
|
||||
const {
|
||||
home_state_code,
|
||||
entity_type,
|
||||
target_states,
|
||||
include_ra_each = true,
|
||||
expedited = false,
|
||||
} = req.body ?? {};
|
||||
|
||||
if (!home_state_code || !entity_type
|
||||
|| !Array.isArray(target_states) || target_states.length === 0) {
|
||||
res.status(400).json({
|
||||
error: "home_state_code, entity_type, and non-empty target_states required",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const et = String(entity_type).toLowerCase();
|
||||
const feeCol =
|
||||
et === "llc" || et === "pllc" ? "foreign_llc_fee"
|
||||
: et === "corporation" || et === "c_corp"
|
||||
|| et === "s_corp" || et === "pc"
|
||||
|| et === "nonprofit" ? "foreign_corp_fee"
|
||||
: null;
|
||||
if (!feeCol) {
|
||||
res.status(400).json({ error: `unsupported entity_type: ${entity_type}` });
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull fees + sanity-check the target state is eligible.
|
||||
const { rows } = await pool.query(
|
||||
`SELECT j.code,
|
||||
j.name,
|
||||
j.supports_foreign_qualification,
|
||||
f.${feeCol} AS state_fee_cents,
|
||||
f.expedited_fee AS expedited_raw,
|
||||
f.publication_required
|
||||
FROM jurisdictions j
|
||||
LEFT JOIN state_filing_fees f ON f.state_code = j.code
|
||||
WHERE j.code = ANY($1::varchar[])`,
|
||||
[target_states.map((s: string) => String(s).toUpperCase())],
|
||||
);
|
||||
|
||||
const perStateServiceFee =
|
||||
target_states.length === 1 ? SERVICE_FEE_SINGLE_CENTS : SERVICE_FEE_MULTI_CENTS;
|
||||
|
||||
let grand = 0;
|
||||
const items = rows.map((r) => {
|
||||
if (!r.supports_foreign_qualification) {
|
||||
return {
|
||||
state_code: r.code,
|
||||
error: "not_supported",
|
||||
};
|
||||
}
|
||||
const stateFee = Number(r.state_fee_cents || 0);
|
||||
// state_filing_fees.expedited_fee is seeded inconsistently — the
|
||||
// same normalization we do in the Python sizer (dollars×10000 →
|
||||
// cents). See scripts/workers/crypto_offramp/sizer.py.
|
||||
const expRaw = Number(r.expedited_raw || 0);
|
||||
const expFee = expedited ? (expRaw > 50000 ? Math.floor(expRaw / 100) : expRaw) : 0;
|
||||
const raFee = include_ra_each ? NWRA_RA_DEFAULT_CENTS : 0;
|
||||
const publication = r.publication_required ? true : false;
|
||||
const total = stateFee + expFee + raFee + perStateServiceFee;
|
||||
grand += total;
|
||||
return {
|
||||
state_code: r.code,
|
||||
state_name: r.name,
|
||||
state_fee_cents: stateFee,
|
||||
expedited_fee_cents: expFee,
|
||||
nwra_ra_fee_cents: raFee,
|
||||
service_fee_cents: perStateServiceFee,
|
||||
total_cents: total,
|
||||
publication_required: publication,
|
||||
};
|
||||
});
|
||||
|
||||
res.json({
|
||||
home_state_code,
|
||||
entity_type: et,
|
||||
include_ra_each,
|
||||
expedited,
|
||||
per_state_service_fee_cents: perStateServiceFee,
|
||||
items,
|
||||
grand_total_cents: grand,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// ── GET /api/v1/foreign-qualification/registrations ──────────────────
|
||||
router.get(
|
||||
"/api/v1/foreign-qualification/registrations",
|
||||
requireAdmin,
|
||||
async (req: Request, res: Response) => {
|
||||
const status = (req.query.status as string) || "";
|
||||
const targetState = (req.query.target_state as string) || "";
|
||||
const limit = Math.min(Number(req.query.limit) || 100, 500);
|
||||
|
||||
const conditions: string[] = [];
|
||||
const params: (number | string)[] = [];
|
||||
if (status) {
|
||||
conditions.push(`status = $${params.length + 1}`);
|
||||
params.push(status);
|
||||
}
|
||||
if (targetState) {
|
||||
conditions.push(`target_state_code = $${params.length + 1}`);
|
||||
params.push(targetState.toUpperCase());
|
||||
}
|
||||
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
params.push(limit);
|
||||
|
||||
const { rows } = await pool.query(
|
||||
`SELECT * FROM v_foreign_qualifications_pipeline
|
||||
${where}
|
||||
LIMIT $${params.length}`,
|
||||
params,
|
||||
);
|
||||
res.json({ registrations: rows });
|
||||
},
|
||||
);
|
||||
|
||||
// ── GET /api/v1/foreign-qualification/registrations/:id ──────────────
|
||||
router.get(
|
||||
"/api/v1/foreign-qualification/registrations/:id",
|
||||
requireAdmin,
|
||||
async (req: Request, res: Response) => {
|
||||
const id = Number(req.params.id);
|
||||
if (!Number.isFinite(id)) {
|
||||
res.status(400).json({ error: "bad id" }); return;
|
||||
}
|
||||
const { rows } = await pool.query(
|
||||
`SELECT * FROM foreign_qualification_registrations WHERE id = $1`,
|
||||
[id],
|
||||
);
|
||||
if (!rows.length) { res.status(404).json({ error: "not found" }); return; }
|
||||
res.json({ registration: rows[0] });
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
Loading…
Add table
Add a link
Reference in a new issue