Frontend (order/fcc-carrier-registration): - Add a referral/discount code box on the review step that validates against /api/v1/discount/:code and shows the discount line + adjusted total. Discount applies to service fee + add-ons, never state filing fees. - Prefill + auto-apply from ?code= / ?ref= query param (referral links). Backend (fcc-carrier-registration route): - Accept discount_code, validate it, store discount_code/discount_cents, and subtract from the total. Checkout already reads discount_cents to apply the Stripe coupon. - Create a pending commission when the code belongs to an active sales agent. Commission fix (agents.createCommission): - Percent-type agents now earn commission_pct on ALL order types. Previously canada_crtc/formation/bundle used flat defaults and ignored percent agents. Agent: created sales agent Jay Kordic (The Horizon Group) with custom code REF-JAYK05 -> client gets 5% off discountable services, agent earns 15%. Idempotent setup script in scripts/create_agent_jaykordic.cjs.
332 lines
13 KiB
TypeScript
332 lines
13 KiB
TypeScript
/**
|
|
* FCC Carrier / ISP Registration — order creation & status API.
|
|
*
|
|
* POST /api/v1/fcc-carrier-registration — create order
|
|
* GET /api/v1/fcc-carrier-registration/:id — get order status
|
|
* GET /api/v1/fcc-carrier-registration/state-fees — formation fees per state
|
|
*/
|
|
|
|
import { Router, type Request, type Response } from "express";
|
|
import { pool } from "../db.js";
|
|
import { randomBytes } from "crypto";
|
|
|
|
const router = Router();
|
|
|
|
const BASE_FEE_CENTS = 129900; // $1,299
|
|
const FORMATION_MARKUP_CENTS = 2500; // $25 filing service
|
|
const STIR_SHAKEN_FEE_CENTS = 49900;
|
|
const OCN_FEE_CENTS = 265000;
|
|
const STATE_PUC_FEE_CENTS = 39900; // per state
|
|
|
|
function generateOrderNumber(): string {
|
|
const year = new Date().getFullYear();
|
|
const id = randomBytes(4).toString("hex").toUpperCase();
|
|
return `FCR-${year}-${id}`;
|
|
}
|
|
|
|
// ── GET /api/v1/fcc-carrier-registration/state-fees ─────────────────────────
|
|
|
|
router.get("/api/v1/fcc-carrier-registration/state-fees", async (_req: Request, res: Response) => {
|
|
try {
|
|
const { rows } = await pool.query(
|
|
`SELECT state_code, state_name, llc_formation_fee, corp_formation_fee,
|
|
expedited_fee, typical_processing_days
|
|
FROM state_filing_fees ORDER BY state_name`,
|
|
);
|
|
res.json({ states: rows, markup_cents: FORMATION_MARKUP_CENTS });
|
|
} catch (err) {
|
|
console.error("[fcc-carrier-reg] state-fees error:", err);
|
|
res.status(500).json({ error: "Could not load state fees" });
|
|
}
|
|
});
|
|
|
|
// ── POST /api/v1/fcc-carrier-registration ───────────────────────────────────
|
|
|
|
router.post("/api/v1/fcc-carrier-registration", async (req: Request, res: Response) => {
|
|
try {
|
|
const {
|
|
customer_name, customer_email, customer_phone,
|
|
entity_source, entity_legal_name, ein, formation_state, entity_type, frn,
|
|
contact_name, contact_email, contact_phone, contact_title,
|
|
address_street, address_city, address_state, address_zip,
|
|
service_wizard, services,
|
|
engagement_accepted,
|
|
discount_code,
|
|
} = req.body ?? {};
|
|
|
|
// Validate required fields
|
|
if (!customer_email || !customer_name) {
|
|
res.status(400).json({ error: "customer_name and customer_email are required." });
|
|
return;
|
|
}
|
|
if (!entity_source || !["existing", "new_formation"].includes(entity_source)) {
|
|
res.status(400).json({ error: "entity_source must be 'existing' or 'new_formation'." });
|
|
return;
|
|
}
|
|
if (!contact_name || !contact_email) {
|
|
res.status(400).json({ error: "contact_name and contact_email are required." });
|
|
return;
|
|
}
|
|
|
|
// Determine included services from wizard + checklist
|
|
const svcList: string[] = Array.isArray(services) ? services : [];
|
|
const wizardData = service_wizard || {};
|
|
|
|
const includeFormation = entity_source === "new_formation";
|
|
const includeRmd = true; // always included in base
|
|
const includeCpni = true;
|
|
const includeCalea = true;
|
|
const includeBdc = true;
|
|
const includeDcAgent = true;
|
|
const includeStirShaken = svcList.includes("stir_shaken");
|
|
const includeOcn = svcList.includes("ocn");
|
|
const statePucStates = svcList.includes("state_puc") ? (wizardData.puc_states || []) : [];
|
|
|
|
// Calculate pricing
|
|
let formationFeeCents = 0;
|
|
let stateFeeCents = 0;
|
|
if (includeFormation && formation_state) {
|
|
// Look up state filing fee
|
|
const feeCol = (entity_type === "corporation") ? "corp_formation_fee" : "llc_formation_fee";
|
|
try {
|
|
const feeResult = await pool.query(
|
|
`SELECT ${feeCol} AS fee FROM state_filing_fees WHERE state_code = $1`,
|
|
[formation_state.toUpperCase()],
|
|
);
|
|
if (feeResult.rows.length > 0 && feeResult.rows[0].fee) {
|
|
stateFeeCents = Number(feeResult.rows[0].fee);
|
|
}
|
|
} catch {}
|
|
formationFeeCents = FORMATION_MARKUP_CENTS;
|
|
}
|
|
|
|
let addonFeeCents = 0;
|
|
if (includeStirShaken) addonFeeCents += STIR_SHAKEN_FEE_CENTS;
|
|
if (includeOcn) addonFeeCents += OCN_FEE_CENTS;
|
|
const pucFeeCents = statePucStates.length * STATE_PUC_FEE_CENTS;
|
|
|
|
// ── Discount / referral code ──────────────────────────────────────────────
|
|
// Discounts apply to the Performance West service fee only. State filing fees
|
|
// (passed through at cost) are never discountable, matching the CRTC flow.
|
|
let discountCents = 0;
|
|
let normalizedDiscountCode: string | null = null;
|
|
if (discount_code && typeof discount_code === "string" && discount_code.trim().length >= 2) {
|
|
const code = discount_code.toUpperCase().trim();
|
|
try {
|
|
const dcResult = await pool.query(
|
|
"SELECT * FROM discount_codes WHERE code = $1",
|
|
[code],
|
|
);
|
|
if (dcResult.rows.length > 0) {
|
|
const dc = dcResult.rows[0];
|
|
const now = new Date();
|
|
const active = dc.active === true;
|
|
const notExpired = !dc.expires_at || new Date(dc.expires_at) >= now;
|
|
const started = !dc.starts_at || new Date(dc.starts_at) <= now;
|
|
const underGlobalLimit = dc.max_uses === null || dc.current_uses < dc.max_uses;
|
|
// Scope check: allow codes scoped to this service (or unscoped)
|
|
let inScope = true;
|
|
if (dc.applies_to) {
|
|
const allowed = String(dc.applies_to).split(",").map((s: string) => s.trim().toLowerCase());
|
|
inScope = allowed.includes("fcc_carrier_registration") || allowed.includes("all");
|
|
}
|
|
// Email allowlist check
|
|
let emailOk = true;
|
|
if (dc.allowed_emails && dc.allowed_emails.length > 0) {
|
|
const allowed = dc.allowed_emails.map((e: string) => e.toLowerCase());
|
|
emailOk = allowed.includes(customer_email.toLowerCase().trim());
|
|
}
|
|
if (active && notExpired && started && underGlobalLimit && inScope && emailOk) {
|
|
// Discountable base = service fee + add-ons (not state filing fees).
|
|
const discountable = BASE_FEE_CENTS + addonFeeCents;
|
|
if (dc.discount_type === "percent") {
|
|
discountCents = Math.round((discountable * dc.discount_value) / 100);
|
|
} else {
|
|
discountCents = Math.min(dc.discount_value, discountable);
|
|
}
|
|
normalizedDiscountCode = code;
|
|
}
|
|
}
|
|
} catch (dcErr) {
|
|
console.warn("[fcc-carrier-reg] Discount lookup failed (non-fatal):", dcErr);
|
|
}
|
|
}
|
|
|
|
const orderNumber = generateOrderNumber();
|
|
|
|
const result = await pool.query(
|
|
`INSERT INTO fcc_carrier_registrations (
|
|
order_number, customer_email, customer_name, customer_phone,
|
|
entity_source, entity_legal_name, entity_type, formation_state, ein, frn,
|
|
contact_name, contact_email, contact_phone, contact_title,
|
|
address_street, address_city, address_state, address_zip,
|
|
service_wizard,
|
|
include_formation, include_dc_agent, include_rmd, include_cpni,
|
|
include_calea, include_bdc, include_stir_shaken, include_ocn,
|
|
state_puc_states,
|
|
service_fee_cents, formation_fee_cents, state_fee_cents,
|
|
puc_fee_cents, addon_fee_cents,
|
|
discount_code, discount_cents,
|
|
engagement_accepted_at, engagement_accepted_ip
|
|
) VALUES (
|
|
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,
|
|
$19::jsonb,$20,$21,$22,$23,$24,$25,$26,$27,$28::text[],
|
|
$29,$30,$31,$32,$33,$34,$35,$36,$37
|
|
) RETURNING *`,
|
|
[
|
|
orderNumber,
|
|
customer_email.toLowerCase().trim(),
|
|
customer_name.trim(),
|
|
customer_phone || null,
|
|
entity_source,
|
|
entity_legal_name || null,
|
|
entity_type || null,
|
|
formation_state ? formation_state.toUpperCase() : null,
|
|
ein || null,
|
|
frn || null,
|
|
contact_name.trim(),
|
|
contact_email.toLowerCase().trim(),
|
|
contact_phone || null,
|
|
contact_title || null,
|
|
address_street || null,
|
|
address_city || null,
|
|
address_state ? address_state.toUpperCase() : null,
|
|
address_zip || null,
|
|
JSON.stringify(wizardData),
|
|
includeFormation,
|
|
includeDcAgent,
|
|
includeRmd,
|
|
includeCpni,
|
|
includeCalea,
|
|
includeBdc,
|
|
includeStirShaken,
|
|
includeOcn,
|
|
statePucStates,
|
|
BASE_FEE_CENTS,
|
|
formationFeeCents,
|
|
stateFeeCents,
|
|
pucFeeCents,
|
|
addonFeeCents,
|
|
normalizedDiscountCode,
|
|
discountCents,
|
|
engagement_accepted ? new Date().toISOString() : null,
|
|
engagement_accepted ? (req.ip || req.headers["x-forwarded-for"] || null) : null,
|
|
],
|
|
);
|
|
|
|
const order = result.rows[0];
|
|
|
|
// If formation needed, create a formation_orders row
|
|
if (includeFormation && formation_state) {
|
|
try {
|
|
const formationOrderNumber = `FO-${new Date().getFullYear()}-${randomBytes(3).toString("hex").toUpperCase()}`;
|
|
await pool.query(
|
|
`INSERT INTO formation_orders (
|
|
order_number, customer_name, customer_email, customer_phone,
|
|
state_code, entity_type, entity_name,
|
|
principal_address, principal_city, principal_state, principal_zip,
|
|
service_fee_cents, state_fee_cents, payment_status
|
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,'paid')`,
|
|
[
|
|
formationOrderNumber,
|
|
customer_name.trim(),
|
|
customer_email.toLowerCase().trim(),
|
|
customer_phone || null,
|
|
formation_state.toUpperCase(),
|
|
entity_type || "llc",
|
|
entity_legal_name || null,
|
|
address_street || null,
|
|
address_city || null,
|
|
address_state ? address_state.toUpperCase() : null,
|
|
address_zip || null,
|
|
FORMATION_MARKUP_CENTS,
|
|
stateFeeCents,
|
|
],
|
|
);
|
|
// Link formation order to carrier registration
|
|
await pool.query(
|
|
`UPDATE fcc_carrier_registrations SET formation_order_number = $1 WHERE order_number = $2`,
|
|
[formationOrderNumber, orderNumber],
|
|
);
|
|
} catch (formErr) {
|
|
console.warn("[fcc-carrier-reg] Formation order creation failed (non-fatal):", formErr);
|
|
}
|
|
}
|
|
|
|
const subtotalCents = BASE_FEE_CENTS + formationFeeCents + stateFeeCents + pucFeeCents + addonFeeCents;
|
|
const totalCents = Math.max(0, subtotalCents - discountCents);
|
|
|
|
// If this order used a sales agent's referral code, record a pending commission.
|
|
if (normalizedDiscountCode) {
|
|
try {
|
|
const agentCheck = await pool.query(
|
|
"SELECT sa.agent_code FROM sales_agents sa JOIN discount_codes dc ON sa.discount_code_id = dc.id WHERE dc.code = $1 AND sa.active = TRUE",
|
|
[normalizedDiscountCode],
|
|
);
|
|
if (agentCheck.rows.length > 0) {
|
|
const { createCommission } = await import("./agents.js");
|
|
await createCommission({
|
|
agentCode: agentCheck.rows[0].agent_code,
|
|
orderType: "fcc_carrier_registration",
|
|
orderId: order.id,
|
|
orderNumber: orderNumber,
|
|
serviceSlug: "fcc-carrier-registration",
|
|
customerName: customer_name.trim(),
|
|
customerEmail: customer_email.toLowerCase().trim(),
|
|
orderAmountCents: totalCents,
|
|
discountCents: discountCents,
|
|
});
|
|
}
|
|
} catch (commErr) {
|
|
console.warn("[fcc-carrier-reg] Commission creation failed (non-fatal):", commErr);
|
|
}
|
|
}
|
|
|
|
console.log(
|
|
`[fcc-carrier-reg] Created ${orderNumber}: ${entity_source} for ${customer_email} — $${(totalCents / 100).toFixed(2)}` +
|
|
(discountCents > 0 ? ` (discount ${normalizedDiscountCode} -$${(discountCents / 100).toFixed(2)})` : ""),
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
order_number: orderNumber,
|
|
order_id: orderNumber,
|
|
order_type: "fcc_carrier_registration",
|
|
total_cents: totalCents,
|
|
pricing: {
|
|
base: BASE_FEE_CENTS,
|
|
formation: formationFeeCents + stateFeeCents,
|
|
addons: addonFeeCents,
|
|
puc: pucFeeCents,
|
|
subtotal: subtotalCents,
|
|
discount_code: normalizedDiscountCode,
|
|
discount_cents: discountCents,
|
|
total: totalCents,
|
|
},
|
|
});
|
|
} catch (err) {
|
|
console.error("[fcc-carrier-reg] Create error:", err);
|
|
res.status(500).json({ error: "Could not create order." });
|
|
}
|
|
});
|
|
|
|
// ── GET /api/v1/fcc-carrier-registration/:id ────────────────────────────────
|
|
|
|
router.get("/api/v1/fcc-carrier-registration/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const { rows } = await pool.query(
|
|
`SELECT * FROM fcc_carrier_registrations WHERE order_number = $1`,
|
|
[req.params.id],
|
|
);
|
|
if (rows.length === 0) {
|
|
res.status(404).json({ error: "Order not found" });
|
|
return;
|
|
}
|
|
res.json(rows[0]);
|
|
} catch (err) {
|
|
console.error("[fcc-carrier-reg] Get error:", err);
|
|
res.status(500).json({ error: "Could not load order." });
|
|
}
|
|
});
|
|
|
|
export default router;
|