/** * Exit survey + referral endpoints. * * POST /api/v1/survey — Submit exit survey response * GET /api/v1/survey/:order — Get survey form data (pre-fill) * POST /api/v1/referral/check — Validate a referral code * GET /api/v1/referral/:email — Get referral code + stats for a customer */ import { Router, type Request, type Response } from "express"; import { pool } from "../db.js"; const router = Router(); const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || ""; const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID || ""; // POST /api/v1/survey — Submit exit survey router.post("/api/v1/survey", async (req: Request, res: Response) => { try { const { order_number, rating, feedback, would_recommend } = req.body ?? {}; if (!order_number || !rating) { res.status(400).json({ error: "order_number and rating required." }); return; } // Get customer email from order const orderResult = await pool.query( "SELECT customer_email, customer_name, service_name FROM compliance_orders WHERE order_number = $1", [order_number], ); const order = orderResult.rows[0] as Record | undefined; const email = (order?.customer_email as string) || ""; await pool.query( `INSERT INTO exit_surveys (order_number, customer_email, rating, feedback, would_recommend) VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING`, [order_number, email, rating, feedback || null, would_recommend ?? null], ); // Telegram alert for low ratings if (rating <= 3 && TELEGRAM_BOT_TOKEN && TELEGRAM_CHAT_ID) { const text = `⚠️ Low survey rating\nOrder: ${order_number}\nRating: ${"⭐".repeat(rating)}\nCustomer: ${email}\nFeedback: ${feedback || "(none)"}`; fetch(`https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ chat_id: TELEGRAM_CHAT_ID, text }), }).catch(() => {}); } // If high rating, return Google review link const showReview = rating >= 4; const googleReviewUrl = process.env.GOOGLE_REVIEW_URL || ""; res.json({ success: true, show_review: showReview, google_review_url: googleReviewUrl || null, referral_code: order ? await getOrCreateReferralCode(email, (order.customer_name as string) || "") : null, }); } catch (err) { console.error("[survey] Error:", err); res.status(500).json({ error: "Survey submission failed." }); } }); // POST /api/v1/referral/check — Validate a referral code router.post("/api/v1/referral/check", async (req: Request, res: Response) => { try { const { code } = req.body ?? {}; if (!code) { res.status(400).json({ valid: false, error: "code required." }); return; } const result = await pool.query( "SELECT code, customer_name FROM referral_codes WHERE code = $1", [code.toUpperCase()], ); if (result.rows.length === 0) { res.json({ valid: false }); return; } res.json({ valid: true, referrer_name: (result.rows[0] as Record).customer_name, }); } catch { res.status(500).json({ valid: false, error: "Check failed." }); } }); // GET /api/v1/referral/:email — Get referral info for a customer router.get("/api/v1/referral/:email", async (req: Request, res: Response) => { try { const email = req.params.email; const result = await pool.query( "SELECT code, times_used, total_earned_cents, balance_cents FROM referral_codes WHERE customer_email = $1", [email], ); if (result.rows.length === 0) { res.json({ has_code: false }); return; } const r = result.rows[0] as Record; res.json({ has_code: true, code: r.code, times_used: r.times_used, total_earned: ((r.total_earned_cents as number) / 100).toFixed(2), balance: ((r.balance_cents as number) / 100).toFixed(2), }); } catch { res.status(500).json({ error: "Failed." }); } }); async function getOrCreateReferralCode(email: string, name: string): Promise { // Check if already exists const existing = await pool.query( "SELECT code FROM referral_codes WHERE customer_email = $1", [email], ); if (existing.rows.length > 0) { return (existing.rows[0] as Record).code as string; } // Generate code from name const cleanName = name.replace(/[^a-zA-Z]/g, "").toUpperCase().substring(0, 12); let code = `REF-${cleanName || "CUSTOMER"}`; // Ensure unique const check = await pool.query("SELECT 1 FROM referral_codes WHERE code = $1", [code]); if (check.rows.length > 0) { code = `${code}${Math.floor(Math.random() * 99)}`; } await pool.query( `INSERT INTO referral_codes (code, customer_email, customer_name) VALUES ($1, $2, $3) ON CONFLICT (customer_email) DO NOTHING`, [code, email, name], ); return code; } export default router;