Skip to main content

Overview

PixelTech uses Firebase Cloud Functions with Nodemailer to send transactional emails. Two main triggers send emails when orders are created and when shipping details are added.

Configuration

SMTP transport is configured using environment variables. From emails.js:5-13:
const transporter = nodemailer.createTransport({
    host: process.env.SMTP_HOST,
    port: parseInt(process.env.SMTP_PORT) || 465,
    secure: true,
    auth: {
        user: process.env.SMTP_EMAIL,
        pass: process.env.SMTP_PASSWORD
    }
});

Environment Variables

SMTP_HOST
string
required
SMTP server hostname (e.g., smtp.gmail.com)
SMTP_PORT
number
default:"465"
SMTP port (465 for SSL, 587 for TLS)
SMTP_EMAIL
string
required
Sender email address
SMTP_PASSWORD
string
required
SMTP authentication password or app-specific password

Email Functions

sendOrderConfirmation

Sends order confirmation email when a new order is created. Location: emails.js:222-252 Trigger: Firestore document created in orders/{orderId} Function Signature:
exports.sendOrderConfirmation = onDocumentCreated("orders/{orderId}", async (event) => {
    const orderData = event.data.data();
    const orderId = event.params.orderId;
    // ...
});
Email Details:
  • From: "PixelTech Pedidos" <[email protected]>
  • Subject: ¡Recibimos tu pedido! #ABC12345 🎉
  • Template: Order summary with items, pricing, and customer info
Implementation:
const email = orderData.buyerInfo?.email || orderData.extraData?.billingData?.email;

if (!email) {
    console.log(`Orden ${orderId} sin email. Omisión.`);
    return;
}

const htmlContent = getBeautifulEmailTemplate('CONFIRMATION', orderData, orderId);

const mailOptions = {
    from: `"PixelTech Pedidos" <${process.env.SMTP_EMAIL}>`,
    to: email,
    subject: `¡Recibimos tu pedido! #${orderId.slice(0,8).toUpperCase()} 🎉`,
    html: htmlContent
};

await transporter.sendMail(mailOptions);
await event.data.ref.update({ confirmationEmailSent: true });

sendDispatchNotification

Sends shipping notification when order status changes to dispatched or tracking number is added. Location: emails.js:255-288 Trigger: Firestore document updated in orders/{orderId} Function Signature:
exports.sendDispatchNotification = onDocumentUpdated("orders/{orderId}", async (event) => {
    const newData = event.data.after.data();
    const previousData = event.data.before.data();
    // ...
});
Trigger Conditions:
const isDispatched = (
    newData.status === 'dispatched' || 
    newData.status === 'enviado' || 
    newData.status === 'DESPACHADO' || 
    newData.status === 'EN_RUTA'
) && previousData.status !== newData.status;

const trackingAdded = newData.trackingNumber && !previousData.trackingNumber;

if (isDispatched || trackingAdded) {
    // Send dispatch email
}
Email Details:
  • From: "PixelTech Envíos" <[email protected]>
  • Subject: ¡Tu pedido va en camino! 🚚 #ABC12345
  • Template: Includes tracking number and carrier information

Email Templates

Template System

The getBeautifulEmailTemplate() function generates responsive HTML emails. From emails.js:36-219:
function getBeautifulEmailTemplate(type, order, orderId) {
    const isDispatch = type === 'DISPATCH';
    const primaryColor = "#00D6D6"; // Brand Cyan
    const darkColor = "#1e293b";    // Slate-900
    
    // Generate items list
    const itemsHtml = (order.items || []).map(item => `
        <tr>
            <td>
                <img src="${item.mainImage}" style="width: 60px; height: 60px;">
                <p>${item.name}</p>
                <p>${item.color} | Cant: ${item.quantity}</p>
            </td>
            <td>${formatMoney(item.price * item.quantity)}</td>
        </tr>
    `).join('');
    
    return `<!DOCTYPE html>...</html>`;
}

Template Features

Responsive Design

Mobile-optimized layout with proper viewport meta tags

Brand Consistency

Uses PixelTech brand colors (#00D6D6 cyan, #1e293b dark)

Order Summary

Product images, names, quantities, and pricing breakdown

Tracking Links

Direct links to carrier tracking pages when available
From emails.js:25-33:
const getTrackingLink = (carrier, guide) => {
    if (!carrier || !guide) return "#";
    const c = carrier.toLowerCase();
    
    if (c.includes('servientrega')) 
        return "https://www.servientrega.com/wps/portal/rastreo-envio";
    if (c.includes('interrapidisimo')) 
        return "https://interrapidisimo.com/sigue-tu-envio/";
    if (c.includes('coordinadora')) 
        return "https://coordinadora.com/rastreo/rastreo-de-guia/";
    
    return `https://www.google.com/search?q=${carrier}+rastreo`;
};
Emails include a pre-filled WhatsApp link for customer support. From emails.js:73-79:
const customerName = order.buyerInfo?.name || 'Cliente';
const cleanOrderId = orderId.slice(0, 8).toUpperCase();
const waMessage = `Hola PixelTech, tengo una duda sobre mi pedido #${cleanOrderId} a nombre de ${customerName}.`;
const waLink = `https://wa.me/573229243907?text=${encodeURIComponent(waMessage)}`;
Rendered in footer:
<p>
    ¿Tienes dudas? 
    <a href="{waLink}" style="color: #00D6D6; font-weight: bold;">
        Contáctanos por WhatsApp
    </a>
</p>

Email Template Structure

Confirmation Email

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700;900&display=swap');
    </style>
</head>
<body style="font-family: 'Inter', sans-serif; background-color: #f8fafc;">
    <table width="100%">
        <tr>
            <td align="center">
                <!-- Header: Dark background with logo and title -->
                <td style="background-color: #1e293b; padding: 40px;">
                    <img src="https://pixeltechcol.firebaseapp.com/img/logo.png" height="40px">
                    <h1 style="color: #ffffff;">¡Gracias por tu compra! 🎉</h1>
                    <p style="color: #94a3b8;">Orden #ABC12345</p>
                </td>
                
                <!-- Body: Order summary -->
                <td style="padding: 40px;">
                    <p>Hola <strong>Juan Pérez</strong>,</p>
                    <p>Hemos recibido tu pedido correctamente...</p>
                    
                    <!-- Items table -->
                    <table width="100%">
                        <tr>
                            <td>iPhone 14 Pro - Negro - 256GB</td>
                            <td>$4,500,000</td>
                        </tr>
                    </table>
                    
                    <!-- Totals -->
                    <table>
                        <tr>
                            <td>Envío</td>
                            <td>$15,000</td>
                        </tr>
                        <tr>
                            <td>Total Pagado</td>
                            <td>$4,515,000</td>
                        </tr>
                    </table>
                    
                    <!-- Address box -->
                    <div style="background-color: #f8fafc; padding: 20px;">
                        <p>Dirección de Envío</p>
                        <p>Calle 123 #45-67</p>
                        <p>Bogotá</p>
                    </div>
                    
                    <!-- CTA Button -->
                    <a href="https://pixeltechcol.com/shop/order-detail.html?id={orderId}"
                       style="background-color: #1e293b; color: #fff; padding: 15px 30px;">
                        Ver Detalles en la Web
                    </a>
                </td>
                
                <!-- Footer -->
                <td style="background-color: #f1f5f9; padding: 20px;">
                    <p>&copy; 2026 PixelTech Colombia.</p>
                    <p>¿Tienes dudas? <a href="{waLink}">Contáctanos</a></p>
                </td>
            </td>
        </tr>
    </table>
</body>
</html>

Dispatch Email

Same structure as confirmation, but includes:
<!-- Tracking box (if trackingNumber exists) -->
<div style="background-color: #ecfeff; border: 1px solid #cfFAFE; padding: 20px;">
    <p>Número de Guía (Servientrega)</p>
    <p style="font-size: 24px; font-family: monospace;">1234567890</p>
    <a href="https://servientrega.com/rastreo" 
       style="background-color: #00D6D6; color: #000; padding: 10px 20px;">
        Rastrear Pedido
    </a>
</div>

Helper Functions

Format Currency

From emails.js:16-22:
const formatMoney = (amount) => {
    return new Intl.NumberFormat('es-CO', { 
        style: 'currency', 
        currency: 'COP', 
        minimumFractionDigits: 0 
    }).format(amount || 0);
};
Output: $4.515.000 (Colombian format)

Email Tracking

Orders are updated with email status flags:
// After sending confirmation
await event.data.ref.update({ confirmationEmailSent: true });

// After sending dispatch notification
await event.data.after.ref.update({ dispatchEmailSent: true });

Error Handling

Email failures are logged but don’t block order processing:
try {
    await transporter.sendMail(mailOptions);
    console.log(`Email CONFIRMACION enviado a ${email}`);
} catch (error) {
    console.error('Error enviando email:', error);
    return; // Don't throw - order is still valid
}

Testing Emails

Local Testing with Gmail

  1. Enable 2-factor authentication on Gmail account
  2. Generate an app-specific password
  3. Set environment variables:
SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
SMTP_EMAIL=[email protected]
SMTP_PASSWORD=your-app-password

Test Email Trigger

Create a test order in Firestore to trigger confirmation email:
await db.collection('orders').add({
    buyerInfo: { email: '[email protected]', name: 'Test User' },
    items: [{ name: 'Test Product', price: 100000, quantity: 1 }],
    total: 100000,
    shippingData: { address: 'Test Address', city: 'Bogotá' }
});

Best Practices

1

Always Check for Email

Validate email exists before attempting to send
2

Use Transactional Email Service

For production, use SendGrid, Mailgun, or AWS SES for better deliverability
3

Include Unsubscribe Link

For marketing emails (not required for transactional emails)
4

Test on Multiple Clients

Preview emails in Gmail, Outlook, Apple Mail, and mobile devices
5

Monitor Bounce Rates

Track failed deliveries and update customer email addresses

Email Deliverability

SPF Record

Add SPF record to DNS:
v=spf1 include:_spf.google.com ~all

DKIM Signing

Configure DKIM in your SMTP provider for authentication.

DMARC Policy

Set DMARC policy:
v=DMARC1; p=none; rua=mailto:[email protected]

Build docs developers (and LLMs) love