Modos de Integración
A continuación veremos cómo implementar los distintos tipos de flujo previamente explicados en la sección Tipos de Flujo.
Cada ejemplo es funcional y listo para usar. Solo necesitás reemplazar la obtención del sessionToken con tu endpoint de backend.
Todos estos ejemplos usan los conceptos explicados en Fundamentos de Integración. Si no los leíste, te recomendamos hacerlo primero.
En todos los flujos, el sessionToken se obtiene desde tu backend, que es el único que conoce tu API Key. El frontend nunca debe exponer la API Key directamente.
URLs de los ambientes
| Ambiente | iframe (integradores) | API (sesiones) |
|---|---|---|
| PREPROD | https://iframe-preprod.lakautac.com.ar | https://web-preprod.lakautac.com.ar |
| PROD | https://iframe.lakautac.com.ar/embed/ | https://www.lakautac.com.ar |
Obtener Session Token (común a todos los flujos)
Antes de inicializar el iframe, tu frontend debe solicitar un token de sesión a tu backend:
// Tu frontend pide el token a TU backend
async function obtenerSessionToken() {
const response = await fetch('/api/firma/session', { method: 'POST' });
const data = await response.json();
return data.sessionToken;
}
Tu backend internamente llama a la API de Lakaut con tu API Key:
// Tu BACKEND (ejemplo en Node.js)
// PREPROD: https://web-preprod.lakautac.com.ar
// PROD: https://www.lakautac.com.ar
const LAKAUT_WEB_URL = process.env.LAKAUT_WEB_URL;
app.post('/api/firma/session', async (req, res) => {
const response = await fetch(
`${LAKAUT_WEB_URL}/api/integration/session/new?id=TU_INTEGRATOR_ID`,
{
method: 'POST',
headers: { 'X-Api-Key': process.env.LAKAUT_API_KEY }
}
);
const data = await response.json();
res.json({ sessionToken: data.tokenSession });
});
Flujo 1: Modal con Carga Manual
HTML del iframe
<div id="modal-overlay" class="modal-overlay">
<div class="modal-content">
<button class="modal-close" onclick="cerrarModalFirma()">✕</button>
<iframe
id="lakaut-firma"
src=""
style="width: 100%; height: 500px; border: none;"
allow="clipboard-read; clipboard-write; camera *; microphone"
></iframe>
</div>
</div>
JavaScript
// PREPROD: https://iframe-preprod.lakautac.com.ar
// PROD: https://iframe.lakautac.com.ar/embed/
const IFRAME_ORIGIN = 'https://iframe-preprod.lakautac.com.ar';
const iframe = document.getElementById('lakaut-firma');
let iframeInicializado = false;
let sessionToken = null;
async function abrirModalFirma() {
// 1. Obtener session token desde tu backend
sessionToken = await obtenerSessionToken();
// 2. Cargar el iframe con el token
iframe.src = `${IFRAME_ORIGIN}/embed/?sessionToken=${sessionToken}`;
document.getElementById('modal-overlay').classList.add('active');
}
function cerrarModalFirma() {
document.getElementById('modal-overlay').classList.remove('active');
}
// Escuchar eventos
window.addEventListener('message', (event) => {
if (event.origin !== IFRAME_ORIGIN) return;
const { type, payload } = event.data;
switch (type) {
case 'lakaut.ready':
// Inicializar solo la primera vez
if (!iframeInicializado) {
iframe.contentWindow.postMessage({
type: 'lakaut.init',
payload: {
nonce: crypto.randomUUID(),
idemKey: crypto.randomUUID(),
sessionToken: sessionToken,
userData: {
dni: '12345678',
cuil: '20-12345678-9',
email: 'usuario@tuempresa.com',
gender: 'M',
phone: '1122334455'
}
}
}, IFRAME_ORIGIN);
iframeInicializado = true;
}
break;
case 'lakaut.handshake.ack':
console.log('Conexión establecida');
break;
case 'lakaut.signature.completed':
console.log('Documento firmado:', payload.signedDocId);
cerrarModalFirma();
// Procesar documento firmado
guardarDocumentoFirmado(payload);
break;
case 'lakaut.error':
console.error('Error:', payload);
break;
}
});
Flujo 2: Modal con Carga Automática
HTML del iframe
<div id="modal-overlay" class="modal-overlay">
<div class="modal-content">
<button class="modal-close" onclick="cerrarModalFirma()">✕</button>
<div id="loading">Cargando documento...</div>
<iframe
id="lakaut-firma"
src=""
style="width: 100%; height: 500px; border: none; display: none;"
allow="clipboard-read; clipboard-write; camera *; microphone"
></iframe>
</div>
</div>
JavaScript
const IFRAME_ORIGIN = 'https://iframe-preprod.lakautac.com.ar';
const iframe = document.getElementById('lakaut-firma');
let contratoActual = null;
let sessionToken = null;
async function firmarContrato(contratoId) {
document.getElementById('modal-overlay').classList.add('active');
// 1. Obtener session token y PDF en paralelo
const [token, pdfBase64] = await Promise.all([
obtenerSessionToken(),
obtenerContratoPDF(contratoId)
]);
sessionToken = token;
contratoActual = { id: contratoId, base64: pdfBase64 };
// 2. Cargar iframe con el token
iframe.src = `${IFRAME_ORIGIN}/embed/?sessionToken=${sessionToken}`;
}
// Escuchar eventos
window.addEventListener('message', (event) => {
if (event.origin !== IFRAME_ORIGIN) return;
const { type, payload } = event.data;
switch (type) {
case 'lakaut.ready':
iframe.contentWindow.postMessage({
type: 'lakaut.init',
payload: {
nonce: crypto.randomUUID(),
idemKey: crypto.randomUUID(),
sessionToken: sessionToken,
userData: {
dni: '12345678',
cuil: '20-12345678-9',
email: 'usuario@tuempresa.com',
gender: 'M',
phone: '1122334455'
}
}
}, IFRAME_ORIGIN);
break;
case 'lakaut.handshake.ack':
console.log('Conexión establecida');
// Cargar el documento automáticamente
if (contratoActual?.base64) {
iframe.contentWindow.postMessage({
type: 'lakaut.load.file',
payload: {
fileName: `Contrato-${contratoActual.id}.pdf`,
mime: 'application/pdf',
base64: contratoActual.base64
}
}, IFRAME_ORIGIN);
document.getElementById('loading').style.display = 'none';
iframe.style.display = 'block';
}
break;
case 'lakaut.signature.completed':
console.log('Documento firmado:', payload.signedDocId);
cerrarModalFirma();
guardarDocumentoFirmado(payload);
break;
case 'lakaut.error':
console.error('Error:', payload);
break;
}
});
// Obtener PDF del backend
async function obtenerContratoPDF(contratoId) {
const response = await fetch(`/api/contratos/${contratoId}/pdf`);
const blob = await response.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(',')[1]);
reader.readAsDataURL(blob);
});
}
Flujo 3: Vista Embebida con Carga Manual
HTML
<div class="header">
<h1>Mis Documentos</h1>
<p>Seleccioná el documento que necesitás firmar</p>
</div>
<div class="firma-section">
<div id="loading">Inicializando firma...</div>
<iframe
id="lakaut-firma"
src=""
style="width: 100%; height: calc(100vh - 200px); border: 1px solid #ddd; border-radius: 8px; display: none;"
allow="clipboard-read; clipboard-write; camera *; microphone"
></iframe>
</div>
JavaScript
const IFRAME_ORIGIN = 'https://iframe-preprod.lakautac.com.ar';
const iframe = document.getElementById('lakaut-firma');
let sessionToken = null;
// Inicializar al cargar la página
window.addEventListener('load', async () => {
sessionToken = await obtenerSessionToken();
iframe.src = `${IFRAME_ORIGIN}/embed/?sessionToken=${sessionToken}`;
});
// Escuchar eventos
window.addEventListener('message', (event) => {
if (event.origin !== IFRAME_ORIGIN) return;
const { type, payload } = event.data;
switch (type) {
case 'lakaut.ready':
iframe.contentWindow.postMessage({
type: 'lakaut.init',
payload: {
nonce: crypto.randomUUID(),
idemKey: crypto.randomUUID(),
sessionToken: sessionToken,
userData: {
dni: '12345678',
cuil: '20-12345678-9',
email: 'usuario@tuempresa.com',
gender: 'M',
phone: '1122334455'
}
}
}, IFRAME_ORIGIN);
document.getElementById('loading').style.display = 'none';
iframe.style.display = 'block';
break;
case 'lakaut.handshake.ack':
console.log('Conexión establecida');
break;
case 'lakaut.signature.completed':
console.log('Documento firmado:', payload.signedDocId);
mostrarNotificacion('¡Documento firmado exitosamente!');
guardarDocumentoFirmado(payload);
break;
case 'lakaut.error':
console.error('Error:', payload);
mostrarNotificacion('Error: ' + payload.message, 'error');
break;
}
});
function guardarDocumentoFirmado(payload) {
fetch('/api/documentos/firmados', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
signedDocId: payload.signedDocId,
sha256: payload.document.sha256,
size: payload.document.size,
fechaFirma: new Date().toISOString()
})
});
}
Flujo 4: Vista Embebida con Carga Automática
HTML
<div class="header">
<h1>Firmar Contrato</h1>
</div>
<div id="info-contrato" class="info-contrato">
<!-- Se llena dinámicamente -->
</div>
<div class="container">
<div id="loading">Preparando documento para firma...</div>
<iframe
id="lakaut-firma"
src=""
style="width: 100%; height: calc(100vh - 300px); border: 1px solid #ddd; border-radius: 8px; display: none;"
allow="clipboard-read; clipboard-write; camera *; microphone"
></iframe>
</div>
JavaScript
const IFRAME_ORIGIN = 'https://iframe-preprod.lakautac.com.ar';
const iframe = document.getElementById('lakaut-firma');
let contratoData = null;
let sessionToken = null;
// Obtener ID del contrato de la URL
const urlParams = new URLSearchParams(window.location.search);
const contratoId = urlParams.get('id') || 'CONTRATO-001';
// Cargar datos del contrato al iniciar
window.addEventListener('load', async () => {
try {
// Obtener session token, información del contrato y PDF en paralelo
const [token, contrato, pdfBase64] = await Promise.all([
obtenerSessionToken(),
fetch(`/api/contratos/${contratoId}`).then(r => r.json()),
obtenerContratoPDF(contratoId)
]);
sessionToken = token;
// Mostrar información
document.getElementById('info-contrato').innerHTML = `
<h2>${contrato.titulo}</h2>
<p><strong>Número:</strong> ${contrato.numero}</p>
`;
contratoData = {
id: contratoId,
base64: pdfBase64,
nombre: contrato.titulo
};
// Cargar iframe con el token
iframe.src = `${IFRAME_ORIGIN}/embed/?sessionToken=${sessionToken}`;
} catch (error) {
console.error('Error cargando contrato:', error);
document.getElementById('loading').innerHTML =
'<p style="color: red;">Error cargando el contrato.</p>';
}
});
// Escuchar eventos
window.addEventListener('message', (event) => {
if (event.origin !== IFRAME_ORIGIN) return;
const { type, payload } = event.data;
switch (type) {
case 'lakaut.ready':
iframe.contentWindow.postMessage({
type: 'lakaut.init',
payload: {
nonce: crypto.randomUUID(),
idemKey: crypto.randomUUID(),
sessionToken: sessionToken,
userData: {
dni: '12345678',
cuil: '20-12345678-9',
email: 'usuario@tuempresa.com',
gender: 'M',
phone: '1122334455'
}
}
}, IFRAME_ORIGIN);
break;
case 'lakaut.handshake.ack':
console.log('Conexión establecida');
// Cargar el documento automáticamente
if (contratoData?.base64) {
iframe.contentWindow.postMessage({
type: 'lakaut.load.file',
payload: {
fileName: `${contratoData.nombre}.pdf`,
mime: 'application/pdf',
base64: contratoData.base64
}
}, IFRAME_ORIGIN);
document.getElementById('loading').style.display = 'none';
iframe.style.display = 'block';
}
break;
case 'lakaut.signature.completed':
console.log('Documento firmado:', payload.signedDocId);
guardarDocumentoFirmado(payload);
window.location.href = `/contratos/firmado?id=${contratoData.id}`;
break;
case 'lakaut.error':
console.error('Error:', payload);
break;
}
});
async function obtenerContratoPDF(contratoId) {
const response = await fetch(`/api/contratos/${contratoId}/pdf`);
const blob = await response.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(',')[1]);
reader.readAsDataURL(blob);
});
}
function guardarDocumentoFirmado(payload) {
fetch('/api/contratos/firmados', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contratoId: contratoData.id,
signedDocId: payload.signedDocId,
sha256: payload.document.sha256,
size: payload.document.size,
fechaFirma: new Date().toISOString()
})
});
}
Comparativa de Flujos
| Flujo | Visualización | Carga | Ideal para |
|---|---|---|---|
| 1 | Modal | Manual | Firma rápida ad-hoc |
| 2 | Modal | Automática | Firma de contrato específico sin salir del contexto |
| 3 | Embebida | Manual | Sección dedicada de documentos |
| 4 | Embebida | Automática | Página de revisión y firma de documento específico |
Convertir PDF a Base64
Si necesitás cargar documentos automáticamente, podés utilizar estas funciones auxiliares:
// Desde un archivo File (input type="file")
async function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
// Remover el prefijo "data:application/pdf;base64,"
const base64 = reader.result.split(',')[1];
resolve(base64);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// Desde una URL (tu backend)
async function fetchPdfAsBase64(url) {
const response = await fetch(url);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(',')[1]);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}