Si cobras pagos con Stripe en México, necesitas facturar cada transacción. Hacerlo manualmente es un desperdicio de tiempo. En este tutorial técnico, te muestro cómo conectar Stripe con gigstack usando código real en Node.js para automatizar completamente tu facturación.
Stripe procesa tus pagos, gigstack se encarga del SAT. Cero intervención manual, cero errores de RFC, facturas entregadas automáticamente a tus clientes en segundos.
Casos de uso comunes:
Antes de empezar, asegúrate de tener:
Primero, instala las dependencias necesarias:
npm install stripe express dotenv @gigstack/sdk-nodeCrea tu archivo .env con tus credenciales:
STRIPE_SECRET_KEY=sk_test_tu_clave_stripe
STRIPE_WEBHOOK_SECRET=whsec_tu_webhook_secret
GIGSTACK_API_KEY=gsk_live_tu_clave_gigstack
GIGSTACK_ENDPOINT=https://api.gigstack.io/v1Crea un endpoint que escuche eventos de Stripe. Este código captura pagos exitosos y dispara la facturación:
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const { GigstackClient } = require('@gigstack/sdk-node');
const app = express();
const gigstack = new GigstackClient({
apiKey: process.env.GIGSTACK_API_KEY,
endpoint: process.env.GIGSTACK_ENDPOINT
});
app.post('/webhooks/stripe',
express.raw({ type: 'application/json' }),
async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.error('⚠️ Webhook signature verification failed:', err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Manejar evento de pago exitoso
if (event.type === 'payment_intent.succeeded') {
const paymentIntent = event.data.object;
await generarFactura(paymentIntent);
}
res.json({ received: true });
});Esta es la función crítica que conecta el pago de Stripe con gigstack:
async function generarFactura(paymentIntent) {
try {
// Extraer metadata del cliente (debe incluirse al crear PaymentIntent)
const { rfc, email, razon_social, uso_cfdi, metodo_pago } = paymentIntent.metadata;
// Validar RFC antes de facturar
if (!rfc || rfc.length < 12) {
throw new Error('RFC inválido en metadata');
}
// Preparar items de la factura
const items = [{
cantidad: 1,
clave_producto_servicio: '81112100', // Consultoría en software
clave_unidad: 'E48', // Unidad de servicio
descripcion: paymentIntent.description || 'Servicio de suscripción',
valor_unitario: paymentIntent.amount / 100, // Stripe usa centavos
impuestos: [{
tipo: 'IVA',
tasa: 0.16,
retencion: false
}]
}];
// Crear factura en gigstack
const factura = await gigstack.invoices.create({
tipo: 'I', // Ingreso
receptor: {
rfc: rfc,
nombre: razon_social,
uso_cfdi: uso_cfdi || 'G03', // Default: Gastos en general
domicilio_fiscal_receptor: '01234' // Requerido desde 2026
},
conceptos: items,
forma_pago: metodo_pago || '03', // Transferencia electrónica
metodo_pago: 'PUE', // Pago en una exhibición
moneda: 'MXN',
external_id: paymentIntent.id // Vincular con Stripe
});
console.log('✅ Factura generada:', factura.uuid);
// Enviar factura por email automáticamente
await gigstack.invoices.sendEmail(factura.id, {
to: email,
subject: '📄 Tu factura de gigstack',
template: 'default'
});
return factura;
} catch (error) {
console.error('❌ Error al facturar:', error.message);
// Implementar retry logic o notificación a equipo
await notificarErrorFacturacion(paymentIntent, error);
throw error;
}
}Para que esto funcione, necesitas capturar el RFC del cliente antes de procesar el pago. Agrega estos campos a tu checkout:
// Frontend: Capturar RFC en el checkout
const paymentIntent = await stripe.paymentIntents.create({
amount: 50000, // $500 MXN
currency: 'mxn',
description: 'Plan Pro - Mes de Enero 2026',
metadata: {
rfc: 'XAXX010101000',
email: 'cliente@ejemplo.com',
razon_social: 'Empresa Ejemplo SA de CV',
uso_cfdi: 'G03',
metodo_pago: '03'
}
});El SAT puede rechazar facturas por múltiples razones. Implementa un sistema robusto de manejo de errores:
async function notificarErrorFacturacion(paymentIntent, error) {
// Clasificar tipo de error
const esErrorSAT = error.code?.startsWith('SAT_');
const esErrorRFC = error.message?.includes('RFC');
if (esErrorRFC) {
// RFC inválido: notificar al cliente para corrección
await enviarEmailCorreccionRFC(paymentIntent);
} else if (esErrorSAT) {
// Error del SAT: reintentar después
await agendarReintento(paymentIntent, error);
} else {
// Error desconocido: alertar al equipo técnico
await alertarEquipoTecnico(paymentIntent, error);
}
}
async function agendarReintento(paymentIntent, error) {
// Usar cron job o queue (Bull, BeeQueue, etc.)
await queue.add('retry-invoice', {
paymentIntent: paymentIntent.id,
intentos: 1,
error: error.message
}, {
delay: 5 * 60 * 1000, // Reintentar en 5 minutos
attempts: 3,
backoff: {
type: 'exponential',
delay: 5000
}
});
}Antes de ir a producción, prueba todo en el entorno de sandbox de gigstack:
// Configuración para testing
const gigstackTest = new GigstackClient({
apiKey: process.env.GIGSTACK_TEST_API_KEY,
endpoint: 'https://sandbox.gigstack.io/v1',
sandbox: true
});
// RFCs de prueba del SAT
const RFC_PRUEBA = 'EKU9003173C9'; // RFC genérico para testing
// Usar Stripe test mode
const stripeTest = require('stripe')(process.env.STRIPE_TEST_KEY);/validate-rfc antes del checkoutexternal_id para evitar facturas duplicadasImplementa logs estructurados para trackear el flujo completo:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'invoicing-errors.log', level: 'error' }),
new winston.transports.File({ filename: 'invoicing-combined.log' })
]
});
logger.info('Factura creada', {
stripe_payment_id: paymentIntent.id,
gigstack_invoice_id: factura.id,
uuid: factura.uuid,
rfc: factura.receptor.rfc,
monto: paymentIntent.amount / 100,
timestamp: new Date().toISOString()
});Respuesta: Verifica que tu endpoint esté accesible públicamente (usa ngrok en desarrollo), confirma que la URL esté correctamente configurada en el dashboard de Stripe, y revisa que estés usando express.raw() en lugar de express.json() para validar la firma del webhook. Stripe necesita el body raw para verificar la autenticidad.
Respuesta: Implementa un flujo de corrección: (1) Guarda el pago como "pendiente de facturar" en tu base de datos, (2) Envía un email al cliente con un link para actualizar su RFC, (3) Cuando corrija el RFC, dispara la facturación manualmente con gigstack.invoices.create(). Usa external_id para vincular con el payment_intent original.
Respuesta: Sí. Escucha el evento charge.refunded de Stripe y usa gigstack.invoices.cancel(invoice_id, { motivo: '01', uuid_replacement: null }). El motivo '01' indica "Comprobante emitido con errores con relación". Si generas una nota de crédito, incluye el uuid_replacement del nuevo comprobante.
Respuesta: gigstack permite 100 requests por minuto en planes estándar y 500 req/min en planes enterprise. Si procesas alto volumen, implementa un sistema de queue (Bull, RabbitMQ) para distribuir las llamadas y evitar errores 429. El SDK de Node.js incluye retry automático con backoff exponencial desde la versión 2.1.0.
Integrar Stripe con gigstack elimina completamente la carga manual de facturación. Con este código base, tienes un sistema robusto que maneja pagos, genera facturas automáticamente, y entrega comprobantes fiscales sin intervención humana.
Recuerda probar exhaustivamente en sandbox antes de ir a producción, y monitorea tus logs para identificar problemas temprano.
¿Necesitas ayuda con la implementación? Revisa la documentación completa de gigstack o contacta al equipo técnico.
