/** * Welcome email sender — extracted from the welcome-sequence cron handler * to keep it out of the `"use server"` action surface. Cron handlers * already validate `Authorization: Bearer ${CRON_SECRET}`, so the * sender itself does not need to re-check; running this code outside * a server action also means a malicious caller can't directly hit * `sendWelcomeEmail` via the Next.js Server Actions endpoint. */ type WelcomeEmailContent = { subject: string; heading: string; body: string; cta_text: string; cta_url: string; }; function welcomeEmailTemplates(): Record> { return { en: { 1: { subject: "Welcome to {brand} — here's what to expect", heading: "You're in!", body: "Thanks for subscribing to {brand}'s wholesale updates. Here's what you can expect:\n\n• New product announcements before anyone else\n• Exclusive wholesale pricing\n• Seasonal availability alerts\n• Quick reorder from your saved preferences", cta_text: "Explore wholesale catalog", cta_url: "/wholesale", }, 2: { subject: "How wholesale ordering works with {brand}", heading: "Ordering is simple", body: "Here's how our wholesale process works:\n\n1. Browse the catalog and add items to your cart\n2. Checkout and pay online, or request an invoice\n3. Choose your pickup date at checkout\n4. We'll have your order ready when you arrive\n\nNo account required to browse — sign up only when you're ready to order.", cta_text: "See current availability", cta_url: "/wholesale/portal", }, 3: { subject: "Your first order is waiting — {brand} wholesale", heading: "Ready to try us out?", body: "If you've been thinking about placing your first wholesale order with {brand}, now's a great time.\n\nOur current seasonal selection includes produce from our farm and partner growers, sourced for freshness and quality.\n\nQuestions? Reply to this email — we read every message.", cta_text: "Start my first order", cta_url: "/wholesale/register", }, 4: { subject: "You're all set — wholesale updates from {brand}", heading: "You're all set", body: "You're now fully set up to receive wholesale updates from {brand}.\n\nWe'll send you occasional emails about new products, seasonal availability, and any special offers. No spam — just the useful stuff.\n\nYou can unsubscribe at any time.", cta_text: "Browse the catalog", cta_url: "/wholesale/portal", }, }, es: { 1: { subject: "Bienvenido a {brand} — esto es lo que puedes esperar", heading: "¡Bienvenido!", body: "Gracias por suscribirte a las actualizaciones mayoristas de {brand}. Esto es lo que puedes esperar:\n\n• Anuncios de nuevos productos antes que nadie\n• Precios exclusivos al por mayor\n• Alertas de disponibilidad por temporada\n• Reorden rápido desde tus preferencias guardadas", cta_text: "Explorar catálogo mayorista", cta_url: "/wholesale", }, 2: { subject: "Cómo funciona el pedido al por mayor con {brand}", heading: "Ordenar es simple", body: "Así funciona nuestro proceso mayorista:\n\n1. Explora el catálogo y agrega artículos a tu carrito\n2. Paga en línea o solicita una factura\n3. Elige tu fecha de recogida al pagar\n4. Tendremos tu pedido listo cuando llegues\n\nNo necesitas cuenta para explorar — regístrate solo cuando estés listo para ordenar.", cta_text: "Ver disponibilidad actual", cta_url: "/wholesale/portal", }, 3: { subject: "Tu primer pedido está esperando — {brand} mayorista", heading: "¿Listo para probarnos?", body: "Si has estado pensando en hacer tu primer pedido al por mayor con {brand}, ahora es un excelente momento.\n\nNuestra selección actual de temporada incluye productos de nuestra granja y productores asociados, seleccionados por su frescura y calidad.\n\n¿Preguntas? Responde a este correo — leemos cada mensaje.", cta_text: "Comenzar mi primer pedido", cta_url: "/wholesale/register", }, 4: { subject: "Todo listo — actualizaciones mayoristas de {brand}", heading: "Todo está listo", body: "Ahora estás completamente configurado para recibir actualizaciones mayoristas de {brand}.\n\nTe enviaremos correos ocasionales sobre nuevos productos, disponibilidad por temporada y ofertas especiales. Sin spam — solo cosas útiles.\n\nPuedes darte de baja en cualquier momento.", cta_text: "Explorar el catálogo", cta_url: "/wholesale/portal", }, }, }; } function buildWelcomeEmail(params: { brandName: string; contactName: string | null; locale: string; step: number; ctaUrl: string; }): { subject: string; html: string; text: string } { const { brandName, contactName, locale, step, ctaUrl } = params; const t = welcomeEmailTemplates()[locale]?.[step] ?? welcomeEmailTemplates().en[step]; const greeting = locale === "es" ? (contactName ? `Hola ${contactName}` : `Hola`) : (contactName ? `Hi ${contactName}` : `Hi there`); const fullCtaUrl = ctaUrl.startsWith("http") ? ctaUrl : `https://route-commerce-platform.vercel.app${ctaUrl}`; const footerText = locale === "es" ? "Te suscribiste a actualizaciones mayoristas de {brand}. Cancela la suscripción en cualquier momento." : "You subscribed to wholesale updates from {brand}. Unsubscribe at any time."; const subject = t.subject.replace("{brand}", brandName); const heading = t.heading; const body = t.body.replace(/{brand}/g, brandName); const html = `

${locale === "es" ? "Bienvenido" : "Welcome"}

${heading}

${brandName}

${greeting},

${body}

${t.cta_text}

${footerText.replace("{brand}", brandName)}

`; const text = `[${brandName} - ${heading}]\n\n${greeting},\n\n${body}\n\n${t.cta_text}: ${fullCtaUrl}\n\n${footerText.replace("{brand}", brandName)}`; return { subject, html, text }; } export type WelcomeSequenceEntry = { id: string; brand_id: string; contact_id: string | null; contact_email: string; contact_name: string | null; brand_name: string | null; locale: string; sequence_step: number; last_email_sent_at: string | null; next_email_at: string | null; status: string; created_at: string; }; export type SendWelcomeEmailResult = { success: boolean; error?: string }; /** * Build and dispatch a single welcome email step. Intended to be called * from the cron handler at `/api/email-automation/welcome-sequence` * (which validates `CRON_SECRET`) — not from a server action surface. */ export async function sendWelcomeEmail( entry: WelcomeSequenceEntry, step: number, ): Promise { const { brand_name, contact_name, locale, contact_email } = entry; const t = welcomeEmailTemplates()[locale]?.[step] ?? welcomeEmailTemplates().en[step]; const ctaUrl = t.cta_url; const { subject, html, text } = buildWelcomeEmail({ brandName: brand_name ?? "Our Farm", contactName: contact_name, locale: locale ?? "en", step, ctaUrl, }); const RESEND_API_KEY = process.env.RESEND_API_KEY ?? ""; if (!RESEND_API_KEY) return { success: false, error: "Resend not configured" }; try { const res = await fetch("https://api.resend.com/emails", { method: "POST", headers: { Authorization: `Bearer ${RESEND_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: process.env.FROM_EMAIL ?? "Route Commerce ", to: [contact_email], subject, html, text, }), }); if (!res.ok) { const err = await res.text(); return { success: false, error: err }; } return { success: true }; } catch (e) { return { success: false, error: String(e) }; } }