Si estás construyendo un producto SaaS, e-commerce o plataforma en México, necesitas facturación electrónica CFDI 4.0. En lugar de construir desde cero (complejidad masiva, certificaciones PAC, mantenimiento de cambios SAT), integras gigstack API en <2 horas y olvidas el problema.
En este tutorial crearás tu primera integración: automatizar facturación al recibir un pago. Incluye código real en Node.js, Python y PHP listo para producción 2026.
gsk_live_...)IMPORTANTE: Nunca expongas tu API key en código frontend o repos públicos.
gigstack usa Bearer token en header Authorization:
Authorization: Bearer gsk_live_tu_api_keymkdir gigstack-integration
cd gigstack-integration
npm init -y
npm install axios dotenvCrea .env:
GIGSTACK_API_KEY=gsk_live_tu_api_key
GIGSTACK_BASE_URL=https://api.gigstack.io/v2mkdir gigstack-integration
cd gigstack-integration
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install requests python-dotenvCrea .env igual que Node.js.
mkdir gigstack-integration
cd gigstack-integration
composer require guzzlehttp/guzzle vlucas/phpdotenvCrea .env igual que Node.js.
Antes de facturar, valida que el RFC del cliente esté activo en SAT.
// createClient.js
require('dotenv').config();
const axios = require('axios');
const API_KEY = process.env.GIGSTACK_API_KEY;
const BASE_URL = process.env.GIGSTACK_BASE_URL;
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
};
async function createClient(clientData) {
try {
const response = await axios.post(
`${BASE_URL}/clients`,
clientData,
{ headers }
);
console.log('✅ Cliente creado:', response.data.data);
return response.data.data;
} catch (error) {
console.error('❌ Error:', error.response?.data || error.message);
throw error;
}
}
// Ejemplo de uso
const newClient = {
rfc: 'XAXX010101000', // RFC genérico para pruebas
name: 'Cliente Demo SA de CV',
email: 'cliente@demo.com',
postal_code: '01000',
tax_regime: '601' // General de Ley Personas Morales
};
createClient(newClient);# create_client.py
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv('GIGSTACK_API_KEY')
BASE_URL = os.getenv('GIGSTACK_BASE_URL')
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
def create_client(client_data):
try:
response = requests.post(
f'{BASE_URL}/clients',
json=client_data,
headers=headers
)
response.raise_for_status()
print('✅ Cliente creado:', response.json()['data'])
return response.json()['data']
except requests.exceptions.RequestException as e:
print('❌ Error:', e.response.json() if e.response else str(e))
raise
# Ejemplo de uso
new_client = {
'rfc': 'XAXX010101000',
'name': 'Cliente Demo SA de CV',
'email': 'cliente@demo.com',
'postal_code': '01000',
'tax_regime': '601'
}
create_client(new_client)<?php
// createClient.php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
$apiKey = $_ENV['GIGSTACK_API_KEY'];
$baseUrl = $_ENV['GIGSTACK_BASE_URL'];
$client = new Client([
'base_uri' => $baseUrl,
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json'
]
]);
function createClient($httpClient, $clientData) {
try {
$response = $httpClient->post('/clients', [
'json' => $clientData
]);
$data = json_decode($response->getBody(), true);
echo "✅ Cliente creado: " . json_encode($data['data']) . "\n";
return $data['data'];
} catch (Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
throw $e;
}
}
// Ejemplo de uso
$newClient = [
'rfc' => 'XAXX010101000',
'name' => 'Cliente Demo SA de CV',
'email' => 'cliente@demo.com',
'postal_code' => '01000',
'tax_regime' => '601'
];
createClient($client, $newClient);Una vez creado el cliente, genera su factura.
// createInvoice.js
require('dotenv').config();
const axios = require('axios');
const API_KEY = process.env.GIGSTACK_API_KEY;
const BASE_URL = process.env.GIGSTACK_BASE_URL;
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
};
async function createInvoice(invoiceData) {
try {
const response = await axios.post(
`${BASE_URL}/invoices`,
invoiceData,
{ headers }
);
console.log('✅ Factura generada:', response.data.data);
console.log(' UUID:', response.data.data.uuid);
console.log(' PDF:', response.data.data.pdf_url);
console.log(' XML:', response.data.data.xml_url);
return response.data.data;
} catch (error) {
console.error('❌ Error:', error.response?.data || error.message);
throw error;
}
}
// Ejemplo de uso
const invoice = {
client_id: 'cliente_id_del_paso_anterior',
payment_form: '04', // Tarjeta de crédito
payment_method: 'PUE', // Pago en una sola exhibición
cfdi_use: 'G03', // Gastos en general
items: [
{
description: 'Suscripción mensual Premium',
quantity: 1,
unit_price: 999.00,
tax_rate: 0.16, // IVA 16%
product_key: '84111506', // Servicios de suscripción
unit_key: 'E48' // Servicio
}
],
notes: 'Gracias por tu compra'
};
createInvoice(invoice);# create_invoice.py
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv('GIGSTACK_API_KEY')
BASE_URL = os.getenv('GIGSTACK_BASE_URL')
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
def create_invoice(invoice_data):
try:
response = requests.post(
f'{BASE_URL}/invoices',
json=invoice_data,
headers=headers
)
response.raise_for_status()
data = response.json()['data']
print('✅ Factura generada:', data)
print(f' UUID: {data["uuid"]}')
print(f' PDF: {data["pdf_url"]}')
print(f' XML: {data["xml_url"]}')
return data
except requests.exceptions.RequestException as e:
print('❌ Error:', e.response.json() if e.response else str(e))
raise
# Ejemplo de uso
invoice = {
'client_id': 'cliente_id_del_paso_anterior',
'payment_form': '04',
'payment_method': 'PUE',
'cfdi_use': 'G03',
'items': [
{
'description': 'Suscripción mensual Premium',
'quantity': 1,
'unit_price': 999.00,
'tax_rate': 0.16,
'product_key': '84111506',
'unit_key': 'E48'
}
],
'notes': 'Gracias por tu compra'
}
create_invoice(invoice)<?php
// createInvoice.php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
$apiKey = $_ENV['GIGSTACK_API_KEY'];
$baseUrl = $_ENV['GIGSTACK_BASE_URL'];
$client = new Client([
'base_uri' => $baseUrl,
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json'
]
]);
function createInvoice($httpClient, $invoiceData) {
try {
$response = $httpClient->post('/invoices', [
'json' => $invoiceData
]);
$data = json_decode($response->getBody(), true)['data'];
echo "✅ Factura generada: " . json_encode($data) . "\n";
echo " UUID: {$data['uuid']}\n";
echo " PDF: {$data['pdf_url']}\n";
echo " XML: {$data['xml_url']}\n";
return $data;
} catch (Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
throw $e;
}
}
// Ejemplo de uso
$invoice = [
'client_id' => 'cliente_id_del_paso_anterior',
'payment_form' => '04',
'payment_method' => 'PUE',
'cfdi_use' => 'G03',
'items' => [
[
'description' => 'Suscripción mensual Premium',
'quantity' => 1,
'unit_price' => 999.00,
'tax_rate' => 0.16,
'product_key' => '84111506',
'unit_key' => 'E48'
]
],
'notes' => 'Gracias por tu compra'
];
createInvoice($client, $invoice);Una vez generada la factura, descarga archivos para almacenar o enviar al cliente.
// downloadFiles.js
const fs = require('fs');
const axios = require('axios');
async function downloadFile(url, filename) {
try {
const response = await axios.get(url, { responseType: 'arraybuffer' });
fs.writeFileSync(filename, response.data);
console.log(`✅ Descargado: ${filename}`);
} catch (error) {
console.error(`❌ Error descargando ${filename}:`, error.message);
}
}
// Uso después de crear factura
const invoiceData = response.data.data; // Del paso anterior
downloadFile(invoiceData.pdf_url, `factura_${invoiceData.uuid}.pdf`);
downloadFile(invoiceData.xml_url, `factura_${invoiceData.uuid}.xml`);{
"error": {
"code": "invalid_rfc",
"message": "RFC no válido o inactivo en padrón SAT"
}
}Solución: Validar RFC antes de crear cliente. gigstack valida contra SAT en tiempo real.
{
"error": {
"code": "client_exists",
"message": "Cliente con este RFC ya existe",
"existing_client_id": "cliente_123"
}
}Solución: Usar existing_client_id para crear factura directamente.
{
"error": {
"code": "invalid_amount",
"message": "El monto debe ser mayor a 0"
}
}Solución: Verificar cálculos antes de enviar request.
Automatizar facturación al recibir pago Stripe:
// server.js
require('dotenv').config();
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const axios = require('axios');
const app = express();
app.use(express.json());
const GIGSTACK_API_KEY = process.env.GIGSTACK_API_KEY;
const GIGSTACK_BASE_URL = process.env.GIGSTACK_BASE_URL;
app.post('/webhooks/stripe', async (req, res) => {
const event = req.body;
if (event.type === 'payment_intent.succeeded') {
const paymentIntent = event.data.object;
// Obtener datos cliente desde metadata
const clientData = {
rfc: paymentIntent.metadata.rfc,
name: paymentIntent.metadata.customer_name,
email: paymentIntent.receipt_email,
postal_code: paymentIntent.metadata.postal_code,
tax_regime: paymentIntent.metadata.tax_regime || '601'
};
// Crear factura automáticamente
try {
const invoiceData = {
client: clientData, // gigstack crea cliente si no existe
payment_form: '04',
payment_method: 'PUE',
cfdi_use: paymentIntent.metadata.cfdi_use || 'G03',
items: [
{
description: paymentIntent.description,
quantity: 1,
unit_price: paymentIntent.amount / 100, // Stripe usa centavos
tax_rate: 0.16,
product_key: '84111506',
unit_key: 'E48'
}
]
};
const response = await axios.post(
`${GIGSTACK_BASE_URL}/invoices`,
invoiceData,
{ headers: { 'Authorization': `Bearer ${GIGSTACK_API_KEY}` } }
);
console.log('✅ Factura generada:', response.data.data.uuid);
} catch (error) {
console.error('❌ Error facturando:', error.response?.data);
}
}
res.json({ received: true });
});
app.listen(3000, () => console.log('🚀 Servidor corriendo en puerto 3000'));Promedio <2 segundos desde request hasta factura timbrada. El endpoint es sincrónico: cuando devuelve respuesta, la factura ya está lista.
Usa API key de test (empieza con gsk_test_...). Facturas en modo test no se envían al SAT.
Sí. Endpoint POST /invoices/{uuid}/cancel con motivo de cancelación.
