Qué vas a aprender: Integrar la API de gigstack para generar facturas automáticas desde tu aplicación Node.js.
Tiempo estimado: 25-30 minutos
Stack técnico: Node.js 14+, Express.js, npm
Si alguna vez has intentado integrar facturación electrónica en México, sabes el dolor de cabeza que representa. No solo necesitas conectar una API, sino entender toda la parte fiscal del SAT: tipos de comprobante, regímenes fiscales, claves de producto SAT, retenciones, traslados... y la lista sigue.
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 gigstack, todo eso se abstrae. Tú envías los datos básicos de la transacción (cliente, monto, descripción) y gigstack se encarga de:
Vamos a construir una integración completa en menos de 30 minutos.
Antes de empezar, asegúrate de tener:
node --versionPrimero, crea un nuevo proyecto Node.js e instala las dependencias necesarias:
# Crear directorio del proyecto
mkdir gigstack-integration
cd gigstack-integration
# Inicializar proyecto Node.js
npm init -y
# Instalar dependencias
npm install express axios dotenv
Crea un archivo .env en la raíz del proyecto para almacenar tu API key de forma segura:
# .env
GIGSTACK_API_KEY=tu_api_key_aqui
PORT=3000
⚠️ Importante: Nunca commitees tu archivo .env a Git. Agrega .env a tu .gitignore.
Crea un archivo gigstack-client.js que maneje todas las peticiones a la API:
// gigstack-client.js
const axios = require('axios');
require('dotenv').config();
const GIGSTACK_API_URL = 'https://api.gigstack.pro/v1';
const API_KEY = process.env.GIGSTACK_API_KEY;
// Cliente HTTP configurado para gigstack
const gigstackClient = axios.create({
baseURL: GIGSTACK_API_URL,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
timeout: 10000 // 10 segundos timeout
});
// Interceptor para logging (útil en desarrollo)
gigstackClient.interceptors.request.use(
config => {
console.log(`[gigstack API] ${config.method.toUpperCase()} ${config.url}`);
return config;
},
error => Promise.reject(error)
);
// Manejo centralizado de errores
gigstackClient.interceptors.response.use(
response => response,
error => {
if (error.response) {
// El servidor respondió con un código de error
console.error('[gigstack API Error]', error.response.status, error.response.data);
} else if (error.request) {
// La petición se hizo pero no hubo respuesta
console.error('[gigstack API] No response received');
} else {
// Error al configurar la petición
console.error('[gigstack API] Request error:', error.message);
}
return Promise.reject(error);
}
);
module.exports = gigstackClient;
Ahora crea invoice-service.js con la lógica para crear facturas:
// invoice-service.js
const gigstackClient = require('./gigstack-client');
/**
* Genera una factura electrónica usando gigstack API
* @param {Object} invoiceData - Datos de la factura
* @returns {PromiseCrea server.js con un servidor Express que exponga un endpoint para facturar:
// server.js
const express = require('express');
const { createInvoice, getInvoiceStatus } = require('./invoice-service');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware para parsear JSON
app.use(express.json());
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', service: 'gigstack-integration' });
});
// Endpoint para crear facturas
app.post('/api/invoices', async (req, res) => {
try {
const { customerName, customerEmail, customerRFC, customerZipCode, items } = req.body;
// Validación básica
if (!customerName || !customerEmail || !customerRFC || !items || items.length === 0) {
return res.status(400).json({
error: 'Faltan datos requeridos: customerName, customerEmail, customerRFC, items'
});
}
// Generar factura con gigstack
const result = await createInvoice({
customerName,
customerEmail,
customerRFC,
customerZipCode,
items
});
if (result.success) {
return res.status(201).json({
message: 'Factura creada exitosamente',
invoice: result.invoice
});
} else {
return res.status(500).json({
error: 'Error al crear factura',
details: result.error
});
}
} catch (error) {
console.error('Error en endpoint /api/invoices:', error);
return res.status(500).json({ error: 'Error interno del servidor' });
}
});
// Endpoint para consultar estatus de factura
app.get('/api/invoices/:id', async (req, res) => {
const { id } = req.params;
const result = await getInvoiceStatus(id);
if (result.success) {
return res.json(result.invoice);
} else {
return res.status(404).json({ error: result.error });
}
});
// Iniciar servidor
app.listen(PORT, () => {
console.log(`🚀 Servidor corriendo en http://localhost:${PORT}`);
console.log(`📄 Health check: http://localhost:${PORT}/health`);
});
Inicia el servidor:
node server.js
Desde otra terminal, prueba el endpoint con curl o Postman:
curl -X POST http://localhost:3000/api/invoices \
-H "Content-Type: application/json" \
-d '{
"customerName": "ACME Corp SA de CV",
"customerEmail": "facturacion@acme.com",
"customerRFC": "ACM010101ABC",
"customerZipCode": "06600",
"items": [
{
"description": "Licencia Software SaaS - Plan Pro",
"quantity": 1,
"unitPrice": 999.00
}
]
}'
Respuesta esperada (200 OK):
{
"message": "Factura creada exitosamente",
"invoice": {
"uuid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890",
"status": "active",
"xml_url": "https://storage.gigstack.pro/invoices/...",
"pdf_url": "https://storage.gigstack.pro/invoices/...",
"total": 1158.84,
"created_at": "2026-01-02T08:00:00Z"
}
}
✅ ¡Listo! Tu primera factura generada con gigstack.
❌ Error 1: 401 Unauthorized
.env tenga la API key correcta desde app.gigstack.pro/settings// Verifica que se cargue correctamente
console.log('API Key:', process.env.GIGSTACK_API_KEY ? '✓ Configurada' : '✗ Faltante');
❌ Error 2: 400 Bad Request - Invalid RFC
function isValidRFC(rfc) {
// RFC Persona Moral: 12 caracteres
// RFC Persona Física: 13 caracteres
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
return rfcRegex.test(rfc);
}
if (!isValidRFC(customerRFC)) {
throw new Error('RFC inválido');
}
❌ Error 3: 422 Unprocessable Entity - Invalid product_key
// Claves comunes:
const PRODUCT_KEYS = {
SOFTWARE: '84111506', // Servicios de facturación
CONSULTING: '85121801', // Servicios de consultoría
DIGITAL_SERVICES: '81112100' // Servicios digitales
};
1. Manejo de retries con exponential backoff
Las APIs pueden fallar temporalmente. Implementa lógica de reintentos:
async function createInvoiceWithRetry(invoiceData, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await createInvoice(invoiceData);
} catch (error) {
if (attempt === maxRetries) throw error;
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
console.log(`Retry ${attempt}/${maxRetries} in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
2. Logging y monitoring
Registra todas las operaciones para debugging:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'gigstack-invoices.log' })
]
});
// En tu código:
logger.info('Invoice created', { uuid: invoice.uuid, customer: customerName });
3. Validación de datos con Joi o Zod
const Joi = require('joi');
const invoiceSchema = Joi.object({
customerName: Joi.string().required(),
customerEmail: Joi.string().email().required(),
customerRFC: Joi.string().pattern(/^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/).required(),
customerZipCode: Joi.string().length(5).required(),
items: Joi.array().min(1).required()
});
const { error } = invoiceSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
4. Rate limiting para evitar abusos
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100 // máximo 100 requests por ventana
});
app.use('/api/invoices', limiter);
5. Usa variables de entorno para diferentes ambientes
// .env.development
GIGSTACK_API_URL=https://sandbox.gigstack.pro/v1
// .env.production
GIGSTACK_API_URL=https://api.gigstack.pro/v1
Ahora que tienes la integración básica funcionando, puedes explorar:
📖 Docs oficiales: docs.gigstack.io
💬 Soporte técnico: soporte@gigstack.io
🧪 Sandbox para testing: app.gigstack.pro
💡 Catálogo SAT (claves de producto, unidades): pys.sat.gob.mx
Prueba gigstack en sandbox gratis y factura en menos de 30 minutos. Sin trámites con el SAT, sin conocimientos fiscales, solo código.
Crea un archivo gigstack-client.js que maneje todas las peticiones a la API:
// gigstack-client.js
const axios = require('axios');
require('dotenv').config();
const GIGSTACK_API_URL = 'https://api.gigstack.pro/v1';
const API_KEY = process.env.GIGSTACK_API_KEY;
// Cliente HTTP configurado para gigstack
const gigstackClient = axios.create({
baseURL: GIGSTACK_API_URL,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
timeout: 10000 // 10 segundos timeout
});
// Interceptor para logging (útil en desarrollo)
gigstackClient.interceptors.request.use(
config => {
console.log(`[gigstack API] ${config.method.toUpperCase()} ${config.url}`);
return config;
},
error => Promise.reject(error)
);
// Manejo centralizado de errores
gigstackClient.interceptors.response.use(
response => response,
error => {
if (error.response) {
// El servidor respondió con un código de error
console.error('[gigstack API Error]', error.response.status, error.response.data);
} else if (error.request) {
// La petición se hizo pero no hubo respuesta
console.error('[gigstack API] No response received');
} else {
// Error al configurar la petición
console.error('[gigstack API] Request error:', error.message);
}
return Promise.reject(error);
}
);
module.exports = gigstackClient;
Ahora crea invoice-service.js con la lógica para crear facturas:
// invoice-service.js
const gigstackClient = require('./gigstack-client');
/**
* Genera una factura electrónica usando gigstack API
* @param {Object} invoiceData - Datos de la factura
* @returns {Promise} - Factura generada con UUID, XML y PDF
*/
async function createInvoice(invoiceData) {
try {
const payload = {
// Datos del receptor (tu cliente)
customer: {
name: invoiceData.customerName,
email: invoiceData.customerEmail,
rfc: invoiceData.customerRFC,
tax_system: invoiceData.customerTaxSystem || '601', // General de Ley Personas Morales (default)
zip_code: invoiceData.customerZipCode
},
// Conceptos/items de la factura
items: invoiceData.items.map(item => ({
description: item.description,
quantity: item.quantity,
unit_price: item.unitPrice,
product_key: item.productKey || '84111506', // Servicios de facturación (default)
unit_key: item.unitKey || 'E48' // Unidad de servicio (default)
})),
// Método de pago
payment_method: invoiceData.paymentMethod || 'PUE', // Pago en una sola exhibición
payment_form: invoiceData.paymentForm || '03', // Transferencia electrónica
// Uso del CFDI (para qué usa el cliente la factura)
use: invoiceData.use || 'G03' // Gastos en general
};
// Hacer petición POST a gigstack API
const response = await gigstackClient.post('/invoices', payload);
// gigstack responde con el CFDI timbrado
return {
success: true,
invoice: {
uuid: response.data.uuid, // Folio fiscal del SAT
status: response.data.status,
xml_url: response.data.xml_url, // URL del XML
pdf_url: response.data.pdf_url, // URL del PDF
total: response.data.total,
created_at: response.data.created_at
}
};
} catch (error) {
return {
success: false,
error: {
message: error.response?.data?.message || error.message,
code: error.response?.status,
details: error.response?.data
}
};
}
}
/**
* Consulta el estatus de una factura
* @param {string} invoiceId - ID de la factura en gigstack
* @returns {Promise}
*/
async function getInvoiceStatus(invoiceId) {
try {
const response = await gigstackClient.get(`/invoices/${invoiceId}`);
return { success: true, invoice: response.data };
} catch (error) {
return {
success: false,
error: error.response?.data?.message || error.message
};
}
}
module.exports = {
createInvoice,
getInvoiceStatus
};
Crea server.js con un servidor Express que exponga un endpoint para facturar:
// server.js
const express = require('express');
const { createInvoice, getInvoiceStatus } = require('./invoice-service');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware para parsear JSON
app.use(express.json());
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', service: 'gigstack-integration' });
});
// Endpoint para crear facturas
app.post('/api/invoices', async (req, res) => {
try {
const { customerName, customerEmail, customerRFC, customerZipCode, items } = req.body;
// Validación básica
if (!customerName || !customerEmail || !customerRFC || !items || items.length === 0) {
return res.status(400).json({
error: 'Faltan datos requeridos: customerName, customerEmail, customerRFC, items'
});
}
// Generar factura con gigstack
const result = await createInvoice({
customerName,
customerEmail,
customerRFC,
customerZipCode,
items
});
if (result.success) {
return res.status(201).json({
message: 'Factura creada exitosamente',
invoice: result.invoice
});
} else {
return res.status(500).json({
error: 'Error al crear factura',
details: result.error
});
}
} catch (error) {
console.error('Error en endpoint /api/invoices:', error);
return res.status(500).json({ error: 'Error interno del servidor' });
}
});
// Endpoint para consultar estatus de factura
app.get('/api/invoices/:id', async (req, res) => {
const { id } = req.params;
const result = await getInvoiceStatus(id);
if (result.success) {
return res.json(result.invoice);
} else {
return res.status(404).json({ error: result.error });
}
});
// Iniciar servidor
app.listen(PORT, () => {
console.log(`🚀 Servidor corriendo en http://localhost:${PORT}`);
console.log(`📄 Health check: http://localhost:${PORT}/health`);
});
Inicia el servidor:
node server.js
Desde otra terminal, prueba el endpoint con curl o Postman:
curl -X POST http://localhost:3000/api/invoices \
-H "Content-Type: application/json" \
-d '{
"customerName": "ACME Corp SA de CV",
"customerEmail": "facturacion@acme.com",
"customerRFC": "ACM010101ABC",
"customerZipCode": "06600",
"items": [
{
"description": "Licencia Software SaaS - Plan Pro",
"quantity": 1,
"unitPrice": 999.00
}
]
}'
Respuesta esperada (200 OK):
{
"message": "Factura creada exitosamente",
"invoice": {
"uuid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890",
"status": "active",
"xml_url": "https://storage.gigstack.pro/invoices/...",
"pdf_url": "https://storage.gigstack.pro/invoices/...",
"total": 1158.84,
"created_at": "2026-01-02T08:00:00Z"
}
}
✅ ¡Listo! Tu primera factura generada con gigstack.
❌ Error 1: 401 Unauthorized
.env tenga la API key correcta desde app.gigstack.pro/settings// Verifica que se cargue correctamente
console.log('API Key:', process.env.GIGSTACK_API_KEY ? '✓ Configurada' : '✗ Faltante');
❌ Error 2: 400 Bad Request - Invalid RFC
function isValidRFC(rfc) {
// RFC Persona Moral: 12 caracteres
// RFC Persona Física: 13 caracteres
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
return rfcRegex.test(rfc);
}
if (!isValidRFC(customerRFC)) {
throw new Error('RFC inválido');
}
❌ Error 3: 422 Unprocessable Entity - Invalid product_key
// Claves comunes:
const PRODUCT_KEYS = {
SOFTWARE: '84111506', // Servicios de facturación
CONSULTING: '85121801', // Servicios de consultoría
DIGITAL_SERVICES: '81112100' // Servicios digitales
};
1. Manejo de retries con exponential backoff
Las APIs pueden fallar temporalmente. Implementa lógica de reintentos:
async function createInvoiceWithRetry(invoiceData, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await createInvoice(invoiceData);
} catch (error) {
if (attempt === maxRetries) throw error;
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
console.log(`Retry ${attempt}/${maxRetries} in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
2. Logging y monitoring
Registra todas las operaciones para debugging:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'gigstack-invoices.log' })
]
});
// En tu código:
logger.info('Invoice created', { uuid: invoice.uuid, customer: customerName });
3. Validación de datos con Joi o Zod
const Joi = require('joi');
const invoiceSchema = Joi.object({
customerName: Joi.string().required(),
customerEmail: Joi.string().email().required(),
customerRFC: Joi.string().pattern(/^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/).required(),
customerZipCode: Joi.string().length(5).required(),
items: Joi.array().min(1).required()
});
const { error } = invoiceSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
4. Rate limiting para evitar abusos
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100 // máximo 100 requests por ventana
});
app.use('/api/invoices', limiter);
5. Usa variables de entorno para diferentes ambientes
// .env.development
GIGSTACK_API_URL=https://sandbox.gigstack.pro/v1
// .env.production
GIGSTACK_API_URL=https://api.gigstack.pro/v1
Ahora que tienes la integración básica funcionando, puedes explorar:
📖 Docs oficiales: docs.gigstack.io
💬 Soporte técnico: soporte@gigstack.io
🧪 Sandbox para testing: app.gigstack.pro
💡 Catálogo SAT (claves de producto, unidades): pys.sat.gob.mx
Prueba gigstack en sandbox gratis y factura en menos de 30 minutos. Sin trámites con el SAT, sin conocimientos fiscales, solo código.
