/** * Service bundle routes. * * Bundles give 20% off when purchasing all services in a category * or a curated cross-category package. */ import { Router } from "express"; import { pool } from "../db.js"; import { submitLimiter } from "../middleware/rate-limit.js"; import { v4 as uuidv4 } from "uuid"; const router = Router(); // Service prices in cents (must match the current site pricing) // discountable: true = our service fee (eligible for bundle discount) // false = pass-through fee (state fees — NOT discountable) const SERVICE_PRICES: Record = { "flsa-audit": { price: 149900, name: "FLSA / Wage & Hour Audit", quote: false, discountable: true }, "contractor-classification": { price: 49900, name: "Contractor Classification Review", quote: false, discountable: true }, "handbook-review": { price: 99900, name: "Employee Handbook Review", quote: false, discountable: true }, "policy-development": { price: 0, name: "Workplace Policy Development", quote: true, discountable: true }, "ccpa-audit": { price: 249900, name: "CCPA/CPRA Compliance Audit", quote: false, discountable: true }, "privacy-policy": { price: 49900, name: "Privacy Policy Generation", quote: false, discountable: true }, "data-mapping": { price: 0, name: "Data Mapping & Inventory", quote: true, discountable: true }, "breach-response": { price: 199900, name: "Breach Response Planning", quote: false, discountable: true }, "consent-audit": { price: 129900, name: "SMS/Call Consent Audit", quote: false, discountable: true }, "dnc-compliance": { price: 79900, name: "DNC Compliance Review", quote: false, discountable: true }, "campaign-review": { price: 59900, name: "Campaign Compliance Review", quote: false, discountable: true }, "fcc-499a": { price: 79900, name: "FCC 499A Filing", quote: false, discountable: true }, "stir-shaken": { price: 0, name: "STIR/SHAKEN Implementation", quote: true, discountable: true }, "ipes-isp": { price: 129900, name: "FCC Carrier / ISP Registration", quote: false, discountable: true }, "database-management": { price: 49900, name: "Telecom Database Management", quote: false, discountable: true }, "state-puc": { price: 39900, name: "State PUC/PSC Filings", quote: false, discountable: true }, "formation": { price: 17900, name: "Business Formation (Basic)", quote: false, discountable: true }, "state-registration": { price: 24900, name: "State Registration", quote: false, discountable: true }, "annual-reports": { price: 9900, name: "Annual Report Filing", quote: false, discountable: true }, "registered-agent": { price: 9900, name: "Registered Agent Service", quote: false, discountable: false }, }; // These are NEVER discountable — pass-through costs and third-party fees // State filing fees: calculated separately per state, added at order time function calculateBundlePrice(services: string[], discountPct: number) { let discountableTotal = 0; // our service fees — eligible for bundle discount let nonDiscountableTotal = 0; // pass-through fees — never discounted let hasQuoteItems = false; const items: Array<{ slug: string; name: string; price: number; quote: boolean; discountable: boolean }> = []; for (const slug of services) { const svc = SERVICE_PRICES[slug]; if (!svc) continue; items.push({ slug, name: svc.name, price: svc.price, quote: svc.quote, discountable: svc.discountable }); if (svc.quote) { hasQuoteItems = true; } else if (svc.discountable) { discountableTotal += svc.price; } else { nonDiscountableTotal += svc.price; } } const originalTotal = discountableTotal + nonDiscountableTotal; const discountCents = Math.round((discountableTotal * discountPct) / 100); const finalTotal = originalTotal - discountCents; return { items, originalTotal, discountableTotal, nonDiscountableTotal, discountPct, discountCents, finalTotal, hasQuoteItems }; } // GET /api/v1/bundles — List all active bundles with calculated pricing router.get("/api/v1/bundles", async (_req, res) => { try { const result = await pool.query( "SELECT * FROM service_bundles WHERE active = TRUE ORDER BY display_order, name", ); const bundles = result.rows.map((b: any) => { const calc = calculateBundlePrice(b.services, b.discount_pct); return { slug: b.slug, name: b.name, description: b.description, category: b.category, discount_pct: b.discount_pct, services: calc.items, original_total_cents: calc.originalTotal, discount_cents: calc.discountCents, final_total_cents: calc.finalTotal, has_quote_items: calc.hasQuoteItems, savings_text: `Save $${(calc.discountCents / 100).toFixed(0)} (${b.discount_pct}% off)`, }; }); res.json({ bundles }); } catch (err) { console.error("[bundles] List error:", err); res.status(500).json({ error: "Could not load bundles." }); } }); // GET /api/v1/bundles/:slug — Single bundle with pricing router.get("/api/v1/bundles/:slug", async (req, res) => { try { const result = await pool.query( "SELECT * FROM service_bundles WHERE slug = $1 AND active = TRUE", [req.params.slug], ); if (result.rows.length === 0) { res.status(404).json({ error: "Bundle not found." }); return; } const b = result.rows[0]; const calc = calculateBundlePrice(b.services, b.discount_pct); res.json({ bundle: { slug: b.slug, name: b.name, description: b.description, category: b.category, discount_pct: b.discount_pct, services: calc.items, original_total_cents: calc.originalTotal, discount_cents: calc.discountCents, final_total_cents: calc.finalTotal, has_quote_items: calc.hasQuoteItems, }, }); } catch (err) { console.error("[bundles] Get error:", err); res.status(500).json({ error: "Could not load bundle." }); } }); // POST /api/v1/bundles/order — Place a bundle order router.post("/api/v1/bundles/order", submitLimiter, async (req, res) => { try { const { bundle_slug, customer_name, customer_email, customer_phone, customer_company, discount_code, } = req.body ?? {}; if (!bundle_slug || !customer_name || !customer_email) { res.status(400).json({ error: "Missing required fields: bundle_slug, customer_name, customer_email" }); return; } // Get the bundle const bundleResult = await pool.query( "SELECT * FROM service_bundles WHERE slug = $1 AND active = TRUE", [bundle_slug], ); if (bundleResult.rows.length === 0) { res.status(404).json({ error: "Bundle not found." }); return; } const bundle = bundleResult.rows[0]; const calc = calculateBundlePrice(bundle.services, bundle.discount_pct); // Discount code — only applies to discountable service fees (NOT state fees) let discountCodeCents = 0; if (discount_code) { const dcResult = await pool.query("SELECT * FROM discount_codes WHERE code = $1 AND active = TRUE", [discount_code.toUpperCase()]); if (dcResult.rows.length > 0) { const dc = dcResult.rows[0]; // Apply discount code ONLY to the already-discounted service fee total (after bundle discount) const discountableAfterBundle = calc.discountableTotal - calc.discountCents; if (dc.discount_type === "percent") { discountCodeCents = Math.round((discountableAfterBundle * dc.discount_value) / 100); } else { discountCodeCents = Math.min(dc.discount_value, discountableAfterBundle); } } } // Grand total: discounted service fees + non-discountable fees const grandTotal = calc.finalTotal - discountCodeCents; const year = new Date().getFullYear(); const short = uuidv4().split("-")[0]!.toUpperCase(); const orderNumber = `BDL-${year}-${short}`; const result = await pool.query( `INSERT INTO bundle_orders ( bundle_slug, order_number, customer_name, customer_email, customer_phone, customer_company, original_total_cents, discount_pct, discount_cents, final_total_cents, discount_code, discount_code_cents, status ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,'received') RETURNING id, order_number`, [ bundle_slug, orderNumber, customer_name, customer_email.toLowerCase().trim(), customer_phone || null, customer_company || null, calc.originalTotal, bundle.discount_pct, calc.discountCents, grandTotal, discount_code ? discount_code.toUpperCase() : null, discountCodeCents, ], ); // 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: "bundle", orderId: result.rows[0].id, orderNumber: result.rows[0].order_number, serviceSlug: bundle_slug, customerName: customer_name, customerEmail: customer_email, orderAmountCents: grandTotal, discountCents: discountCodeCents, }); } } catch (commErr) { console.error("[bundles] Commission creation failed (non-blocking):", commErr); } } res.status(201).json({ success: true, order_number: result.rows[0].order_number, bundle: bundle.name, original_total: `$${(calc.originalTotal / 100).toFixed(2)}`, bundle_discount: `- $${(calc.discountCents / 100).toFixed(2)} (${bundle.discount_pct}% bundle discount)`, code_discount: discountCodeCents > 0 ? `- $${(discountCodeCents / 100).toFixed(2)}` : null, total: `$${(grandTotal / 100).toFixed(2)}`, message: "Bundle order received. We will begin processing within one business day.", }); } catch (err) { console.error("[bundles] Order error:", err); res.status(500).json({ error: "Could not place bundle order." }); } }); export default router;