new-site/api/src/routes/formations.ts
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

326 lines
12 KiB
TypeScript

import { Router } from "express";
import { pool } from "../db.js";
import { submitLimiter } from "../middleware/rate-limit.js";
import { v4 as uuidv4 } from "uuid";
import { createFormationOrder } from "../erpnext-client.js";
const router = Router();
// GET /api/v1/states — Return all states with fees for the order form
router.get("/api/v1/states", async (_req, res) => {
try {
const result = await pool.query(
`SELECT state_code, state_name, llc_formation_fee, corp_formation_fee,
llc_annual_fee, llc_annual_period, corp_annual_fee, corp_annual_period,
expedited_fee, expedited_label, publication_required, publication_est_cost,
franchise_tax_required, franchise_tax_min, franchise_tax_notes,
business_license_required, business_license_fee,
portal_name, online_filing_available, typical_processing_days, notes
FROM state_filing_fees ORDER BY state_name`,
);
res.json({ states: result.rows });
} catch (err) {
console.error("[formations] Error fetching states:", err);
res.status(500).json({ error: "Could not load state data." });
}
});
// POST /api/v1/formations — Create a formation order
router.post("/api/v1/formations", submitLimiter, async (req, res) => {
try {
const {
customer_name,
customer_email,
customer_phone,
customer_company,
state_code,
entity_type,
entity_name,
entity_name_alt,
management_type,
purpose,
principal_address,
principal_city,
principal_state,
principal_zip,
mailing_address,
mailing_city,
mailing_state,
mailing_zip,
members,
include_ra_service,
include_ein,
include_operating_agreement,
expedited,
state_fee_cents,
service_fee_cents,
expedited_fee_cents,
total_cents,
discount_code,
} = req.body ?? {};
// Validation
if (
!customer_name ||
!customer_email ||
!state_code ||
!entity_type ||
!entity_name
) {
res.status(400).json({
error:
"Missing required fields: name, email, state, entity type, entity name",
});
return;
}
if (!["llc", "corporation", "s_corp"].includes(entity_type)) {
res
.status(400)
.json({ error: "Entity type must be: llc, corporation, or s_corp" });
return;
}
if (
!customer_email ||
typeof customer_email !== "string" ||
!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(customer_email)
) {
res.status(400).json({ error: "Valid email address is required." });
return;
}
// --- Discount code validation ---
let discountCents = 0;
let validatedDiscountCode: string | null = null;
let discountCodeId: number | null = null;
let referralPayout = 0;
if (discount_code && typeof discount_code === "string" && discount_code.trim()) {
const dc = discount_code.toUpperCase().trim();
const dcResult = await pool.query("SELECT * FROM discount_codes WHERE code = $1", [dc]);
if (dcResult.rows.length > 0) {
const row = dcResult.rows[0];
const now = new Date();
const isActive = row.active;
const notExpired = !row.expires_at || new Date(row.expires_at) > now;
const hasStarted = new Date(row.starts_at) <= now;
const underLimit = row.max_uses === null || row.current_uses < row.max_uses;
// Check per-email limit
let emailOk = true;
if (customer_email && row.max_uses_per_email > 0) {
const eu = await pool.query(
"SELECT COUNT(*) as cnt FROM discount_usage WHERE code = $1 AND customer_email = $2",
[dc, customer_email.toLowerCase().trim()],
);
emailOk = parseInt(eu.rows[0]?.cnt || "0", 10) < row.max_uses_per_email;
}
// Check service scope
let scopeOk = true;
if (row.applies_to) {
const allowed = row.applies_to.split(",").map((s: string) => s.trim().toLowerCase());
scopeOk = allowed.includes("formation");
}
if (isActive && notExpired && hasStarted && underLimit && emailOk && scopeOk) {
validatedDiscountCode = dc;
discountCodeId = row.id;
// Discount applies ONLY to our service fee — never to state filing fees,
// expedited fees, or attorney review fees. Those are pass-through costs.
const serviceFee = service_fee_cents || 17900;
if (row.discount_type === "percent") {
discountCents = Math.round((serviceFee * row.discount_value) / 100);
} else {
discountCents = Math.min(row.discount_value, serviceFee);
}
// Referral payout based on service fee only (not state fees)
if (row.referral_pct > 0) {
referralPayout = Math.round((serviceFee * row.referral_pct) / 100);
}
}
}
// Silently ignore invalid codes — don't block the order
}
const finalServiceFee = (service_fee_cents || 17900) - discountCents;
const finalTotal = (total_cents || 0) - discountCents;
const year = new Date().getFullYear();
const short = uuidv4().split("-")[0]!.toUpperCase();
const orderNumber = `PW-${year}-${short}`;
const principalFull = principal_address
? `${principal_address}, ${principal_city}, ${principal_state} ${principal_zip}`
: null;
const mailingFull = mailing_address
? `${mailing_address}, ${mailing_city}, ${mailing_state} ${mailing_zip}`
: null;
const result = await pool.query(
`INSERT INTO formation_orders (
order_number, customer_name, customer_email, customer_phone, customer_company,
state_code, entity_type, entity_name, entity_name_alt,
management_type, principal_address, mailing_address,
members_json, include_ra_service, include_ein, include_operating_agreement,
expedited, state_fee_cents, service_fee_cents, expedited_fee_cents, total_cents,
status
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,'received')
RETURNING id, order_number`,
[
orderNumber,
customer_name.trim(),
customer_email.toLowerCase().trim(),
customer_phone || null,
customer_company || null,
state_code.toUpperCase(),
entity_type,
entity_name.trim(),
entity_name_alt || null,
management_type || "member_managed",
principalFull,
mailingFull,
JSON.stringify(members || []),
include_ra_service !== false,
include_ein || false,
include_operating_agreement || false,
expedited || false,
state_fee_cents || 0,
finalServiceFee,
expedited_fee_cents || 0,
finalTotal,
],
);
const orderId = result.rows[0].id;
// Push to ERPNext as source of truth — non-blocking, don't fail the response
const ENTITY_TYPE_MAP: Record<string, "LLC" | "Corporation" | "S-Corp"> = {
llc: "LLC",
corporation: "Corporation",
s_corp: "S-Corp",
};
const MGMT_TYPE_MAP: Record<string, "Member Managed" | "Manager Managed"> = {
member_managed: "Member Managed",
manager_managed: "Manager Managed",
};
try {
await createFormationOrder({
order_number: orderNumber,
customer: customer_name.trim(),
customer_email: customer_email.toLowerCase().trim(),
customer_phone: customer_phone || undefined,
state_code: state_code.toUpperCase(),
entity_type: ENTITY_TYPE_MAP[entity_type] || "LLC",
entity_name: entity_name.trim(),
entity_name_alt: entity_name_alt || undefined,
management_type: MGMT_TYPE_MAP[management_type || "member_managed"] || "Member Managed",
purpose: purpose || undefined,
principal_address: principalFull || undefined,
mailing_address: mailingFull || undefined,
members: members || [],
include_ra_service: include_ra_service !== false,
include_ein: include_ein || false,
include_operating_agreement: include_operating_agreement || false,
expedited: expedited || false,
state_fee_cents: state_fee_cents || 0,
service_fee_cents: finalServiceFee,
discount_code: validatedDiscountCode || undefined,
discount_cents: discountCents > 0 ? discountCents : undefined,
total_cents: finalTotal,
});
} catch (erpErr) {
console.error("[formations] ERPNext createFormationOrder failed (non-fatal):", erpErr);
}
// Record discount usage
if (validatedDiscountCode && discountCodeId) {
const ip = (req as any).clientIp || req.ip || "";
await pool.query(
`INSERT INTO discount_usage (discount_code_id, code, order_type, order_id, customer_email, discount_amount, referral_payout, ip_address)
VALUES ($1, $2, 'formation', $3, $4, $5, $6, $7)`,
[discountCodeId, validatedDiscountCode, orderId, customer_email.toLowerCase().trim(), discountCents, referralPayout, ip],
);
// Increment usage counter
await pool.query(
"UPDATE discount_codes SET current_uses = current_uses + 1, updated_at = now() WHERE id = $1",
[discountCodeId],
);
}
// Create commission if this order used an agent's referral code
if (discount_code) {
try {
const { createCommission } = await import("./agents.js");
// Check if the discount code belongs to a sales agent
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",
[discount_code.toUpperCase()],
);
if (agentCheck.rows.length > 0) {
await createCommission({
agentCode: agentCheck.rows[0].agent_code,
orderType: "formation",
orderId: result.rows[0].id,
orderNumber: result.rows[0].order_number,
serviceSlug: "formation",
customerName: customer_name,
customerEmail: customer_email,
orderAmountCents: finalTotal,
discountCents: discountCents,
});
}
} catch (commErr) {
console.error("[formations] Commission creation failed (non-blocking):", commErr);
}
}
res.status(201).json({
success: true,
order_number: result.rows[0].order_number,
message: "Formation order received. We will begin processing within one business day.",
discount_applied: discountCents > 0 ? {
code: validatedDiscountCode,
discount_cents: discountCents,
service_fee_after_discount: finalServiceFee,
} : null,
});
} catch (err) {
console.error("[formations] Error:", err);
res
.status(500)
.json({ error: "Could not place your order. Please try again." });
}
});
// GET /api/v1/formations/:orderNumber — Check order status
router.get("/api/v1/formations/:orderNumber", async (req, res) => {
try {
const { orderNumber } = req.params;
const result = await pool.query(
`SELECT order_number, state_code, entity_type, entity_name, status,
state_filing_number, filed_at, delivered_at, created_at
FROM formation_orders WHERE order_number = $1`,
[orderNumber],
);
if (result.rows.length === 0) {
res.status(404).json({ error: "Order not found" });
return;
}
res.json({ order: result.rows[0] });
} catch (err) {
console.error("[formations] Error fetching order:", err);
res
.status(500)
.json({ error: "Could not retrieve order. Please try again." });
}
});
export default router;