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 = { llc: "LLC", corporation: "Corporation", s_corp: "S-Corp", }; const MGMT_TYPE_MAP: Record = { 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;