Skip to content

Node.js + Express

Esta receta muestra el flujo completo en una aplicación Express: autenticar, crear un cliente, emitir una factura y manejar el webhook de resultado.

Emitia-Version: 2026-01-01


Terminal window
npm install express
# Variables de entorno recomendadas:
# EMITIA_API_KEY=sk_test_TU_CLAVE
# EMITIA_WEBHOOK_SECRET=whsec_...

emitia-integration.ts
import crypto from 'crypto';
import express from 'express';
const API_BASE = 'https://api.emitia.co/v1';
const EMITIA_VERSION = '2026-01-01';
// Helper: request autenticado
async function emitia(
method: string,
path: string,
body?: object,
idempotencyKey?: string,
): Promise<unknown> {
const headers: Record<string, string> = {
Authorization: `Bearer ${process.env.EMITIA_API_KEY}`,
'Emitia-Version': EMITIA_VERSION,
};
if (body) headers['Content-Type'] = 'application/json';
if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;
const res = await fetch(`${API_BASE}${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json();
if (!res.ok) throw Object.assign(new Error('Emitia API error'), { status: res.status, data });
return data;
}
// 1. Verificar autenticación (funciona hoy)
async function verifyAuth() {
const account = await emitia('GET', '/account');
console.log('Conectado como:', (account as { name: string }).name);
return account;
}
// 2. Crear cliente (disponible con api-resources)
async function createCustomer(nit: string, legalName: string) {
return emitia(
'POST',
'/customers',
{
organization_type: '1',
identification: { type: '31', number: nit, check_digit: '1' },
legal_name: legalName,
email: 'facturacion@empresa.co',
address: 'Cra 1 # 1-1',
city_code: '11001',
department_code: '11',
country_code: 'CO',
},
crypto.randomUUID(), // Idempotency-Key
);
}
// 3. Crear factura (disponible con api-resources + test-mode-sandbox)
async function createInvoice(customerId: string, orderId: string) {
return emitia(
'POST',
'/invoices',
{
customer_id: customerId,
currency: 'COP',
operation_type: '10',
payment_means: { code: '10', type: '1' },
lines: [
{
description: 'Servicio de consultoría',
quantity: '1.00',
unit_code: '94',
unit_price: '100000.00',
line_extension_amount: '100000.00',
taxes: [{ code: '01', rate: '19.00', base: '100000.00', value: '19000.00' }],
},
],
metadata: { order_id: orderId },
},
crypto.randomUUID(), // Genera UNA SOLA VEZ y guarda si necesitas reintentar
);
}
// 4. Verificar firma del webhook
function verifyWebhook(rawBody: string, signature: string, secret: string): boolean {
const parts = Object.fromEntries(signature.split(',').map((s) => s.split('=')));
const t = parseInt(parts['t'] ?? '', 10);
const v1 = parts['v1'];
if (!t || !v1) return false;
if (Math.abs(Date.now() / 1000 - t) > 300) return false; // 5 min tolerancia
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(v1, 'hex'));
}
// --- Servidor Express ---
const app = express();
app.post('/hooks/emitia', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['emitia-signature'] as string ?? '';
const secret = process.env.EMITIA_WEBHOOK_SECRET ?? '';
if (!verifyWebhook(req.body.toString(), signature, secret)) {
return res.status(401).json({ error: 'Firma inválida' });
}
const event = JSON.parse(req.body.toString()) as { type: string; data: { object: { id: string; cufe?: string } } };
switch (event.type) {
case 'invoice.accepted_by_dian':
console.log('Factura aceptada:', event.data.object.id, 'CUFE:', event.data.object.cufe);
// Actualizar estado en tu DB
break;
case 'invoice.rejected_by_dian':
console.error('Factura rechazada:', event.data.object.id);
// Notificar al usuario
break;
}
res.status(200).json({ received: true });
});
app.listen(3001, () => console.log('Webhook listener en puerto 3001'));
// Ejemplo de uso
verifyAuth()
.then(() => createCustomer('900111222', 'Acme SAS'))
.then((customer) => createInvoice((customer as { id: string }).id, 'order-42'))
.then((invoice) => console.log('Factura creada:', invoice))
.catch(console.error);

  • Registra tu webhook endpoint en POST /v1/webhook-endpoints
  • Configura las variables de entorno en producción
  • Lee Conceptos: Idempotencia para entender el retry pattern