EmailVerify LogoEmailVerify

BigCommerce

Email checker for BigCommerce. Verify customer emails at checkout and registration.

Integre EmailVerify con BigCommerce para verificar correos electrónicos de clientes al momento del pago, reducir el fraude y mejorar la entregabilidad de confirmaciones de pedidos y correos de marketing.

Métodos de Integración

MétodoMejor ParaComplejidad
Script ManagerConfiguración rápidaBaja
Tema StencilTemas personalizadosMedia
API StorefrontTiendas headlessAlta

Método 1: Integración con Script Manager

Agregue verificación de correo electrónico en tiempo real usando BigCommerce Script Manager.

Crear Script de Verificación

  1. Vaya a StorefrontScript Manager
  2. Haga clic en Create a Script
  3. Configure:
    • Name: EmailVerify Email Verification
    • Location: Footer
    • Pages: Checkout

Código del Script

<script>
(function() {
    const EMAILVERIFY_API_KEY = 'YOUR_API_KEY';

    // Esperar a que se cargue el checkout
    function waitForCheckout() {
        const emailInput = document.querySelector('[data-test="customer-email"] input, #email');

        if (emailInput) {
            initVerification(emailInput);
        } else {
            setTimeout(waitForCheckout, 500);
        }
    }

    function initVerification(emailInput) {
        const statusEl = document.createElement('span');
        statusEl.className = 'bv-status';
        emailInput.parentNode.appendChild(statusEl);

        let verifyTimeout;
        let lastEmail = '';

        emailInput.addEventListener('blur', function() {
            const email = this.value;

            if (!email || email === lastEmail || !isValidFormat(email)) {
                return;
            }

            clearTimeout(verifyTimeout);

            verifyTimeout = setTimeout(function() {
                verifyEmail(email, statusEl);
                lastEmail = email;
            }, 500);
        });
    }

    async function verifyEmail(email, statusEl) {
        statusEl.textContent = 'Verificando...';
        statusEl.className = 'bv-status verifying';

        try {
            const response = await fetch('https://api.emailverify.ai/v1/verify', {
                method: 'POST',
                headers: {
                    'Authorization': 'Bearer ' + EMAILVERIFY_API_KEY,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ email: email })
            });

            const data = await response.json();

            if (data.status === 'valid') {
                statusEl.textContent = '✓ Correo electrónico verificado';
                statusEl.className = 'bv-status valid';
            } else if (data.status === 'invalid') {
                statusEl.textContent = '✗ Por favor ingrese un correo electrónico válido';
                statusEl.className = 'bv-status invalid';
            } else if (data.result && data.result.disposable) {
                statusEl.textContent = '✗ Por favor use un correo electrónico permanente';
                statusEl.className = 'bv-status invalid';
            } else {
                statusEl.textContent = '';
                statusEl.className = 'bv-status';
            }
        } catch (error) {
            console.error('Error de verificación:', error);
            statusEl.textContent = '';
            statusEl.className = 'bv-status';
        }
    }

    function isValidFormat(email) {
        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    }

    // Agregar estilos
    const style = document.createElement('style');
    style.textContent = `
        .bv-status {
            display: block;
            margin-top: 4px;
            font-size: 12px;
        }
        .bv-status.verifying { color: #666; }
        .bv-status.valid { color: #28a745; }
        .bv-status.invalid { color: #dc3545; }
    `;
    document.head.appendChild(style);

    // Inicializar
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', waitForCheckout);
    } else {
        waitForCheckout();
    }
})();
</script>

Método 2: Integración con Tema Stencil

Para temas Stencil personalizados, agregue la verificación directamente a los archivos de su tema.

Crear Componente del Tema

// assets/js/theme/global/email-verification.js
import utils from '@bigcommerce/stencil-utils';

export default class EmailVerification {
    constructor() {
        this.apiKey = window.emailverifyConfig?.apiKey;
        this.emailInputs = document.querySelectorAll('input[type="email"]');
        this.cache = new Map();

        if (this.apiKey && this.emailInputs.length) {
            this.init();
        }
    }

    init() {
        this.emailInputs.forEach(input => {
            this.attachVerification(input);
        });
    }

    attachVerification(input) {
        const statusEl = document.createElement('span');
        statusEl.className = 'form-field-verification';
        input.parentNode.appendChild(statusEl);

        let timeout;

        input.addEventListener('blur', () => {
            const email = input.value.trim();

            if (!email || !this.isValidFormat(email)) {
                this.clearStatus(statusEl);
                return;
            }

            clearTimeout(timeout);
            timeout = setTimeout(() => this.verify(email, statusEl, input), 500);
        });
    }

    async verify(email, statusEl, input) {
        // Verificar caché
        if (this.cache.has(email)) {
            this.displayResult(this.cache.get(email), statusEl, input);
            return;
        }

        this.showLoading(statusEl);

        try {
            const response = await fetch('https://api.emailverify.ai/v1/verify', {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${this.apiKey}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ email })
            });

            const data = await response.json();
            this.cache.set(email, data);
            this.displayResult(data, statusEl, input);

        } catch (error) {
            console.error('Error de verificación de correo electrónico:', error);
            this.clearStatus(statusEl);
        }
    }

    displayResult(data, statusEl, input) {
        input.classList.remove('is-valid', 'is-invalid');

        if (data.status === 'valid' && !data.result?.disposable) {
            statusEl.innerHTML = '<span class="icon icon--success"></span> Verificado';
            statusEl.className = 'form-field-verification is-valid';
            input.classList.add('is-valid');
        } else if (data.status === 'invalid') {
            statusEl.innerHTML = '<span class="icon icon--error"></span> Correo electrónico inválido';
            statusEl.className = 'form-field-verification is-invalid';
            input.classList.add('is-invalid');
        } else if (data.result?.disposable) {
            statusEl.innerHTML = '<span class="icon icon--warning"></span> Por favor use un correo electrónico permanente';
            statusEl.className = 'form-field-verification is-invalid';
            input.classList.add('is-invalid');
        } else {
            this.clearStatus(statusEl);
        }
    }

    showLoading(statusEl) {
        statusEl.innerHTML = '<span class="spinner"></span> Verificando...';
        statusEl.className = 'form-field-verification is-loading';
    }

    clearStatus(statusEl) {
        statusEl.innerHTML = '';
        statusEl.className = 'form-field-verification';
    }

    isValidFormat(email) {
        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    }
}

Inicializar en el Tema

// assets/js/theme/global.js
import EmailVerification from './global/email-verification';

export default class Global extends PageManager {
    onReady() {
        // ... otras inicializaciones

        new EmailVerification();
    }
}

Configuración del Tema

Agregue a config.json:

{
    "settings": {
        "emailverify_enabled": true,
        "emailverify_api_key": ""
    }
}

Configuración del Schema

Agregue a schema.json para la configuración de administrador:

[
    {
        "name": "EmailVerify",
        "settings": [
            {
                "type": "checkbox",
                "label": "Enable Email Verification",
                "id": "emailverify_enabled"
            },
            {
                "type": "text",
                "label": "API Key",
                "id": "emailverify_api_key"
            }
        ]
    }
]

Inyectar Configuración en la Plantilla

{{!-- templates/layout/base.html --}}
<script>
    window.emailverifyConfig = {
        enabled: {{settings.emailverify_enabled}},
        apiKey: '{{settings.emailverify_api_key}}'
    };
</script>

Método 3: Integración con API Storefront

Para tiendas BigCommerce headless que utilizan la API Storefront.

Servicio de Verificación

// services/email-verification.ts
interface VerificationResult {
  email: string;
  status: 'valid' | 'invalid' | 'unknown';
  score?: number;
  isDisposable: boolean;
  isRole: boolean;
}

class EmailVerificationService {
  private apiKey: string;
  private cache: Map<string, VerificationResult> = new Map();

  constructor(apiKey: string) {
    this.apiKey = apiKey;
  }

  async verify(email: string): Promise<VerificationResult> {
    // Verificar caché
    const cached = this.cache.get(email);
    if (cached) {
      return cached;
    }

    try {
      const response = await fetch('https://api.emailverify.ai/v1/verify', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.apiKey}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ email }),
      });

      const data = await response.json();

      const result: VerificationResult = {
        email,
        status: data.status,
        score: data.score,
        isDisposable: data.result?.disposable ?? false,
        isRole: data.result?.role ?? false,
      };

      this.cache.set(email, result);
      return result;

    } catch (error) {
      console.error('Error de verificación:', error);
      return {
        email,
        status: 'unknown',
        isDisposable: false,
        isRole: false,
      };
    }
  }

  isValid(result: VerificationResult): boolean {
    return result.status === 'valid' && !result.isDisposable;
  }

  getErrorMessage(result: VerificationResult): string | null {
    if (result.status === 'invalid') {
      return 'Por favor ingrese una dirección de correo electrónico válida.';
    }
    if (result.isDisposable) {
      return 'No se aceptan direcciones de correo electrónico temporales.';
    }
    return null;
  }
}

export const emailVerification = new EmailVerificationService(
  process.env.EMAILVERIFY_API_KEY!
);

Componente React de Checkout

// components/checkout/EmailInput.tsx
import { useState, useCallback } from 'react';
import { emailVerification } from '@/services/email-verification';
import { debounce } from 'lodash';

interface EmailInputProps {
  value: string;
  onChange: (value: string) => void;
  onValidationChange: (isValid: boolean) => void;
}

export function EmailInput({ value, onChange, onValidationChange }: EmailInputProps) {
  const [status, setStatus] = useState<'idle' | 'verifying' | 'valid' | 'invalid'>('idle');
  const [message, setMessage] = useState('');

  const verifyEmail = useCallback(
    debounce(async (email: string) => {
      if (!email || !email.includes('@')) {
        setStatus('idle');
        setMessage('');
        return;
      }

      setStatus('verifying');

      const result = await emailVerification.verify(email);

      if (emailVerification.isValid(result)) {
        setStatus('valid');
        setMessage('Correo electrónico verificado');
        onValidationChange(true);
      } else {
        setStatus('invalid');
        setMessage(emailVerification.getErrorMessage(result) || 'Verificación fallida');
        onValidationChange(false);
      }
    }, 500),
    [onValidationChange]
  );

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newValue = e.target.value;
    onChange(newValue);
    verifyEmail(newValue);
  };

  return (
    <div className="form-field">
      <label htmlFor="email">Dirección de Correo Electrónico</label>
      <input
        type="email"
        id="email"
        value={value}
        onChange={handleChange}
        className={`form-input ${status === 'valid' ? 'is-valid' : ''} ${status === 'invalid' ? 'is-invalid' : ''}`}
      />
      {status !== 'idle' && (
        <span className={`form-feedback ${status}`}>
          {status === 'verifying' && <Spinner />}
          {message}
        </span>
      )}
    </div>
  );
}

Ruta API de Next.js

Para verificación del lado del servidor para proteger su clave API:

// pages/api/verify-email.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Método no permitido' });
  }

  const { email } = req.body;

  if (!email) {
    return res.status(400).json({ error: 'Correo electrónico requerido' });
  }

  try {
    const response = await fetch('https://api.emailverify.ai/v1/verify', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.EMAILVERIFY_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ email }),
    });

    const data = await response.json();

    return res.status(200).json({
      valid: data.status === 'valid' && !data.result?.disposable,
      status: data.status,
      disposable: data.result?.disposable ?? false,
    });

  } catch (error) {
    console.error('Error de verificación:', error);
    return res.status(200).json({ valid: true }); // Permitir en caso de error
  }
}

Integración de Webhooks

Procese pedidos y verifique correos electrónicos de forma asíncrona.

Manejador de Webhook

// api/webhooks/bigcommerce.js
import crypto from 'crypto';
import EmailVerify from '@emailverify/sdk';

const bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY);

export default async function handler(req, res) {
    // Verificar firma del webhook
    const signature = req.headers['x-bc-webhook-signature'];
    const expectedSignature = crypto
        .createHmac('sha256', process.env.BC_WEBHOOK_SECRET)
        .update(JSON.stringify(req.body))
        .digest('base64');

    if (signature !== expectedSignature) {
        return res.status(401).json({ error: 'Firma inválida' });
    }

    const { scope, data } = req.body;

    if (scope === 'store/order/created') {
        await handleOrderCreated(data);
    }

    res.status(200).json({ received: true });
}

async function handleOrderCreated(data) {
    const orderId = data.id;

    // Obtener detalles del pedido de BigCommerce
    const order = await fetchOrder(orderId);

    if (!order.billing_address?.email) {
        return;
    }

    // Verificar correo electrónico
    const result = await bv.verify({ email: order.billing_address.email });

    // Almacenar resultado de verificación
    await storeVerificationResult(orderId, result);

    // Marcar pedidos sospechosos
    if (result.status === 'invalid' || result.is_disposable) {
        await flagOrder(orderId, {
            reason: result.status === 'invalid'
                ? 'Dirección de correo electrónico inválida'
                : 'Correo electrónico desechable detectado',
            verification: result,
        });
    }
}

async function fetchOrder(orderId) {
    const response = await fetch(
        `https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v2/orders/${orderId}`,
        {
            headers: {
                'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
                'Accept': 'application/json',
            },
        }
    );

    return response.json();
}

async function flagOrder(orderId, data) {
    // Agregar nota del pedido o actualizar estado
    await fetch(
        `https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v2/orders/${orderId}`,
        {
            method: 'PUT',
            headers: {
                'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                staff_notes: `⚠️ Advertencia de Verificación de Correo Electrónico: ${data.reason}`,
            }),
        }
    );
}

Registrar Webhook

async function registerWebhook() {
    const response = await fetch(
        `https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v3/hooks`,
        {
            method: 'POST',
            headers: {
                'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                scope: 'store/order/created',
                destination: 'https://yoursite.com/api/webhooks/bigcommerce',
                is_active: true,
            }),
        }
    );

    return response.json();
}

Verificación Masiva de Clientes

Verifique clientes existentes en su tienda.

import EmailVerify from '@emailverify/sdk';

const bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY);

async function verifyAllCustomers() {
    const customers = await fetchAllCustomers();
    const emails = customers.map(c => c.email);

    console.log(`Verificando ${emails.length} correos electrónicos de clientes...`);

    // Enviar trabajo masivo
    const job = await bv.verifyBulk(emails);
    console.log(`ID del trabajo: ${job.job_id}`);

    // Esperar finalización
    let status;
    do {
        await sleep(5000);
        status = await bv.getBulkJobStatus(job.job_id);
        console.log(`Progreso: ${status.progress_percent}%`);
    } while (status.status !== 'completed');

    // Procesar resultados
    const results = await bv.getBulkJobResults(job.job_id);

    const stats = { valid: 0, invalid: 0, disposable: 0 };

    for (const result of results.results) {
        if (result.status === 'valid') stats.valid++;
        else stats.invalid++;

        if (result.is_disposable) stats.disposable++;

        // Actualizar registro del cliente
        const customer = customers.find(c => c.email === result.email);
        if (customer) {
            await updateCustomerMeta(customer.id, {
                email_status: result.status,
                email_score: result.score,
            });
        }
    }

    console.log('Verificación completa:', stats);
    return stats;
}

async function fetchAllCustomers() {
    const customers = [];
    let page = 1;

    while (true) {
        const response = await fetch(
            `https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v3/customers?page=${page}&limit=250`,
            {
                headers: {
                    'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
                },
            }
        );

        const data = await response.json();

        if (data.data.length === 0) break;

        customers.push(...data.data);
        page++;
    }

    return customers;
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

Mejores Prácticas

1. Proteja Su Clave API

Nunca exponga su clave API en código del lado del cliente. Use un proxy:

// Proxy del lado del servidor
app.post('/api/verify', async (req, res) => {
    const { email } = req.body;

    const result = await bv.verify({ email });

    // Devolver solo los datos necesarios
    res.json({
        valid: result.status === 'valid',
        disposable: result.is_disposable,
    });
});

2. Almacenar Resultados en Caché

const cache = new Map();
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 horas

function getCachedResult(email) {
    const cached = cache.get(email);
    if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
        return cached.result;
    }
    return null;
}

function setCachedResult(email, result) {
    cache.set(email, { result, timestamp: Date.now() });
}

3. Manejar Errores con Elegancia

try {
    const result = await verifyEmail(email);
    // ... manejar resultado
} catch (error) {
    console.error('Verificación fallida:', error);
    // Permitir que el proceso de pago continúe
    return { valid: true };
}

Recursos Relacionados

On this page