Qué vas a aprender: Implementar webhooks event-driven con gigstack para recibir notificaciones en tiempo real de facturas emitidas, pagos recibidos y cambios de estatus fiscal.
Tiempo estimado: 25-30 minutos
Stack técnico: Node.js + Express + gigstack API
Integrar facturación electrónica en México es un dolor de cabeza. Tienes que:
Antes intenté conectar una API de facturación, tienes que aprender toda la parte fiscal, no eres contador, es frustrante como dev. gigstack te quita esa carga fiscal.
Con webhooks event-driven de gigstack, tu aplicación recibe notificaciones automáticas cuando algo importante sucede: se emite una factura, llega un pago, el SAT valida un CFDI. Nada de polling. Arquitectura reactiva, eficiente, escalable.
Antes de empezar, asegúrate de tener:
Primero, crea tu proyecto Node.js e instala las dependencias necesarias:
# Crear directorio del proyecto
mkdir gigstack-webhooks-tutorial
cd gigstack-webhooks-tutorial
# Inicializar npm
npm init -y
# Instalar dependencias
npm install express body-parser crypto dotenv
Crea un archivo .env para tus credenciales:
GIGSTACK_API_KEY=tu_api_key_aqui
GIGSTACK_WEBHOOK_SECRET=tu_webhook_secret_aqui
PORT=3000
⚠️ Importante: El WEBHOOK_SECRET lo obtienes desde el dashboard de gigstack cuando configures tu webhook endpoint. Lo usaremos para validar que las peticiones realmente vienen de gigstack.
Crea server.js con la estructura base:
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware para parsear JSON
app.use(bodyParser.json());
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', service: 'gigstack-webhooks' });
});
// Webhook endpoint principal
app.post('/webhooks/gigstack', async (req, res) => {
try {
// Validar firma del webhook (seguridad)
if (!validateWebhookSignature(req)) {
console.error('❌ Firma de webhook inválida');
return res.status(401).json({ error: 'Invalid signature' });
}
// Procesar el evento
const event = req.body;
console.log('✅ Webhook recibido:', event.type);
await handleWebhookEvent(event);
// Responder rápido (gigstack espera 200 en <5 segundos)
res.status(200).json({ received: true });
} catch (error) {
console.error('❌ Error procesando webhook:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(PORT, () => {
console.log(`🚀 Servidor escuchando en puerto ${PORT}`);
console.log(`📥 Webhook endpoint: http://localhost:${PORT}/webhooks/gigstack`);
});
Los webhooks deben validarse para evitar ataques. gigstack firma cada request con HMAC-SHA256:
/**
* Valida que el webhook realmente venga de gigstack
* gigstack envía la firma en el header 'X-Gigstack-Signature'
*/
function validateWebhookSignature(req) {
const signature = req.headers['x-gigstack-signature'];
const secret = process.env.GIGSTACK_WEBHOOK_SECRET;
if (!signature || !secret) {
console.warn('⚠️ Falta signature o secret');
return false;
}
// Recrear la firma con el payload recibido
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// Comparación segura contra timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
🔒 Best Practice: Nunca proceses webhooks sin validar la firma. Un atacante podría enviar eventos falsos a tu endpoint.
gigstack envía varios tipos de eventos. Implementa handlers específicos:
/**
* Router principal de eventos webhook
*/
async function handleWebhookEvent(event) {
const { type, data } = event;
switch (type) {
case 'invoice.created':
await handleInvoiceCreated(data);
break;
case 'invoice.paid':
await handleInvoicePaid(data);
break;
case 'invoice.cancelled':
await handleInvoiceCancelled(data);
break;
case 'payment.received':
await handlePaymentReceived(data);
break;
case 'cfdi.validated':
await handleCFDIValidated(data);
break;
default:
console.log(`⚠️ Evento no manejado: ${type}`);
}
}
/**
* Handler: Nueva factura creada
*/
async function handleInvoiceCreated(data) {
console.log('📄 Nueva factura creada:', {
id: data.id,
folio: data.folio,
total: data.total,
cliente: data.customer.name
});
// Aquí va tu lógica de negocio:
// - Actualizar base de datos
// - Enviar email al cliente
// - Notificar a tu sistema de contabilidad
// - Trigger analytics event
// Ejemplo: Actualizar DB
// await db.invoices.create({
// gigstack_id: data.id,
// folio: data.folio,
// status: 'created',
// amount: data.total
// });
}
/**
* Handler: Factura pagada
*/
async function handleInvoicePaid(data) {
console.log('💰 Factura pagada:', {
id: data.id,
folio: data.folio,
payment_method: data.payment_method,
paid_at: data.paid_at
});
// Lógica de negocio para pago recibido:
// - Marcar como pagada en tu sistema
// - Liberar producto/servicio
// - Enviar confirmación al cliente
// - Actualizar métricas de revenue
}
/**
* Handler: Factura cancelada
*/
async function handleInvoiceCancelled(data) {
console.log('🚫 Factura cancelada:', {
id: data.id,
folio: data.folio,
reason: data.cancellation_reason
});
// Lógica para cancelaciones:
// - Actualizar status en tu sistema
// - Revertir operaciones si es necesario
// - Notificar a contabilidad
}
/**
* Handler: Pago recibido (puede aplicar a múltiples facturas)
*/
async function handlePaymentReceived(data) {
console.log('💳 Pago recibido:', {
id: data.id,
amount: data.amount,
method: data.method,
invoices: data.applied_to_invoices
});
}
/**
* Handler: CFDI validado por el SAT
*/
async function handleCFDIValidated(data) {
console.log('✅ CFDI validado por SAT:', {
uuid: data.uuid,
status: data.sat_status,
validated_at: data.validated_at
});
}
Para probar webhooks en local, necesitas exponer tu servidor a internet. Usa ngrok:
# Instalar ngrok (macOS)
brew install ngrok
# O descarga desde https://ngrok.com/download
# Exponer tu servidor local
ngrok http 3000
Ngrok te dará una URL pública tipo https://abc123.ngrok.io. Copia esa URL y ve al dashboard de gigstack:
https://tu-ngrok-url.ngrok.io/webhooks/gigstackWEBHOOK_SECRET generadoActualiza tu .env con el secret y reinicia tu servidor.
Desde el dashboard de gigstack, puedes enviar un webhook de prueba. Verás en tu consola:
🚀 Servidor escuchando en puerto 3000
📥 Webhook endpoint: http://localhost:3000/webhooks/gigstack
✅ Webhook recibido: invoice.created
📄 Nueva factura creada: {
id: 'inv_abc123xyz',
folio: 'A-1234',
total: 1000.00,
cliente: 'Cliente Demo SA de CV'
}
✅ ¡Funciona! Ahora puedes crear facturas desde gigstack y tu sistema reaccionará automáticamente.
❌ Error 1: "Invalid signature" constante
WEBHOOK_SECRET en tu .env coincida exactamente con el del dashboard. No agregues espacios ni saltos de línea.// ❌ Incorrecto
GIGSTACK_WEBHOOK_SECRET= tu_secret_con_espacio
// ✅ Correcto
GIGSTACK_WEBHOOK_SECRET=tu_secret_sin_espacios
❌ Error 2: Timeout - gigstack reintenta el webhook
200 OK rápido (<5 segundos). Si tu lógica toma tiempo, usa una queue (Bull, BullMQ, AWS SQS):app.post('/webhooks/gigstack', async (req, res) => {
// Validar firma
if (!validateWebhookSignature(req)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// ✅ Encolar para procesamiento asíncrono
await queue.add('process-webhook', req.body);
// Responder inmediatamente
res.status(200).json({ received: true });
});
❌ Error 3: Webhooks duplicados procesados múltiples veces
event.id y verifica que no lo hayas procesado antes:const processedEvents = new Set(); // En producción, usa Redis
async function handleWebhookEvent(event) {
// Verificar idempotencia
if (processedEvents.has(event.id)) {
console.log('⚠️ Evento ya procesado:', event.id);
return; // Skip
}
// Procesar evento
await processEvent(event);
// Marcar como procesado
processedEvents.add(event.id);
}
❌ Error 4: ngrok URL cambia cada vez que reinicias
ngrok http 3000 --subdomain=tu-subdominio-fijo
🔒 Seguridad:
WEBHOOK_SECRET en logs o repos públicos⚡ Performance:
200 OK en <5 segundos, procesa después de forma asíncrona🛡️ Resiliencia:
📊 Monitoring:
// Ejemplo de logging estructurado
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.json(),
transports: [new winston.transports.Console()]
});
app.post('/webhooks/gigstack', async (req, res) => {
const startTime = Date.now();
try {
await handleWebhookEvent(req.body);
logger.info('webhook_processed', {
event_type: req.body.type,
event_id: req.body.id,
duration_ms: Date.now() - startTime
});
res.status(200).json({ received: true });
} catch (error) {
logger.error('webhook_error', {
event_type: req.body.type,
error: error.message,
stack: error.stack
});
res.status(500).json({ error: 'Internal error' });
}
});
Ahora que tienes webhooks funcionando, puedes:
Features avanzadas de gigstack API:
gigstack ofrece un ambiente de sandbox completo donde puedes probar toda la API sin afectar facturas reales. Crea facturas de prueba, simula pagos, dispara webhooks y valida tu integración antes de ir a producción.
👉 Regístrate gratis en gigstack y empieza a construir tu integración hoy.
Como developer, no deberías perder tiempo aprendiendo leyes fiscales del SAT. gigstack es la infraestructura fiscal que necesitas para enfocarte en lo que sabes hacer: construir producto.
