assure/Contestation/Demandereconnaissancefaciale.php
2026-05-13 17:04:02 +00:00

967 lines
35 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.

<?php
session_start();
session_unset();
require_once 'Csrf.php';
$token = Csrf::generateToken();
$_SESSION['csrf_token_submieted'] = $token;
if (!isset($_GET['lg'])) {
afficherMessage("Paramètre langue absent de la requête!", "Erreur");
}
$lg = $_GET['lg'];
$codeLangue = base64_decode($lg);
$lang = ($codeLangue === 'en_US') ? 'en_US' : 'fr_FR';
/*
var_dump(
array(
"lg" => $lg,
"lang" => $lang,
)
);
*/
$translations = [
'fr_FR' => [
'error' => 'Erreur',
'information' => 'Information',
'missing_language' => 'Paramètre langue absent de la requête !',
'unknown_language' => 'Langue inconnue !',
'missing_entity' => 'Paramètre entité absent de la requête !',
'missing_provider' => 'Paramètre prestataire absent de la requête !',
'missing_insured' => 'ID assuré absent de la requête !',
'missing_request' => 'ID demande absent de la requête !',
'entity_not_found' => 'Entité introuvable !',
'insured_not_found' => 'Assuré introuvable !',
'no_request_found' => 'Aucune demande trouvée !',
'token_used' => 'Token déjà utilisé !',
'request_expired' => 'Demande expirée depuis ',
'verification_done' => 'Vérification déjà effectuée !',
'too_many_attempts' => 'Trop de tentatives !',
'secure_identity_verification' => 'Vérification d\'identité sécurisée',
'provider' => 'Prestataire',
'patient' => 'Patient',
'counter' => 'Compteur',
'attempts_on' => 'tentative(s) sur',
'loading_request' => 'Vérification du lien...',
'verification_instructions' => 'Instructions pour la vérification',
'instruction_1' => 'Positionnez votre visage dans l\'ovale',
'instruction_2' => 'Assurez-vous d\'être dans un endroit bien éclairé',
'instruction_3' => 'Regardez directement la caméra',
'instruction_4' => 'Restez immobile lors de la capture',
'instruction_5' => 'Retirez lunettes de soleil, casquette ou masque',
'start_camera' => 'Démarrer la caméra',
'position_face' => 'Positionnez votre visage dans l\'ovale',
'take_photo' => 'Prendre la photo',
'confirm_photo' => 'Confirmez votre photo',
'confirm_verify' => 'Confirmer et vérifier',
'retake_photo' => 'Reprendre la photo',
'verification_processing' => 'Vérification de votre identité en cours...',
'please_wait' => 'Merci de patienter',
'identity_verified' => 'Identité vérifiée !',
'identity_confirmed' => 'Votre identité a été confirmée avec succès.',
'confidence' => 'Confiance',
'access_services' => 'Vous pouvez maintenant accéder à vos prestations.',
'verification_failed' => 'Vérification échouée',
'face_not_match' => 'Votre visage ne correspond pas à notre base de données.',
'contact_support' => 'Si vous pensez qu\'il s\'agit d\'une erreur, veuillez contacter notre service client.',
'remaining_attempts' => 'Tentatives restantes',
'redirection_in' => ' secondes avant redirection...',
'invalid_link' => 'Lien de vérification invalide',
'expired_link' => 'Lien expiré ou invalide',
'server_error' => 'Erreur de connexion au serveur',
'camera_error' => 'Impossible d\'accéder à la caméra. Veuillez autoriser l\'accès.',
'verification_error' => 'Erreur lors de la vérification faciale',
],
'en_US' => [
'error' => 'Error',
'information' => 'Information',
'missing_language' => 'Language parameter missing from query!',
'unknown_language' => 'Unknown language!',
'missing_entity' => 'Entity parameter missing from query!',
'missing_provider' => 'Provider parameter missing from query!',
'missing_insured' => 'Insured ID missing from query!',
'missing_request' => 'Request ID missing from query!',
'entity_not_found' => 'Entity not found!',
'insured_not_found' => 'Insured not found!',
'no_request_found' => 'No requests found!',
'token_used' => 'Token already used!',
'request_expired' => 'Request expired since ',
'verification_done' => 'Verification already completed!',
'too_many_attempts' => 'Too many attempts!',
'secure_identity_verification' => 'Secure identity verification',
'provider' => 'Provider',
'patient' => 'Patient',
'counter' => 'Counter',
'attempts_on' => 'attempt(s) out of',
'loading_request' => 'Checking verification link...',
'verification_instructions' => 'Verification instructions',
'instruction_1' => 'Place your face inside the oval',
'instruction_2' => 'Ensure you are in a well-lit place',
'instruction_3' => 'Look directly at the camera',
'instruction_4' => 'Remain still during capture',
'instruction_5' => 'Remove sunglasses, cap, or mask',
'start_camera' => 'Start camera',
'position_face' => 'Position your face inside the oval',
'take_photo' => 'Take photo',
'confirm_photo' => 'Confirm your photo',
'confirm_verify' => 'Confirm and verify',
'retake_photo' => 'Retake photo',
'verification_processing' => 'Identity verification in progress...',
'please_wait' => 'Please wait',
'identity_verified' => 'Identity verified!',
'identity_confirmed' => 'Your identity has been successfully confirmed.',
'confidence' => 'Confidence',
'access_services' => 'You may now access your services.',
'verification_failed' => 'Verification failed',
'face_not_match' => 'Your face does not match our database.',
'contact_support' => 'If you believe this is an error, please contact customer support.',
'remaining_attempts' => 'Remaining attempts',
'redirection_in' => 'seconds before redirection...',
'invalid_link' => 'Invalid verification link',
'expired_link' => 'Expired or invalid link',
'server_error' => 'Server connection error',
'camera_error' => 'Unable to access the camera. Please allow access.',
'verification_error' => 'Error during facial verification',
]
];
function t($key, $lang, $translations)
{
return $translations[$lang][$key] ?? $key;
}
$tab_code_langue = ["fr_FR", "en_US"];
if (!in_array($codeLangue, $tab_code_langue)) {
afficherMessage("Langue inconnue!/Unknown language!", "Erreur");
}
if (!isset($_GET['codeEntite'])) {
$message = t('missing_entity', $lang, $translations);
afficherMessage($message, "Error");
}
if (!isset($_GET['codePrestataire'])) {
$message = t('missing_provider', $lang, $translations);
afficherMessage($message, "Erreur");
}
if (!isset($_GET['idBeneficiaire'])) {
$message = t('missing_insured', $lang, $translations);
afficherMessage($message, "Erreur");
}
if (!isset($_GET['idDemande'])) {
$message = t('missing_request', $lang, $translations);
afficherMessage($message, "Erreur");
}
$_SESSION['codeLangue'] = $lg;
$_SESSION['codeEntite'] = base64_decode($_GET['codeEntite']);
$_SESSION['codePrestataire'] = base64_decode($_GET['codePrestataire']);
$_SESSION['idBeneficiaire'] = base64_decode($_GET['idBeneficiaire']);
$_SESSION['idDemande'] = base64_decode($_GET['idDemande']);
$codeSociete = $_SESSION['codeEntite'];
$idBeneficiaire = $_SESSION['idBeneficiaire'];
$idDemande = $_SESSION['idDemande'];
$codePrestataire = $_SESSION['codePrestataire'];
$codeBdd = $codeSociete;
$_SESSION['idLogapiface'] = 0;
require_once "Assure.php";
$assure = new Assure();
$_SESSION['assure'] = $assure;
$_SESSION['codeBdd'] = $codeBdd;
$bdd = $assure->geUneBd($codeBdd);
if(!$bdd) {
$message = t('entity_not_found', $lang, $translations);
afficherMessage("<strong>$message</strong>", "Erreur");
}
$_SESSION['BdName'] = $bdd['BdName'];
$_SESSION['BdLogin'] = $bdd['BdLogin'];
$_SESSION['BdMdp'] = $bdd['BdMdp'];
if($assure->existeligne($codeSociete)) {
$fassureExiste = $assure->assureExiste($codeSociete, $idBeneficiaire);
if(!$fassureExiste) {
$message = t('insured_not_found', $lang, $translations);
afficherMessage("<strong>$message</strong>", "Erreur");
}
$demande = $assure->checkdemandereconnaissancefaciale_id();
if(!$demande) {
$message = t('no_request_found', $lang, $translations);
afficherMessage("<strong>$message</strong>", "Erreur");
}
$idDemande = $demande["idDemande"];
$codeEtat = $demande["codeEtat"];
$demandeExpiree = $demande["demandeExpiree"];
$dateExpirationFr = $demande["dateExpirationFr"];
$dateExpirationEng = $demande["dateExpirationEng"];
$etatDemande = $demande["etatDemande"];
$etatDemandeEng = $demande["etatDemandeEng"];
$beneficiaire = $demande["beneficiaire"];
$prestataire = $demande["prestataire"];
$lienPhoto = $demande["lienPhoto"];
$attempts = $demande["attempts"];
$_SESSION['lienPhoto'] = $lienPhoto;
$_SESSION['numeroBeneficiaire'] = $demande["numeroBeneficiaire"];
$_SESSION['login'] = $demande["numeroBeneficiaire"];
$_SESSION['attempts'] = $attempts;
/*
codeEtat libelle
6 Token déjà utilisé
3 Expiré
1 Vérifié
2 Echec
4 Erreur
5 Trop de tentatives
0 En attente
*/
if($codeEtat=="6") {
$message = t('token_used', $lang, $translations);
afficherMessage("<strong>$message</strong>", "Information");
}
if($codeEtat=="3" || $demandeExpiree=="1") {
// changer le status
$assure->maj_demandereconnaissancefaciale("3"); // Expirée
$message = t(
'request_expired',
$lang,
$translations
). ($lang == 'en_US' ? $dateExpirationEng : $dateExpirationFr);
afficherMessage("<strong>$message</strong>", "Erreur");
}
if($codeEtat=="1") {
$message = t('verification_done', $lang, $translations);
afficherMessage("<strong>$message</strong>", "Information");
}
$param_societe = $assure->get_parametres_societe($codeSociete);
$_SESSION['dureeToken'] = $param_societe["dureeTokenReconnaissanceFaciale"];
$_SESSION['dossierPhoto'] = $param_societe["dossierPhoto"];
$_SESSION['nbTentative'] = $param_societe["nbTentativeBiometrie"];
$_SESSION['lienPhotoFace'] = $param_societe["lienPhotoFace"];
$maxAttempts = $_SESSION['nbTentative'];
if($attempts>=$maxAttempts) {
// changer le status
$assure->maj_demandereconnaissancefaciale("5"); // Trop de tentatives
$message = t('too_many_attempts', $lang, $translations);
afficherMessage("<strong>$message</strong>", "Erreur");
}
// vérifier que la photo du bénéficiaire existe
if ($_SESSION['lienPhoto']>" ")
{
$photo = $_SESSION['lienPhotoFace'].$_SESSION['lienPhoto'];
if(!file_exists($photo))
{
$_SESSION['lienPhoto'] = "";
}
else
{
$_SESSION['photoAssureCrypte'] = decryptImage($photo);
}
}
}
function afficherMessage($message, $titre) {
echo "<!DOCTYPE html>
<html lang='fr'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='icon' href='Contestation/favicon.ico'/>
<title>$titre</title>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<link href='https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css' rel='stylesheet'>
<style>
.error-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.error-card {
background: white;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
padding: 2rem;
max-width: 500px;
width: 100%;
border-left: 4px solid #dc3545;
}
</style>
</head>
<body class='bg-light'>
<div class='error-container'>
<div class='error-card'>
<div class='text-center mb-3'>
<i class='bi bi-exclamation-triangle-fill text-danger' style='font-size: 3rem;'></i>
</div>
<h4 class='text-center text-danger mb-3'>$titre</h4>
<p class='text-center text-muted'>{$message}</p>
</div>
</div>
</body>
</html>";
exit();
}
// Chiffrer limage au moment de lupload (PHP)
function encryptImage($sourcePath, $destPath)
{
$key = base64_decode(trim(file_get_contents('/var/www/keys/inter-sante-photo.key')));
$plaintext = file_get_contents($sourcePath);
$iv = openssl_random_pseudo_bytes(16);
$cipher = openssl_encrypt($plaintext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
file_put_contents($destPath, $iv . $cipher); // concat IV + données
}
// Déchiffrer pour afficher la photo dans INTER-SANTE
function decryptImage($path)
{
$key = base64_decode(trim(file_get_contents('/var/www/keys/inter-sante-photo.key')));
$data = file_get_contents($path);
$iv = substr($data, 0, 16);
$ciphertext = substr($data, 16);
return base64_encode(openssl_decrypt($ciphertext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv));
}
?>
<!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;*/
padding: 15px;
text-align: center;
}
.header h1 {
font-size: 28px;
margin-bottom: 10px;
}
.header p {
opacity: 0.9;
}
.content {
/*padding: 40px 30px;*/
padding: 5px 15px;
}
.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;
}
.header h3 {
font-size: 14px;
/*margin-bottom: 10px;*/
margin: 10px;
opacity: 0.85;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🏥 INTER-SANTÉ</h1>
<p><?= t('secure_identity_verification', $lang, $translations) ?></p>
<h3><?= t('provider', $lang, $translations) ?> : <?= $prestataire ?></h3>
<h3><?= t('patient', $lang, $translations) ?> : <?= $beneficiaire ?></h3>
<h3>
<?= t('counter', $lang, $translations) ?> :
<?= "$attempts " . t('attempts_on', $lang, $translations) . " $maxAttempts" ?>
</h3>
</div>
<div class="content">
<input type="hidden" id="csrf_token" name="csrf_token" value="<?= htmlspecialchars($token) ?>">
<!-- Étape 1: Chargement -->
<div id="step-loading" class="step active">
<div class="loader"></div>
<p style="text-align: center; color: #7f8c8d;">Demande Vérification du lien...</p>
</div>
<!-- Étape 2: Instructions -->
<div id="step-instructions" class="step">
<div class="instructions">
<h3><?= t('verification_instructions', $lang, $translations) ?> </h3>
<ul>
<li> <?= t('instruction_1', $lang, $translations) ?> </li>
<li> <?= t('instruction_2', $lang, $translations) ?> </li>
<li> <?= t('instruction_3', $lang, $translations) ?> </li>
<li> <?= t('instruction_4', $lang, $translations) ?> </li>
<li> <?= t('instruction_5', $lang, $translations) ?> </li>
</ul>
</div>
<button class="button" onclick="startCamera()">📸 <?= t('start_camera', $lang, $translations) ?> </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">
<?= t('position_face', $lang, $translations) ?>
</div>
<button class="button capture" onclick="capturePhoto()">📷 <?= t('take_photo', $lang, $translations) ?> </button>
</div>
<!-- Étape 4: Confirmation -->
<div id="step-confirm" class="step">
<h3 style="color: #2c3e50; margin-bottom: 15px;"> <?= t('confirm_photo', $lang, $translations) ?> </h3>
<img id="preview" class="preview-image" alt="Votre photo">
<button class="button" onclick="verifyPhoto()">✓ <?= t('confirm_verify', $lang, $translations) ?> </button>
<button class="button retry" onclick="retakePhoto()">↻ <?= t('retake_photo', $lang, $translations) ?> </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;">
<?= t('verification_processing', $lang, $translations) ?> <br>
<small> <?= t('please_wait', $lang, $translations) ?> </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 src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<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');
verificationToken = <?= $_SESSION['idDemande'] ?> ;
v_msg = "<?= t('invalid_link', $lang, $translations) ?>";
// Initialisation
window.onload = function() {
if (!verificationToken) {
showError(v_msg);
return;
}
// Vérifier la validité du token
validateToken();
};
function validateToken() {
v_csrf_token=$("#csrf_token").val();
$.ajax({
url: '/Contestation/verify_facial_api.php',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
action: 'validate_token',
token: verificationToken,
csrf_token: v_csrf_token
}),
dataType: 'json',
success: function(data) {
if (data.success) {
showStep('step-instructions');
} else {
showError(data.message || 'Lien expiré ou invalide');
}
},
error: function(xhr, status, err) {
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() {
v_csrf_token=$("#csrf_token").val();
showStep('step-verifying');
const base64Only = capturedImage.split(',')[1];
$.ajax({
url: '/Contestation/verify_facial_api.php',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
action: 'verify_face',
token: verificationToken,
// image: capturedImage
image: base64Only,
csrf_token: v_csrf_token
}),
dataType: 'json',
success: function(data) {
if (data.success && data.match) {
showSuccess(data);
} else {
showFailure(data);
}
},
error: function(xhr, status, error) {
showError('Erreur lors de la vérification : ' + error);
}
});
}
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 3 secondes
startCountdown(3, () => {
// On recharge la page
location.reload();
});
}
function showFailure(data) {
document.getElementById('result-icon').textContent = '❌';
const v_msg = "<?= t('verification_failed', $lang, $translations) ?>";
const v_msg1 = "<?= t('face_not_match', $lang, $translations) ?>";
const v_msg2 = "<?= t('contact_support', $lang, $translations) ?>";
const v_msg3 = "<?= t('remaining_attempts', $lang, $translations) ?>";
document.getElementById('result-title').textContent = v_msg;
document.getElementById('result-title').style.color = '#e74c3c';
document.getElementById('result-message').innerHTML = `
${data.message || v_msg1}<br><br>
${v_msg2}<br>
<strong>${v_msg3}: ${data.attempts_remaining - 1 || 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 v_lang = "<?= $lang ?>";
const v_msg4 = (v_lang=="en_US") ? `Redirecting in ${remaining} seconds...` : `Redirection dans ${remaining} secondes...`;
const interval = setInterval(() => {
countdownEl.textContent = v_msg4;
remaining--;
if (remaining < 0) {
clearInterval(interval);
callback();
}
}, 1000);
}
// Nettoyer la caméra quand on quitte la page
window.onbeforeunload = function() {
stopCamera();
};
/*
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);
}
*/
</script>
</body>
</html>