<!
DOCTYPE html>
<html lang="es-MX">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Validador CFDI con QR | SAT</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/qrcode.min.js"></script>
<style>
:root {
--sat-blue: #0f3964;
--sat-gold: #f6b01e;
}
.header-sat {
background: linear-gradient(135deg, var(--sat-blue) 0%, #1a5a92 100%);
color: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
.btn-sat {
background-color: var(--sat-blue);
color: white;
transition: all 0.3s;
}
.btn-sat:hover {
background-color: #1a5a92;
transform: translateY(-2px);
}
.logo-sat {
height: 70px;
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
}
.qr-container {
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
display: inline-block;
margin: 0 auto;
}
.status-badge {
font-size: 1rem;
padding: 8px 12px;
border-radius: 20px;
}
.detail-card {
border-left: 4px solid var(--sat-blue);
transition: transform 0.3s;
}
.detail-card:hover {
transform: translateX(5px);
}
</style>
</head>
<body>
<header class="header-sat py-3 sticky-top">
<div class="container">
<div class="row align-items-center">
<div class="col-md-1">
<img
src="https://upload.wikimedia.org/wikipedia/commons/3/39/Logo_SAT_2019.png" alt="SAT"
class="logo-sat img-fluid">
</div>
<div class="col-md-11 ps-4">
<h1 class="mb-1">Validador de Comprobantes Fiscales</h1>
<p class="mb-0 opacity-75">Servicio de Verificación de CFDI con Código QR</p>
</div>
</div>
</div>
</header>
<main class="container my-4">
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card border-0 shadow-lg">
<div class="card-header bg-white py-3 border-bottom">
<div class="d-flex justify-content-between align-items-center">
<h2 class="h5 mb-0 text-sat-blue">
<i class="bi bi-receipt me-2"></i>Consulta de CFDI
</h2>
<span class="badge bg-sat-gold text-dark">v3.1</span>
</div>
</div>
<div class="card-body">
<form id="cfdiForm" class="needs-validation" novalidate>
<div class="row g-3">
<div class="col-md-12">
<label for="uuid" class="form-label fw-bold">Folio Fiscal (UUID)</label>
<div class="input-group">
<span class="input-group-text bg-light">
<i class="bi bi-upc-scan"></i>
</span>
<input type="text" class="form-control form-control-lg" id="uuid"
required
value="AAA112EC-34D0-411F-8BFD-E138402C34B4"
pattern="[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]
{12}">
<button class="btn btn-outline-secondary" type="button"
id="pasteUuid">
<i class="bi bi-clipboard"></i> Pegar
</button>
</div>
<div class="invalid-feedback">
Ingrese un UUID válido (32 caracteres alfanuméricos con guiones)
</div>
</div>
<div class="col-md-6">
<label for="rfcEmisor" class="form-label fw-bold">RFC del
Emisor</label>
<input type="text" class="form-control" id="rfcEmisor" required
value="ASA960508FRA"
pattern="[A-ZÑ&]{3,4}[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[A-
Z0-9]{2}[0-9A]">
<div class="invalid-feedback">
RFC emisor inválido
</div>
</div>
<div class="col-md-6">
<label for="rfcReceptor" class="form-label fw-bold">RFC del
Receptor</label>
<input type="text" class="form-control" id="rfcReceptor" required
value="AAAF620901477"
pattern="[A-ZÑ&]{3,4}[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[A-
Z0-9]{2}[0-9A]">
<div class="invalid-feedback">
RFC receptor inválido
</div>
</div>
<div class="col-md-12">
<label for="total" class="form-label fw-bold">Total del
Comprobante</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" step="0.01" class="form-control" id="total"
required
value="147901.16">
<span class="input-group-text">MXN</span>
</div>
</div>
</div>
<div class="d-grid gap-2 mt-4">
<button type="submit" class="btn btn-sat btn-lg py-3">
<i class="bi bi-qr-code me-2"></i>Validar y Generar Código QR
</button>
</div>
</form>
<hr class="my-4">
<div id="resultSection">
<div class="d-flex justify-content-between align-items-center mb-3">
<h3 class="h5 mb-0">
<i class="bi bi-file-earmark-check me-2"></i>Resultado de Validación
</h3>
<span class="badge status-badge bg-success">
<i class="bi bi-check-circle me-1"></i>CFDI VÁLIDO
</span>
</div>
<div class="row">
<div class="col-md-8">
<div class="card detail-card h-100">
<div class="card-body">
<h4 class="h6 fw-bold text-sat-blue mb-3">
<i class="bi bi-receipt me-2"></i>Detalles del Comprobante
</h4>
<div class="row">
<div class="col-md-6">
<p class="mb-2"><strong
class="text-muted">UUID:</strong><br>
<span class="text-break">AAA112EC-34D0-411F-8BFD-
E138402C34B4</span>
</p>
<p class="mb-2"><strong class="text-muted">Fecha
Emisión:</strong><br>
25/04/2024 12:34:56
</p>
<p class="mb-2"><strong class="text-muted">Certificado
SAT:</strong><br>
<span
class="text-success">00001000000509123456</span>
</p>
</div>
<div class="col-md-6">
<p class="mb-2"><strong class="text-muted">RFC
Emisor:</strong><br>
ASA960508FRA
</p>
<p class="mb-2"><strong class="text-muted">RFC
Receptor:</strong><br>
AAAF620901477
</p>
<p class="mb-2"><strong
class="text-muted">Total:</strong><br>
$147,901.16 MXN
</p>
</div>
</div>
<div class="alert alert-success mt-3">
<i class="bi bi-info-circle me-2"></i>Este comprobante se
encuentra registrado y vigente en el sistema del SAT.
</div>
</div>
</div>
</div>
<div class="col-md-4 mt-3 mt-md-0">
<div class="card h-100">
<div class="card-body text-center">
<h4 class="h6 fw-bold text-sat-blue mb-3">
<i class="bi bi-qr-code me-2"></i>Código de Verificación
</h4>
<div class="qr-container mb-3">
<div id="qrCode"></div>
</div>
<p class="small text-muted mb-0">
Escanee este código para verificar la autenticidad del
comprobante en el portal del SAT
</p>
<button class="btn btn-sm btn-outline-primary mt-3"
id="downloadQR">
<i class="bi bi-download me-1"></i>Descargar QR
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-4 text-center">
<div class="alert alert-light border">
<p class="mb-0 small text-muted">
<i class="bi bi-shield-lock me-1"></i>Este servicio es únicamente para
consulta.
Para validación oficial utilice el
<a href="https://verificacfdi.facturaelectronica.sat.gob.mx/"
target="_blank">portal del SAT</a>.
</p>
</div>
</div>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></
script>
<script>
// Generar QR al cargar la página
document.addEventListener('DOMContentLoaded', function() {
const qrData = generateSatUrl();
generateQRCode(qrData);
// Configurar botón de descarga
document.getElementById('downloadQR').addEventListener('click', downloadQR);
// Configurar botón de pegar UUID
document.getElementById('pasteUuid').addEventListener('click', async () => {
try {
const text = await navigator.clipboard.readText();
document.getElementById('uuid').value = text;
} catch (err) {
alert('No se pudo acceder al portapapeles');
}
});
});
function generateSatUrl() {
const uuid = document.getElementById('uuid').value;
const rfcEmisor = document.getElementById('rfcEmisor').value;
const rfcReceptor = document.getElementById('rfcReceptor').value;
const total = document.getElementById('total').value;
return `https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx?` +
`id=${uuid}&re=${rfcEmisor}&rr=${rfcReceptor}&tt=${total}`;
}
function generateQRCode(data) {
const qrCodeElement = document.getElementById('qrCode');
qrCodeElement.innerHTML = '';
new QRCode(qrCodeElement, {
text: data,
width: 180,
height: 180,
colorDark: "#0f3964",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
}
function downloadQR() {
const canvas = document.querySelector('#qrCode canvas');
const link = document.createElement('a');
link.download = `CFDI-${document.getElementById('uuid').value}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
}
// Validación de formulario
document.getElementById('cfdiForm').addEventListener('submit', function(e) {
e.preventDefault();
if (this.checkValidity()) {
const qrData = generateSatUrl();
generateQRCode(qrData);
// Mostrar sección de resultados con animación
const resultSection = document.getElementById('resultSection');
resultSection.style.opacity = 0;
resultSection.style.display = 'block';
setTimeout(() => {
resultSection.style.transition = 'opacity 0.5s';
resultSection.style.opacity = 1;
}, 100);
// Scroll a resultados
resultSection.scrollIntoView({ behavior: 'smooth' });
}
this.classList.add('was-validated');
});
</script>
</body>
</html>