assure/Contestation/Demandereconnaissancefacialeia.php
2026-02-22 10:08:39 +00:00

514 lines
16 KiB
PHP
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>INTER-SANTÉ - Vérification d'identité</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 600px;
width: 100%;
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 28px;
margin-bottom: 10px;
}
.header p {
opacity: 0.9;
}
.content {
padding: 40px 30px;
}
.step {
display: none;
}
.step.active {
display: block;
}
.video-container {
position: relative;
background: #000;
border-radius: 15px;
overflow: hidden;
margin: 20px 0;
}
#video, #canvas {
width: 100%;
display: block;
border-radius: 15px;
}
#canvas {
display: none;
}
.face-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 250px;
height: 300px;
border: 3px dashed rgba(255, 255, 255, 0.7);
border-radius: 50%;
pointer-events: none;
}
.instructions {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
}
.instructions h3 {
color: #2c3e50;
margin-bottom: 15px;
display: flex;
align-items: center;
}
.instructions h3::before {
content: "";
margin-right: 10px;
}
.instructions ul {
list-style: none;
padding-left: 0;
}
.instructions li {
padding: 8px 0;
padding-left: 25px;
position: relative;
}
.instructions li::before {
content: "✓";
position: absolute;
left: 0;
color: #27ae60;
font-weight: bold;
}
.button {
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
color: white;
border: none;
padding: 15px 30px;
font-size: 16px;
border-radius: 10px;
cursor: pointer;
width: 100%;
margin: 10px 0;
transition: transform 0.2s, box-shadow 0.2s;
font-weight: 600;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4);
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.button.capture {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
}
.button.retry {
background: linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%);
}
.status {
padding: 15px;
border-radius: 10px;
margin: 20px 0;
text-align: center;
font-weight: 500;
}
.status.info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.status.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status.warning {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeeba;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.preview-image {
max-width: 100%;
border-radius: 15px;
margin: 20px 0;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.match-result {
text-align: center;
padding: 30px;
}
.match-result .icon {
font-size: 80px;
margin-bottom: 20px;
}
.match-result h2 {
color: #2c3e50;
margin-bottom: 15px;
}
.countdown {
font-size: 14px;
color: #7f8c8d;
text-align: center;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🏥 INTER-SANTÉ</h1>
<p>Vérification d'identité sécurisée</p>
</div>
<div class="content">
<!-- Étape 1: Chargement -->
<div id="step-loading" class="step active">
<div class="loader"></div>
<p style="text-align: center; color: #7f8c8d;">Vérification du lien...</p>
</div>
<!-- Étape 2: Instructions -->
<div id="step-instructions" class="step">
<div class="instructions">
<h3>Instructions pour la vérification</h3>
<ul>
<li>Positionnez votre visage dans l'ovale</li>
<li>Assurez-vous d'être dans un endroit bien éclairé</li>
<li>Regardez directement la caméra</li>
<li>Restez immobile lors de la capture</li>
<li>Retirez lunettes de soleil, casquette ou masque</li>
</ul>
</div>
<button class="button" onclick="startCamera()">📸 Démarrer la caméra</button>
</div>
<!-- Étape 3: Capture -->
<div id="step-capture" class="step">
<div class="video-container">
<video id="video" autoplay playsinline></video>
<canvas id="canvas"></canvas>
<div class="face-overlay"></div>
</div>
<div id="camera-status" class="status info">
Positionnez votre visage dans l'ovale
</div>
<button class="button capture" onclick="capturePhoto()">📷 Prendre la photo</button>
</div>
<!-- Étape 4: Confirmation -->
<div id="step-confirm" class="step">
<h3 style="color: #2c3e50; margin-bottom: 15px;">Confirmez votre photo</h3>
<img id="preview" class="preview-image" alt="Votre photo">
<button class="button" onclick="verifyPhoto()">✓ Confirmer et vérifier</button>
<button class="button retry" onclick="retakePhoto()">↻ Reprendre la photo</button>
</div>
<!-- Étape 5: Vérification en cours -->
<div id="step-verifying" class="step">
<div class="loader"></div>
<p style="text-align: center; color: #7f8c8d; margin-top: 20px;">
Vérification de votre identité en cours...<br>
<small>Merci de patienter</small>
</p>
</div>
<!-- Étape 6: Résultat -->
<div id="step-result" class="step">
<div class="match-result">
<div class="icon" id="result-icon"></div>
<h2 id="result-title"></h2>
<p id="result-message"></p>
</div>
<div id="countdown" class="countdown"></div>
</div>
<!-- Étape 7: Erreur -->
<div id="step-error" class="step">
<div class="status error">
<h3>❌ Erreur</h3>
<p id="error-message"></p>
</div>
</div>
</div>
</div>
<script>
let video = document.getElementById('video');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
let stream = null;
let verificationToken = null;
let capturedImage = null;
// Récupérer le token depuis l'URL
const urlParams = new URLSearchParams(window.location.search);
verificationToken = urlParams.get('token');
// Initialisation
window.onload = function() {
if (!verificationToken) {
showError('Lien de vérification invalide');
return;
}
// Vérifier la validité du token
validateToken();
};
function validateToken() {
fetch('verify_facial_api.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'validate_token',
token: verificationToken
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
showStep('step-instructions');
} else {
showError(data.message || 'Lien expiré ou invalide');
}
})
.catch(error => {
showError('Erreur de connexion au serveur');
});
}
function showStep(stepId) {
document.querySelectorAll('.step').forEach(step => {
step.classList.remove('active');
});
document.getElementById(stepId).classList.add('active');
}
function showError(message) {
document.getElementById('error-message').textContent = message;
showStep('step-error');
}
async function startCamera() {
try {
stream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'user',
width: { ideal: 1280 },
height: { ideal: 720 }
}
});
video.srcObject = stream;
showStep('step-capture');
} catch (error) {
console.error('Erreur caméra:', error);
showError('Impossible d\'accéder à la caméra. Veuillez autoriser l\'accès.');
}
}
function capturePhoto() {
// Configurer le canvas
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// Capturer l'image
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// Convertir en base64
capturedImage = canvas.toDataURL('image/jpeg', 0.9);
// Afficher l'aperçu
document.getElementById('preview').src = capturedImage;
// Arrêter la caméra
stopCamera();
// Passer à l'étape de confirmation
showStep('step-confirm');
}
function retakePhoto() {
capturedImage = null;
startCamera();
}
function stopCamera() {
if (stream) {
stream.getTracks().forEach(track => track.stop());
stream = null;
}
}
function verifyPhoto() {
showStep('step-verifying');
fetch('verify_facial_api.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'verify_face',
token: verificationToken,
image: capturedImage
})
})
.then(response => response.json())
.then(data => {
if (data.success && data.match) {
showSuccess(data);
} else {
showFailure(data);
}
})
.catch(error => {
showError('Erreur lors de la vérification: ' + error.message);
});
}
function showSuccess(data) {
document.getElementById('result-icon').textContent = '✅';
document.getElementById('result-title').textContent = 'Identité vérifiée !';
document.getElementById('result-title').style.color = '#27ae60';
document.getElementById('result-message').innerHTML =
`Votre identité a été confirmée avec succès.<br>
Confiance: ${data.confidence}%<br><br>
<strong>Vous pouvez maintenant accéder à vos prestations.</strong>`;
showStep('step-result');
// Redirection automatique après 5 secondes
startCountdown(5, () => {
window.location.href = data.redirect_url || 'dashboard.php';
});
}
function showFailure(data) {
document.getElementById('result-icon').textContent = '❌';
document.getElementById('result-title').textContent = 'Vérification échouée';
document.getElementById('result-title').style.color = '#e74c3c';
document.getElementById('result-message').innerHTML =
`${data.message || 'Votre visage ne correspond pas à notre base de données.'}<br><br>
Si vous pensez qu'il s'agit d'une erreur, veuillez contacter notre service client.<br>
<strong>Tentatives restantes: ${data.attempts_remaining || 0}</strong>`;
showStep('step-result');
// Permettre une nouvelle tentative si disponible
if (data.attempts_remaining > 0) {
setTimeout(() => {
location.reload();
}, 5000);
}
}
function startCountdown(seconds, callback) {
let remaining = seconds;
const countdownEl = document.getElementById('countdown');
const interval = setInterval(() => {
countdownEl.textContent = `Redirection dans ${remaining} secondes...`;
remaining--;
if (remaining < 0) {
clearInterval(interval);
callback();
}
}, 1000);
}
// Nettoyer la caméra quand on quitte la page
window.onbeforeunload = function() {
stopCamera();
};
</script>
</body>
</html>