Saltar al contenido principal

Fundamentos de Integración

Esta guía explica los conceptos básicos que todos los flujos de integración comparten. Independientemente del tipo de flujo que elijas (modal, embebido, con carga manual o automática), estos fundamentos son necesarios.

tip

Después de leer esta guía, consultá Modos de Integración para ver ejemplos completos de cada flujo.


URLs de los ambientes

AmbienteIframe (integradores)API (sesiones)
PREPRODhttps://iframe-preprod.lakautac.com.arhttps://web-preprod.lakautac.com.ar
PRODhttps://iframe.lakautac.com.ar/embed/https://www.lakautac.com.ar
URLs de producción

Las URLs de producción serán diferentes a las de preproducción. Se comunicarán antes del lanzamiento. Asegurate de que tu integración use variables de configuración para cambiarlas fácilmente.


1. Obtener un Session Token

Antes de cargar el iframe, tu backend debe solicitar un token de sesión efímero. Esto evita exponer tu API Key en el frontend.

Tu Backend                        API Lakaut
│ │
│── POST /api/integration/session/new?id=TU_INTEGRATOR_ID
│ Header: X-Api-Key: TU_API_KEY │
│ │
│<── { tokenSession: "abc-123..." } ─│
│ │
// En tu BACKEND (Node.js, Java, Python, etc.)
// ⚠️ NUNCA hagas esto desde el frontend

// PREPROD: https://web-preprod.lakautac.com.ar
// PROD: https://www.lakautac.com.ar
const LAKAUT_WEB_URL = process.env.LAKAUT_WEB_URL;

async function crearSesionFirma() {
const response = await fetch(
`${LAKAUT_WEB_URL}/api/integration/session/new?id=TU_INTEGRATOR_ID`,
{
method: 'POST',
headers: { 'X-Api-Key': 'TU_API_KEY' } // ← solo en el backend
}
);
const data = await response.json();
return data.tokenSession; // Token efímero de 30 minutos
}

Tu frontend solicita el token a tu propio backend, nunca directamente a Lakaut:

// En tu FRONTEND
const sessionToken = await fetch('/api/firma/session', { method: 'POST' })
.then(r => r.json())
.then(d => d.sessionToken);
Importante

Nunca expongas tu API Key en el frontend. La API Key debe vivir exclusivamente en tu backend. El frontend solo maneja el sessionToken efímero.


2. Incluir el iframe

Una vez obtenido el sessionToken, embebé el iframe:

<!--
PREPROD: https://iframe-preprod.lakautac.com.ar/embed/
PROD: https://iframe.lakautac.com.ar/embed
-->
<iframe
id="lakaut-firma"
src="https://iframe-preprod.lakautac.com.ar/embed/?sessionToken=TU_SESSION_TOKEN"
style="width: 100%; height: 600px; border: none;"
allow="clipboard-read; clipboard-write; camera *; microphone"
></iframe>

Parámetros del iframe

ParámetroDescripción
idIdentificador para acceder al iframe desde JavaScript
srcURL del iframe. Incluye el sessionToken obtenido de tu backend
allowPermisos necesarios para el funcionamiento del clipboard y cámara (biometría)
Tamaño recomendado

Mínimo 400px de ancho y 500px de alto para una experiencia óptima.


3. Inicializar la comunicación

El iframe envía un mensaje lakaut.ready cuando está listo. Podés escucharlo o usar iframe.onload:

// Función helper para generar UUIDs con fallback
function uuidv4() {
if (crypto?.randomUUID) {
return crypto.randomUUID();
}
// Fallback RFC4122 v4
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = crypto.getRandomValues(new Uint8Array(1))[0] & 15;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}

// PREPROD: https://iframe-preprod.lakautac.com.ar
// PROD: https://www.lakautac.com.ar
const IFRAME_ORIGIN = 'https://iframe-preprod.lakautac.com.ar';

const iframe = document.getElementById('lakaut-firma');

// Opción 1: Escuchar lakaut.ready (recomendado)
window.addEventListener('message', (event) => {
if (event.origin !== IFRAME_ORIGIN) return;

if (event.data.type === 'lakaut.ready') {
enviarInit();
}
});

// Opción 2: Usar onload (alternativa)
iframe.onload = () => {
enviarInit();
};

function enviarInit() {
iframe.contentWindow.postMessage({
type: 'lakaut.init',
payload: {
nonce: uuidv4(),
idemKey: uuidv4(),
sessionToken: 'TU_SESSION_TOKEN', // Obtenido de tu backend
userData: {
dni: '12345678',
cuil: '20-12345678-9',
email: 'usuario@ejemplo.com',
gender: 'M', // 'M', 'F' o 'X'
phone: '1122334455',
name: 'Juan Pérez',
address: 'Av. Corrientes 1234, CABA'
}
}
}, IFRAME_ORIGIN);
}

Campos del mensaje de inicialización

CampoTipoRequeridoDescripción
noncestringIdentificador único para esta sesión. Usar crypto.randomUUID()
idemKeystringClave de idempotencia para evitar firmas duplicadas
sessionTokenstringToken de sesión efímero obtenido desde tu backend
userData.dnistringDNI del firmante (8 dígitos). Alternativa a CUIL
userData.cuilstringCUIL del firmante en formato XX-XXXXXXXX-X
userData.emailstringEmail del firmante
userData.genderstringGénero: 'M' (masculino), 'F' (femenino) o 'X' (otro)
userData.phonestringNúmero de teléfono del firmante (10 dígitos)
userData.namestringNombre completo del firmante
userData.addressstringDomicilio del firmante
Migración desde integratorId

Si venías usando integratorId y apiKey en el payload de lakaut.init, reemplazalos por sessionToken. El iframe resuelve internamente el integrador y la API Key a partir del token de sesión.


4. Escuchar eventos del iframe

Configurá un listener para recibir los eventos:

window.addEventListener('message', (event) => {
// IMPORTANTE: Validar el origen del mensaje
// PREPROD: https://iframe-preprod.lakautac.com.ar
// PROD: https://iframe.lakautac.com.ar/embed/
if (event.origin !== IFRAME_ORIGIN) return;

const { type, payload } = event.data;

switch (type) {
case 'lakaut.ready':
// Iframe cargado y listo para recibir init
console.log('Iframe listo');
break;

case 'lakaut.handshake.ack':
// Conexión establecida exitosamente
console.log('Handshake completado ✅');
break;

case 'lakaut.signature.completed':
// Documento firmado exitosamente
handleSignatureCompleted(payload);
break;

case 'lakaut.error':
// Error durante el proceso
handleError(event.data);
break;
}
});

Eventos principales

EventoDescripciónCuándo se dispara
lakaut.readyIframe listo para recibir configuraciónAl cargar el iframe
lakaut.handshake.ackConfirmación de conexiónDespués de enviar lakaut.init
lakaut.signature.completedFirma completadaCuando el usuario firma exitosamente
lakaut.errorError en el procesoCuando ocurre algún error
Validación de origen

Siempre validá que event.origin coincida con la URL del iframe para evitar ataques de seguridad.


5. Manejar el documento firmado

Cuando se completa la firma, recibís el evento lakaut.signature.completed:

function handleSignatureCompleted(payload) {
const { signedDocId, document, delivery } = payload;

// El PDF firmado viene en Base64
if (delivery.mode === 'binary') {
const blob = base64ToBlob(delivery.fileBase64, document.mime);

// Descargar el archivo
downloadBlob(blob, `documento-firmado-${signedDocId}.pdf`);
}

// Guardar referencia en tu sistema
guardarEnBaseDeDatos({
id: signedDocId,
fileName: document.fileName,
sha256: document.sha256,
size: document.size,
fechaFirma: new Date()
});
}
Sobre signedDocId y document.fileName
  • document.fileName puede venir en el payload como el nombre del archivo firmado.
  • signedDocId representa el identificador del documento firmado. En la práctica, según el flujo interno, puede venir como un ID real o como un identificador basado en nombre/handle. Por eso, si querés mostrar un nombre de archivo al usuario, usá document.fileName cuando esté disponible.

Estructura del payload

CampoDescripción
eventIdID único del evento de firma
idemKeyClave de idempotencia (si se envió en init)
signedDocIdID único del documento firmado
document.fileNameNombre del archivo firmado
document.sha256Hash SHA-256 del documento
document.sizeTamaño del documento en bytes
document.mimeTipo MIME (siempre application/pdf)
delivery.modeModo de entrega: 'binary' o 'url'
delivery.fileBase64Contenido del PDF firmado en Base64 (si mode='binary')
delivery.urlURL de descarga (si mode='url')
Formas de entrega del documento firmado (delivery)

El iframe puede entregar el documento firmado de 2 formas:

  • delivery.mode = 'binary': el PDF firmado viene en delivery.fileBase64 (Base64 sin prefijo).
  • delivery.mode = 'url': el PDF firmado se descarga desde delivery.url.

Tu integración debe contemplar ambos casos (por ejemplo, priorizar binary si viene y usar url como fallback).

Funciones auxiliares

// Convertir Base64 a Blob
function base64ToBlob(base64, mimeType) {
const byteCharacters = atob(base64);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], { type: mimeType });
}

// Descargar Blob
function downloadBlob(blob, filename) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}

6. Cargar documento (opcional)

Tenés dos opciones para cargar el documento a firmar:

Opción A: En el init (recomendado)

Incluí el documento directamente en el mensaje lakaut.init:

iframe.contentWindow.postMessage({
type: 'lakaut.init',
payload: {
// ... otros campos
autoLoadFile: {
fileName: 'contrato-servicio.pdf',
mime: 'application/pdf',
base64: tuDocumentoEnBase64 // PDF en Base64 sin prefijo
}
}
}, IFRAME_ORIGIN);

Opción B: Mensaje separado

Enviá el documento después de recibir lakaut.handshake.ack:

window.addEventListener('message', (event) => {
if (event.origin !== IFRAME_ORIGIN) return;

if (event.data.type === 'lakaut.handshake.ack') {
// Ahora podés cargar el documento
iframe.contentWindow.postMessage({
type: 'lakaut.load.file',
payload: {
fileName: 'contrato-servicio.pdf',
mime: 'application/pdf',
base64: tuDocumentoEnBase64 // PDF en Base64 sin prefijo
}
}, IFRAME_ORIGIN);
}
});
¿Qué hace el iframe con el documento?

El iframe soporta dos mecanismos reales de carga, ambos implementados:

  • autoLoadFile en lakaut.init: el iframe precarga el documento apenas recibe el init (si viene autoLoadFile.base64).
  • lakaut.load.file: permite enviar/cambiar el documento luego del init.

Recomendación: si vas a usar lakaut.load.file, envialo después de recibir lakaut.handshake.ack para mantener el orden del protocolo y simplificar el manejo en tu app.


Flujo de comunicación

Tu Backend                      Tu Frontend                     Iframe Lakaut
│ │ │
│ │ │
│<── POST /api/firma/session ──────│ │
│── { sessionToken } ────────────>│ │
│ │ │
│ │ [Iframe carga con token] │
│ │ │
│ │<─── lakaut.ready ──────────────│
│ │ │
│ │──── lakaut.init ──────────────>│
│ │ (sessionToken + userData) │
│ │ │
│ │<─── lakaut.handshake.ack ──────│
│ │ │
│ │──── lakaut.load.file (opc.) ─>│
│ │ │
│ │ [Usuario firma] │
│ │ │
│ │<─── lakaut.signature.completed │
│ │ │

Errores comunes

ErrorCausaSolución
El iframe no cargaDominio no autorizadoContactá a Lakaut para registrar tu dominio
No recibo lakaut.readyIframe bloqueado por CSPVerificá Content-Security-Policy
No recibo el handshakeOrigen incorrecto en postMessageVerificá que estás usando la URL correcta del ambiente
Error 401Session token expiradoSolicitá un nuevo token desde tu backend
El documento no se cargaEnviaste lakaut.load.file antes del handshakeEsperá el evento lakaut.handshake.ack
Base64 inválidoIncluiste el prefijo data:...Enviá solo el contenido base64 sin prefijo
SESSION_TOKEN_EXPIREDEl token de sesión expiró (30 min)Tu backend debe generar uno nuevo

Próximos pasos

Ahora que conocés los fundamentos, elegí el flujo que mejor se adapte a tu caso: