Merge branch 'main' of https://git.ebene.ovh/ebene/assure
This commit is contained in:
commit
da7da6456b
|
|
@ -89,11 +89,114 @@ class Assure extends Modelecontestation {
|
|||
$idDemande = $_SESSION['idDemande'];
|
||||
|
||||
$sql = 'call sp_p_checkdemandereconnaissancefaciale_id(?, ?, ?, ?);';
|
||||
$resultat = $this->executerRequete($sql, array($codeSociete, $codePrestataire, $idBeneficiaire, $idDemande));
|
||||
$resultat = $this->executerRequeteAdin($sql, array($codeSociete, $codePrestataire, $idBeneficiaire, $idDemande));
|
||||
$ligne = $resultat->fetch(PDO::FETCH_ASSOC);
|
||||
return $ligne;
|
||||
}
|
||||
|
||||
public function get_nbTentativeBiometrie($codeSociete)
|
||||
{
|
||||
$sql = 'select nbTentativeBiometrie FROM societeuser WHERE (codeSociete=?);';
|
||||
|
||||
$resultat = $this->executerRequeteAdin($sql, array($codeSociete))->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $resultat['nbTentativeBiometrie'];
|
||||
}
|
||||
|
||||
public function get_parametres_societe($codeSociete)
|
||||
{
|
||||
$sql = 'select
|
||||
dureeTokenReconnaissanceFaciale,
|
||||
dossierPhoto,
|
||||
nbTentativeBiometrie,
|
||||
lienPhotoFace
|
||||
FROM societeuser WHERE (codeSociete=?);';
|
||||
|
||||
$resultat = $this->executerRequeteAdin($sql, array($codeSociete))->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $resultat;
|
||||
}
|
||||
|
||||
public function valider_token()
|
||||
{
|
||||
$codeSociete = $_SESSION['codeBdd'];
|
||||
$codePrestataire = $_SESSION['codePrestataire'];
|
||||
$idBeneficiaire = $_SESSION['idBeneficiaire'];
|
||||
$idDemande = $_SESSION['idDemande'];
|
||||
|
||||
$sql = 'call sp_p_demandereconnaissancefaciale_valide(?, ?, ?, ?);';
|
||||
$resultat = $this->executerRequeteAdin($sql, array($codeSociete, $codePrestataire, $idBeneficiaire, $idDemande));
|
||||
$ligne = $resultat->fetch(PDO::FETCH_ASSOC);
|
||||
return $ligne;
|
||||
}
|
||||
|
||||
public function init_confirmerlaface($del)
|
||||
{
|
||||
$codePrestataire = $_SESSION['codePrestataire'];
|
||||
$user = $_SESSION['login'];
|
||||
$username = $_SESSION['numeroBeneficiaire'];
|
||||
|
||||
$sql = 'call sp_init_confirmerlaface(?, ?, ?, ?);';
|
||||
$resultat = $this->executerRequete($sql, array($codePrestataire, $user, $username, $del));
|
||||
$ligne = $resultat->fetch(PDO::FETCH_ASSOC);
|
||||
$_SESSION['idLogapiface'] = $ligne['idLogapiface'];
|
||||
}
|
||||
|
||||
public function init_reconnaitrelaface()
|
||||
{
|
||||
$codePrestataire = $_SESSION['codePrestataire'];
|
||||
$user = $_SESSION['login'];
|
||||
|
||||
$sql = 'call sp_init_reconnaitrelaface(?, ?);';
|
||||
$resultat = $this->executerRequete($sql, array($codePrestataire, $user));
|
||||
$ligne = $resultat->fetch(PDO::FETCH_ASSOC);
|
||||
$_SESSION['idLogapiface'] = $ligne['idLogapiface'];
|
||||
}
|
||||
|
||||
public function succes_reconnaitrelaface($username, $resultat)
|
||||
{
|
||||
$idLogapiface = $_SESSION['idLogapiface'];
|
||||
|
||||
$sql = 'call sp_succes_reconnaitrelaface(?, ?, ?);';
|
||||
$this->executerRequete($sql, array($idLogapiface, $username, $resultat));
|
||||
}
|
||||
|
||||
public function echec_reconnaitrelaface($resultat)
|
||||
{
|
||||
$idLogapiface = $_SESSION['idLogapiface'];
|
||||
|
||||
$sql = 'call sp_echec_reconnaitrelaface(?, ?);';
|
||||
$this->executerRequete($sql, array($idLogapiface, $resultat));
|
||||
}
|
||||
|
||||
public function init_traiterlaface($codeActionFace)
|
||||
{
|
||||
$codePrestataire = $_SESSION['codePrestataire'];
|
||||
$user = $_SESSION['login'];
|
||||
$username = $_SESSION['numeroBeneficiaire'];
|
||||
|
||||
$sql = 'call sp_init_traiterlaface(?, ?, ?, ?);';
|
||||
$resultat = $this->executerRequete($sql, array($codePrestataire, $user, $username, $codeActionFace));
|
||||
$ligne = $resultat->fetch(PDO::FETCH_ASSOC);
|
||||
$_SESSION['idLogapiface'] = $ligne['idLogapiface'];
|
||||
}
|
||||
|
||||
public function resultat_traitement_face($username, $success, $message)
|
||||
{
|
||||
$idLogapiface = $_SESSION['idLogapiface'];
|
||||
|
||||
$sql = 'CALL sp_resultat_traitement_face(?, ?, ?, ?);';
|
||||
|
||||
$this->executerRequete($sql, array($idLogapiface, $username, $success, $message));
|
||||
}
|
||||
|
||||
public function maj_demandereconnaissancefaciale($codeEtat)
|
||||
{
|
||||
$idDemande = $_SESSION['idDemande'];
|
||||
|
||||
$sql = 'call sp_p_maj_demandereconnaissancefaciale(?, ?, ?);';
|
||||
$this->executerRequete($sql, array($codeEtat, $_SERVER['REMOTE_ADDR'], $idDemande));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
session_unset();
|
||||
|
||||
if (!isset($_GET['lg'])) {
|
||||
afficherMessage("Paramètre langue absent de la requête!");
|
||||
afficherMessage("Paramètre langue absent de la requête!", "Erreur");
|
||||
}
|
||||
|
||||
$lg = $_GET['lg'];
|
||||
|
|
@ -11,27 +11,27 @@
|
|||
|
||||
$tab_code_langue = ["fr_FR", "en_US"];
|
||||
if (!in_array($codeLangue, $tab_code_langue)) {
|
||||
afficherMessage("Langue inconnue!");
|
||||
afficherMessage("Langue inconnue!", "Erreur");
|
||||
}
|
||||
|
||||
if (!isset($_GET['codeEntite'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Entity parameter missing from query!" : "Paramètre entité absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
afficherMessage($msg, "Error");
|
||||
}
|
||||
|
||||
if (!isset($_GET['codePrestataire'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Provider parameter missing from query!" : "Paramètre prestataire absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
afficherMessage($msg, "Erreur");
|
||||
}
|
||||
|
||||
if (!isset($_GET['idBeneficiaire'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Insured ID missing from query!" : "ID assuré absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
afficherMessage($msg, "Erreur");
|
||||
}
|
||||
|
||||
if (!isset($_GET['idDemande'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Reqest ID missing from query!" : "ID demande absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
afficherMessage($msg, "Erreur");
|
||||
}
|
||||
|
||||
$_SESSION['codeLangue'] = $lg;
|
||||
|
|
@ -47,28 +47,19 @@
|
|||
|
||||
$codeBdd = $codeSociete;
|
||||
|
||||
$_SESSION['idLogapiface'] = 0;
|
||||
|
||||
require_once "Assure.php";
|
||||
|
||||
$assure = new Assure();
|
||||
$_SESSION['assure'] = $assure;
|
||||
|
||||
$_SESSION['codeBdd'] = $codeBdd;
|
||||
$bdd = $assure->geUneBd($codeBdd);
|
||||
|
||||
/*
|
||||
var_dump(
|
||||
array(
|
||||
// "bdd" => $bdd,
|
||||
"codeSociete" => $codeSociete,
|
||||
"codePrestataire" => $codePrestataire,
|
||||
"idBeneficiaire" => $idBeneficiaire,
|
||||
)
|
||||
|
||||
);
|
||||
*/
|
||||
|
||||
if(!$bdd) {
|
||||
$message = $codeLangue == 'en_US' ? "Entity not found!" : "Entité introuvable!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
afficherMessage("<strong>$message</strong>", "Erreur");
|
||||
}
|
||||
|
||||
$_SESSION['BdName'] = $bdd['BdName'];
|
||||
|
|
@ -80,7 +71,7 @@
|
|||
|
||||
if(!$fassureExiste) {
|
||||
$message = $codeLangue == 'en_US' ? "Insured not found!" : "Assuré introuvable!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
afficherMessage("<strong>$message</strong>", "Erreur");
|
||||
}
|
||||
|
||||
$demande = $assure->checkdemandereconnaissancefaciale_id();
|
||||
|
|
@ -101,83 +92,85 @@
|
|||
$beneficiaire = $demande["beneficiaire"];
|
||||
$prestataire = $demande["prestataire"];
|
||||
|
||||
// var_dump($demande);
|
||||
/*
|
||||
idDemande
|
||||
codePrestataire
|
||||
idBeneficiaire
|
||||
codeEtat
|
||||
nbTentative
|
||||
lienPhotoWebcam
|
||||
dateExpiration
|
||||
dateExpirationFr
|
||||
dateExpirationEng
|
||||
demandeExpiree
|
||||
beneficiaire
|
||||
prestataire
|
||||
*/
|
||||
$lienPhoto = $demande["lienPhoto"];
|
||||
|
||||
/*
|
||||
var_dump(
|
||||
array(
|
||||
// "bdd" => $bdd,
|
||||
"idDemande" => $idDemande,
|
||||
"codeEtat" => $codeEtat,
|
||||
"demandeExpiree" => $demandeExpiree,
|
||||
"etatDemande" => $etatDemande,
|
||||
"etatDemandeEng" => $etatDemandeEng,
|
||||
$attempts = $demande["attempts"];
|
||||
|
||||
$_SESSION['lienPhoto'] = $lienPhoto;
|
||||
|
||||
"beneficiaire" => $beneficiaire,
|
||||
"prestataire" => $prestataire,
|
||||
)
|
||||
|
||||
);
|
||||
*/
|
||||
$_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=="3" || $demandeExpiree=="1") {
|
||||
if($codeEtat=="6") {
|
||||
$message = $codeLangue == 'en_US' ? "Token already used!" : "Token déjà utilisé!";
|
||||
afficherMessage("<strong>$message</strong>", "Information");
|
||||
}
|
||||
|
||||
if($codeEtat=="3" || $demandeExpiree=="1") {
|
||||
// changer le status
|
||||
$assure->maj_demandereconnaissancefaciale("3"); // Expirée
|
||||
|
||||
$message = $codeLangue == 'en_US' ? "Request expired since $dateExpirationEng !" : "Demande expirée depuis $dateExpirationFr !";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
afficherMessage("<strong>$message</strong>", "Erreur");
|
||||
}
|
||||
|
||||
if($codeEtat=="1") {
|
||||
$message = $codeLangue == 'en_US' ? "Verification already completed!" : "Vérification déjà exffectuée!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
if($codeEtat=="1") {
|
||||
$message = $codeLangue == 'en_US' ? "Verification already completed!" : "Vérification déjà effectuée!";
|
||||
afficherMessage("<strong>$message</strong>", "Information");
|
||||
}
|
||||
|
||||
if($codeEtat=="2") {
|
||||
$message = $codeLangue == 'en_US' ? "Failure, please try again!" : "Echec, veuilez réessayer!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
$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 = $codeLangue == 'en_US' ? "Too many attempts!" : "Trop de tentatives!";
|
||||
afficherMessage("<strong>$message</strong>", "Erreur");
|
||||
}
|
||||
|
||||
if($codeEtat=="4") {
|
||||
$message = $codeLangue == 'en_US' ? "Error, please try again!" : "Erreur, veuilez réessayer!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
$message = $codeLangue == 'en_US'
|
||||
? "Parameters successfully verified!"
|
||||
: "Paramètres vérifiés avec succès!";
|
||||
afficherMessage("<strong>$message</strong>", false);
|
||||
*/
|
||||
}
|
||||
|
||||
function afficherMessage($message) {
|
||||
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'>
|
||||
<title>Erreur</title>
|
||||
<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>
|
||||
|
|
@ -205,7 +198,7 @@
|
|||
<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'>Erreur</h4>
|
||||
<h4 class='text-center text-danger mb-3'>$titre</h4>
|
||||
<p class='text-center text-muted'>{$message}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -214,6 +207,30 @@
|
|||
exit();
|
||||
}
|
||||
|
||||
// Chiffrer l’image au moment de l’upload (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 openssl_decrypt($ciphertext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
|
||||
return base64_encode(openssl_decrypt($ciphertext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv));
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
|
@ -251,7 +268,8 @@
|
|||
.header {
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
/*padding: 30px;*/
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
@ -265,7 +283,8 @@
|
|||
}
|
||||
|
||||
.content {
|
||||
padding: 40px 30px;
|
||||
/*padding: 40px 30px;*/
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.step {
|
||||
|
|
@ -452,6 +471,13 @@
|
|||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.header h3 {
|
||||
font-size: 14px;
|
||||
/*margin-bottom: 10px;*/
|
||||
margin: 10px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -459,13 +485,20 @@
|
|||
<div class="header">
|
||||
<h1>🏥 INTER-SANTÉ</h1>
|
||||
<p>Vérification d'identité sécurisée</p>
|
||||
|
||||
<h3>Prestataire : <?= $prestataire ?> </h3>
|
||||
|
||||
<h3>Patient : <?= $beneficiaire ?> </h3>
|
||||
|
||||
<h3>Compteur : <?= "$attempts tentative(s) sur $maxAttempts" ?> </h3>
|
||||
|
||||
</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>
|
||||
<p style="text-align: center; color: #7f8c8d;">Demande Vérification du lien...</p>
|
||||
</div>
|
||||
|
||||
<!-- Étape 2: Instructions -->
|
||||
|
|
@ -533,6 +566,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
|
||||
<script>
|
||||
let video = document.getElementById('video');
|
||||
let canvas = document.getElementById('canvas');
|
||||
|
|
@ -543,13 +578,10 @@
|
|||
|
||||
// Récupérer le token depuis l'URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
// alert(urlParams);
|
||||
// verificationToken = urlParams.get('token');
|
||||
verificationToken = urlParams.get('idDemande');
|
||||
codeEntite = urlParams.get('codeEntite');
|
||||
idBeneficiaire = urlParams.get('idBeneficiaire');
|
||||
alert("codeEntite="+codeEntite+" ; idBeneficiaire="+idBeneficiaire);
|
||||
|
||||
|
||||
verificationToken = <?= $_SESSION['idDemande'] ?> ;
|
||||
|
||||
// Initialisation
|
||||
window.onload = function() {
|
||||
if (!verificationToken) {
|
||||
|
|
@ -562,28 +594,30 @@
|
|||
};
|
||||
|
||||
function validateToken() {
|
||||
fetch('verify_facial_api.php', {
|
||||
$.ajax({
|
||||
url: '/Contestation/verify_facial_api.php',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contentType: 'application/json',
|
||||
data: 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');
|
||||
}),
|
||||
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');
|
||||
console.log(xhr.responseText);
|
||||
showError(xhr.responseText);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Erreur de connexion au serveur');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function showStep(stepId) {
|
||||
document.querySelectorAll('.step').forEach(step => {
|
||||
|
|
@ -651,31 +685,38 @@
|
|||
|
||||
function verifyPhoto() {
|
||||
showStep('step-verifying');
|
||||
|
||||
fetch('verify_facial_api.php', {
|
||||
|
||||
const base64Only = capturedImage.split(',')[1];
|
||||
|
||||
$.ajax({
|
||||
url: '/Contestation/verify_facial_api.php',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contentType: 'application/json',
|
||||
data: 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);
|
||||
// image: capturedImage
|
||||
image: base64Only
|
||||
}),
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
// alert(data);
|
||||
console.log(data);
|
||||
if (data.success && data.match) {
|
||||
showSuccess(data);
|
||||
} else {
|
||||
showFailure(data);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
showError('Erreur lors de la vérification: ' + error);
|
||||
// showError(xhr.responseText);
|
||||
// console.log(error);
|
||||
// console.log(xhr.responseText);
|
||||
}
|
||||
})
|
||||
.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 !';
|
||||
|
|
@ -687,9 +728,10 @@
|
|||
|
||||
showStep('step-result');
|
||||
|
||||
// Redirection automatique après 5 secondes
|
||||
startCountdown(5, () => {
|
||||
window.location.href = data.redirect_url || 'dashboard.php';
|
||||
// Redirection automatique après 3 secondes
|
||||
startCountdown(3, () => {
|
||||
// On recharge la page
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -700,7 +742,7 @@
|
|||
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>`;
|
||||
<strong>Tentatives restantes: ${data.attempts_remaining -1 || 0} </strong>`;
|
||||
|
||||
showStep('step-result');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,361 +0,0 @@
|
|||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
|
||||
function afficherMessage($message) {
|
||||
echo "<!DOCTYPE html>
|
||||
<html lang='fr'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
<title>Erreur</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'>Erreur</h4>
|
||||
<p class='text-center text-muted'>{$message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>";
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($_GET['lg'])) {
|
||||
afficherMessage("Paramètre langue absent de la requête!");
|
||||
}
|
||||
|
||||
$lg = $_GET['lg'];
|
||||
$codeLangue = base64_decode($lg);
|
||||
|
||||
$tab_code_langue = ["fr_FR", "en_US"];
|
||||
if (!in_array($codeLangue, $tab_code_langue)) {
|
||||
afficherMessage("Langue inconnue!");
|
||||
}
|
||||
|
||||
if (!isset($_GET['codeEntite'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Entity parameter missing from query!" : "Paramètre entité absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
}
|
||||
|
||||
if (!isset($_GET['idAdherent'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Family parameter missing from query!" : "Paramètre famille absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
}
|
||||
|
||||
if (!isset($_GET['dossier'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Entity dossier missing from query!" : "Paramètre dossier absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
}
|
||||
|
||||
$_SESSION['codeLangue'] = $lg;
|
||||
$_SESSION['codeEntite'] = $_GET['codeEntite'];
|
||||
$_SESSION['idAdherent'] = $_GET['idAdherent'];
|
||||
$_SESSION['dossier'] = $_GET['dossier'];
|
||||
|
||||
$codeEntite = $_GET['codeEntite'];
|
||||
$idAdherent = $_GET['idAdherent'];
|
||||
$dossier = $_GET['dossier'];
|
||||
|
||||
$title = $codeLangue == 'en_US' ? "Contest a medical record" : "Contester un dossier médical";
|
||||
$label = $codeLangue == 'en_US' ? "Submit" : "Soumettre";
|
||||
$labelMotifContestation = $codeLangue == 'en_US' ? "Reason for the contestation:" : "Motif de la contestation:";
|
||||
$placeholder = $codeLangue == 'en_US' ? "Please describe in detail the reason for your contestation..." : "Veuillez décrire en détail le motif de votre contestation...";
|
||||
|
||||
$action = "/Contestation/Validercontestation.php?" .
|
||||
"codeEntite=" . urlencode($codeEntite) .
|
||||
"&idAdherent=" . urlencode($idAdherent) .
|
||||
"&dossier=" . urlencode($dossier) .
|
||||
"&lg=" . urlencode($lg);
|
||||
?>
|
||||
|
||||
<!doctype html>
|
||||
<html lang="<?= $codeLangue == 'en_US' ? 'en' : 'fr' ?>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?= htmlspecialchars($title) ?></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">
|
||||
<link rel="icon" href="/Contestation/favicon.ico"/>
|
||||
<style>
|
||||
.contestation-container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.contestation-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
|
||||
overflow: hidden;
|
||||
max-width: 700px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
|
||||
.contestation-header {
|
||||
background: linear-gradient(135deg, #4caf50 0%, #45a049 100%);
|
||||
color: white;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.contestation-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><path d="M0,0 L100,0 L100,100 Z" fill="rgba(255,255,255,0.1)"/></svg>');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.contestation-header h1 {
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
font-size: 1.8rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.contestation-body {
|
||||
padding: 2.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.required::after {
|
||||
content: " *";
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 10px;
|
||||
padding: 1rem;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 0 0.2rem rgba(76, 175, 80, 0.25);
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
background: linear-gradient(135deg, #4caf50 0%, #45a049 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.character-count {
|
||||
text-align: right;
|
||||
font-size: 0.875rem;
|
||||
color: #6c757d;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
background: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
border-left: 4px solid #4caf50;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-item i {
|
||||
color: #4caf50;
|
||||
margin-right: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.contestation-container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.contestation-card {
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.contestation-header {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.contestation-header h1 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.contestation-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.contestation-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.contestation-header {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.contestation-header h1 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.contestation-body {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="contestation-container">
|
||||
<div class="contestation-card">
|
||||
<div class="contestation-header">
|
||||
<h1>
|
||||
<i class="bi bi-clipboard-x me-2"></i>
|
||||
<?= htmlspecialchars($title) ?>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="contestation-body">
|
||||
<!-- Information Section -->
|
||||
<div class="info-section">
|
||||
<div class="info-item">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
<strong><?= $codeLangue == 'en_US' ? 'Information:' : 'Information :' ?></strong>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="bi bi-building"></i>
|
||||
<span><?= $codeLangue == 'en_US' ? 'Entity:' : 'Entité :' ?> <?= htmlspecialchars($codeEntite) ?></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="bi bi-person"></i>
|
||||
<span><?= $codeLangue == 'en_US' ? 'Family ID:' : 'ID Famille :' ?> <?= htmlspecialchars($idAdherent) ?></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="bi bi-folder"></i>
|
||||
<span><?= $codeLangue == 'en_US' ? 'Record:' : 'Dossier :' ?> <?= htmlspecialchars($dossier) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contestation Form -->
|
||||
<form action="<?= $action ?>" method="post">
|
||||
<div class="mb-4">
|
||||
<label for="motifContestation" class="form-label required">
|
||||
<?= htmlspecialchars($labelMotifContestation) ?>
|
||||
</label>
|
||||
<textarea
|
||||
id="motifContestation"
|
||||
name="motifContestation"
|
||||
class="form-control border border-success"
|
||||
rows="8"
|
||||
required
|
||||
placeholder="<?= htmlspecialchars($placeholder) ?>"
|
||||
maxlength="1000"
|
||||
oninput="updateCharacterCount(this)"></textarea>
|
||||
<div class="character-count">
|
||||
<span id="charCount">0</span>/1000 <?= $codeLangue == 'en_US' ? 'characters' : 'caractères' ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit">
|
||||
<i class="bi bi-send-check me-2"></i>
|
||||
<?= htmlspecialchars($label) ?>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function updateCharacterCount(textarea) {
|
||||
const charCount = textarea.value.length;
|
||||
document.getElementById('charCount').textContent = charCount;
|
||||
|
||||
// Change color when approaching limit
|
||||
const charCountElement = document.getElementById('charCount');
|
||||
if (charCount > 900) {
|
||||
charCountElement.style.color = '#dc3545';
|
||||
charCountElement.style.fontWeight = 'bold';
|
||||
} else if (charCount > 750) {
|
||||
charCountElement.style.color = '#ffc107';
|
||||
charCountElement.style.fontWeight = 'bold';
|
||||
} else {
|
||||
charCountElement.style.color = '#6c757d';
|
||||
charCountElement.style.fontWeight = 'normal';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize character count on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const textarea = document.getElementById('motifContestation');
|
||||
updateCharacterCount(textarea);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,673 +0,0 @@
|
|||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
|
||||
if (!isset($_GET['lg'])) {
|
||||
afficherMessage("Paramètre langue absent de la requête!");
|
||||
}
|
||||
|
||||
$lg = $_GET['lg'];
|
||||
$codeLangue = base64_decode($lg);
|
||||
|
||||
$tab_code_langue = ["fr_FR", "en_US"];
|
||||
if (!in_array($codeLangue, $tab_code_langue)) {
|
||||
afficherMessage("Langue inconnue!");
|
||||
}
|
||||
|
||||
if (!isset($_GET['codeEntite'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Entity parameter missing from query!" : "Paramètre entité absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
}
|
||||
|
||||
if (!isset($_GET['codePrestataire'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Provider parameter missing from query!" : "Paramètre prestataire absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
}
|
||||
|
||||
if (!isset($_GET['idBeneficiaire'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Insured ID missing from query!" : "ID assuré absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
}
|
||||
|
||||
if (!isset($_GET['idDemande'])) {
|
||||
$msg = $codeLangue == 'en_US' ? "Reqest ID missing from query!" : "ID demande absent de la requête!";
|
||||
afficherMessage($msg);
|
||||
}
|
||||
|
||||
$_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;
|
||||
|
||||
require_once "Assure.php";
|
||||
|
||||
$assure = new Assure();
|
||||
|
||||
$_SESSION['codeBdd'] = $codeBdd;
|
||||
$bdd = $assure->geUneBd($codeBdd);
|
||||
|
||||
/*
|
||||
var_dump(
|
||||
array(
|
||||
// "bdd" => $bdd,
|
||||
"codeSociete" => $codeSociete,
|
||||
"codePrestataire" => $codePrestataire,
|
||||
"idBeneficiaire" => $idBeneficiaire,
|
||||
)
|
||||
|
||||
);
|
||||
*/
|
||||
|
||||
if(!$bdd) {
|
||||
$message = $codeLangue == 'en_US' ? "Entity not found!" : "Entité introuvable!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
$_SESSION['BdName'] = $bdd['BdName'];
|
||||
$_SESSION['BdLogin'] = $bdd['BdLogin'];
|
||||
$_SESSION['BdMdp'] = $bdd['BdMdp'];
|
||||
|
||||
if($assure->existeligne($codeSociete)) {
|
||||
$fassureExiste = $assure->assureExiste($codeSociete, $idBeneficiaire);
|
||||
|
||||
if(!$fassureExiste) {
|
||||
$message = $codeLangue == 'en_US' ? "Insured not found!" : "Assuré introuvable!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
$demandereconnaissancefaciale = $assure->checkdemandereconnaissancefaciale();
|
||||
|
||||
var_dump($demandereconnaissancefaciale);
|
||||
|
||||
/*
|
||||
if(!$feuilleMaladieEncours) {
|
||||
$message = $codeLangue == 'en_US' ? "Deadline for appeal has passed!" : "Delai de contestation dépassé!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
$feuilleDejaContestee = $validercontestation->feuilleContestee($codeSociete, $idAdherent, $numeroFeuilleMaladie);
|
||||
|
||||
if($feuilleDejaContestee) {
|
||||
$message = $codeLangue == 'en_US' ? "File already contested!" : "Dossier déjà contesté!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
$validercontestation->contester($codeSociete, $idAdherent, $numeroFeuilleMaladie, $motifContestation);
|
||||
*/
|
||||
|
||||
$message = $codeLangue == 'en_US'
|
||||
? "Parameters successfully verified!"
|
||||
: "Paramètres vérifiés avec succès!";
|
||||
afficherMessage("<strong>$message</strong>", false);
|
||||
}
|
||||
|
||||
function afficherMessage($message) {
|
||||
echo "<!DOCTYPE html>
|
||||
<html lang='fr'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
<title>Erreur</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'>Erreur</h4>
|
||||
<p class='text-center text-muted'>{$message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>";
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!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);
|
||||
// alert(urlParams);
|
||||
// verificationToken = urlParams.get('token');
|
||||
codeEntite = urlParams.get('codeEntite');
|
||||
idBeneficiaire = urlParams.get('idBeneficiaire');
|
||||
alert("codeEntite="+codeEntite+" ; idBeneficiaire="+idBeneficiaire);
|
||||
|
||||
// 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>
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* API pour vérifier le statut de la vérification faciale en temps réel
|
||||
* Utilisé par le prestataire pour savoir quand l'assuré a validé
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
require_once 'database.php';
|
||||
|
||||
$requestId = $_GET['request_id'] ?? null;
|
||||
|
||||
if (!$requestId) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'ID de requête manquant'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
// Récupérer le statut de la vérification
|
||||
$sql = "SELECT vr.*, pas.session_token
|
||||
FROM facial_verification_requests vr
|
||||
LEFT JOIN prestation_authorization_sessions pas
|
||||
ON vr.id = pas.verification_request_id
|
||||
AND pas.status = 'active'
|
||||
WHERE vr.id = ?";
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute([$requestId]);
|
||||
$request = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$request) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Requête non trouvée'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Calculer le temps restant
|
||||
$now = new DateTime();
|
||||
$expiresAt = new DateTime($request['expires_at']);
|
||||
$interval = $now->diff($expiresAt);
|
||||
$secondsRemaining = ($expiresAt->getTimestamp() - $now->getTimestamp());
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'status' => $request['status'],
|
||||
'attempts' => $request['attempts'],
|
||||
'match_confidence' => $request['match_confidence'],
|
||||
'session_token' => $request['session_token'],
|
||||
'seconds_remaining' => max(0, $secondsRemaining),
|
||||
'verified_at' => $request['verified_at']
|
||||
]);
|
||||
|
|
@ -1,513 +0,0 @@
|
|||
<!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>
|
||||
|
|
@ -1,341 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Exemple d'intégration de la vérification faciale dans le flux existant
|
||||
* À utiliser après la lecture du tag NFC ou QR code
|
||||
*/
|
||||
|
||||
require_once 'config.php';
|
||||
require_once 'database.php';
|
||||
require_once 'send_verification_link.php';
|
||||
|
||||
// ====================================================================
|
||||
// SCÉNARIO 1: Lecture du tag NFC/QR Code
|
||||
// ====================================================================
|
||||
|
||||
/**
|
||||
* Fonction appelée après la lecture réussie du tag NFC ou QR code
|
||||
*/
|
||||
function handleNFCOrQRCodeScan($tagData) {
|
||||
// 1. Décoder les données du tag
|
||||
$assureData = json_decode($tagData, true);
|
||||
$assureId = $assureData['assure_id'];
|
||||
|
||||
// 2. Vérifier que l'assuré existe
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
$sql = "SELECT id, nom, prenoms, numero_carte, email, telephone, photo_reference_path
|
||||
FROM assures
|
||||
WHERE id = ? AND statut = 'actif'";
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute([$assureId]);
|
||||
$assure = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$assure) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Assuré non trouvé ou inactif'
|
||||
];
|
||||
}
|
||||
|
||||
// 3. Vérifier si une photo de référence existe
|
||||
if (empty($assure['photo_reference_path'])) {
|
||||
// Pas de photo de référence = accès sans vérification faciale
|
||||
// Loguer cette tentative pour sécurité
|
||||
logAction("Accès sans vérification faciale - Assuré #{$assureId} - Aucune photo de référence", 'WARNING');
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'require_facial_verification' => false,
|
||||
'message' => 'Accès autorisé (pas de photo de référence)',
|
||||
'assure' => $assure
|
||||
];
|
||||
}
|
||||
|
||||
// 4. Envoyer le lien de vérification faciale
|
||||
$verifier = new FacialVerificationLink($conn);
|
||||
|
||||
// Choisir le canal d'envoi selon les préférences
|
||||
$method = 'both'; // 'email', 'whatsapp', ou 'both'
|
||||
|
||||
$result = $verifier->sendVerificationLink($assureId, $method);
|
||||
|
||||
if ($result['success']) {
|
||||
return [
|
||||
'success' => true,
|
||||
'require_facial_verification' => true,
|
||||
'message' => 'Lien de vérification envoyé',
|
||||
'email_sent' => $result['email_sent'],
|
||||
'whatsapp_sent' => $result['whatsapp_sent'],
|
||||
'assure' => $assure
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $result['message']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// SCÉNARIO 2: Vérification de session avant saisie des prestations
|
||||
// ====================================================================
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur a une session d'autorisation valide
|
||||
* À appeler au début de saisie_prestations.php
|
||||
*/
|
||||
function checkAuthorizationSession() {
|
||||
$sessionToken = $_GET['token'] ?? $_SESSION['authorization_token'] ?? null;
|
||||
|
||||
if (!$sessionToken) {
|
||||
return [
|
||||
'authorized' => false,
|
||||
'message' => 'Aucune session d\'autorisation'
|
||||
];
|
||||
}
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
$sql = "SELECT pas.*, a.id as assure_id, a.nom, a.prenoms, a.numero_carte
|
||||
FROM prestation_authorization_sessions pas
|
||||
JOIN assures a ON pas.assure_id = a.id
|
||||
WHERE pas.session_token = ?
|
||||
AND pas.status = 'active'
|
||||
AND pas.expires_at > NOW()";
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute([$sessionToken]);
|
||||
$session = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$session) {
|
||||
return [
|
||||
'authorized' => false,
|
||||
'message' => 'Session invalide ou expirée'
|
||||
];
|
||||
}
|
||||
|
||||
// Stocker en session PHP pour les requêtes suivantes
|
||||
$_SESSION['authorization_token'] = $sessionToken;
|
||||
$_SESSION['authorized_assure_id'] = $session['assure_id'];
|
||||
|
||||
return [
|
||||
'authorized' => true,
|
||||
'assure' => [
|
||||
'id' => $session['assure_id'],
|
||||
'nom' => $session['nom'],
|
||||
'prenoms' => $session['prenoms'],
|
||||
'numero_carte' => $session['numero_carte']
|
||||
],
|
||||
'session' => $session
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une session comme utilisée après saisie d'une prestation
|
||||
*/
|
||||
function markSessionAsUsed($sessionToken) {
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
$sql = "UPDATE prestation_authorization_sessions
|
||||
SET prestations_saisies = prestations_saisies + 1,
|
||||
used_at = NOW()
|
||||
WHERE session_token = ?";
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
return $stmt->execute([$sessionToken]);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// SCÉNARIO 3: Interface utilisateur pour le prestataire
|
||||
// ====================================================================
|
||||
|
||||
/**
|
||||
* Génère l'interface HTML pour le prestataire après scan NFC/QR
|
||||
*/
|
||||
function renderVerificationWaitingScreen($assure, $verificationSent) {
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vérification en attente - INTER-SANTÉ</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f5f5f5;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 50px auto;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.spinner {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-top: 5px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 30px auto;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
.assure-info {
|
||||
background: #ecf0f1;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.status {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.channels {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.channel {
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.channel.sent {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.channel.not-sent {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🏥 INTER-SANTÉ</h1>
|
||||
<h2>Vérification d'identité en cours</h2>
|
||||
|
||||
<div class="assure-info">
|
||||
<h3><?php echo htmlspecialchars($assure['prenoms'] . ' ' . $assure['nom']); ?></h3>
|
||||
<p>Carte N° <?php echo htmlspecialchars($assure['numero_carte']); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="spinner"></div>
|
||||
|
||||
<p class="status">
|
||||
Un lien de vérification a été envoyé à l'assuré.<br>
|
||||
En attente de la vérification faciale...
|
||||
</p>
|
||||
|
||||
<div class="channels">
|
||||
<?php if ($verificationSent['email_sent']): ?>
|
||||
<div class="channel sent">✓ Email envoyé</div>
|
||||
<?php else: ?>
|
||||
<div class="channel not-sent">✗ Email non envoyé</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($verificationSent['whatsapp_sent']): ?>
|
||||
<div class="channel sent">✓ WhatsApp envoyé</div>
|
||||
<?php else: ?>
|
||||
<div class="channel not-sent">✗ WhatsApp non envoyé</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 12px; color: #95a5a6;">
|
||||
Le lien de vérification est valable pendant 15 minutes
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Vérifier le statut toutes les 3 secondes
|
||||
const requestId = <?php echo $verificationSent['request_id']; ?>;
|
||||
|
||||
function checkVerificationStatus() {
|
||||
fetch('check_verification_status.php?request_id=' + requestId)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'verified') {
|
||||
// Rediriger vers la saisie des prestations
|
||||
window.location.href = 'saisie_prestations.php?token=' + data.session_token;
|
||||
} else if (data.status === 'failed' || data.status === 'expired') {
|
||||
// Afficher un message d'erreur
|
||||
alert('La vérification a échoué ou a expiré');
|
||||
window.location.href = 'index.php';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Vérifier toutes les 3 secondes
|
||||
setInterval(checkVerificationStatus, 3000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// EXEMPLE D'UTILISATION COMPLÈTE
|
||||
// ====================================================================
|
||||
|
||||
// Dans votre fichier de scan NFC/QR (ex: scan_handler.php)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$tagData = $_POST['tag_data'] ?? null;
|
||||
|
||||
if (!$tagData) {
|
||||
echo json_encode(['success' => false, 'message' => 'Données manquantes']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Traiter le scan
|
||||
$result = handleNFCOrQRCodeScan($tagData);
|
||||
|
||||
if ($result['success'] && $result['require_facial_verification']) {
|
||||
// Afficher l'écran d'attente
|
||||
renderVerificationWaitingScreen($result['assure'], $result);
|
||||
} elseif ($result['success'] && !$result['require_facial_verification']) {
|
||||
// Accès direct (pas de photo de référence)
|
||||
header('Location: saisie_prestations.php?assure_id=' . $result['assure']['id']);
|
||||
} else {
|
||||
// Erreur
|
||||
echo json_encode($result);
|
||||
}
|
||||
}
|
||||
|
||||
// Dans votre fichier saisie_prestations.php
|
||||
session_start();
|
||||
|
||||
// Vérifier l'autorisation
|
||||
$auth = checkAuthorizationSession();
|
||||
|
||||
if (!$auth['authorized']) {
|
||||
die('Accès non autorisé. Veuillez scanner la carte de l\'assuré.');
|
||||
}
|
||||
|
||||
// L'utilisateur est autorisé, afficher le formulaire de saisie
|
||||
$assure = $auth['assure'];
|
||||
|
||||
// ... Votre code de saisie de prestations ...
|
||||
|
||||
// Après enregistrement d'une prestation
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_prestation'])) {
|
||||
// Enregistrer la prestation
|
||||
// ...
|
||||
|
||||
// Marquer la session comme utilisée
|
||||
markSessionAsUsed($_SESSION['authorization_token']);
|
||||
}
|
||||
|
|
@ -1,37 +1,34 @@
|
|||
<?php
|
||||
/**
|
||||
* API Backend pour la vérification faciale
|
||||
* Gère la validation des tokens et la comparaison des visages
|
||||
*/
|
||||
|
||||
ob_start();
|
||||
require_once "Assure.php";
|
||||
session_start();
|
||||
header('Content-Type: application/json');
|
||||
require_once 'config.php';
|
||||
require_once 'database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
ob_end_clean();
|
||||
echo json_encode(['success' => false, 'message' => 'Méthode non autorisée']);
|
||||
exit;
|
||||
}
|
||||
|
||||
class FacialVerificationAPI {
|
||||
private $db;
|
||||
private $assure_api;
|
||||
private $maxAttempts = 3;
|
||||
|
||||
public function __construct($db) {
|
||||
$this->db = $db;
|
||||
public function __construct() {
|
||||
$this->assure_api = $_SESSION['assure'];
|
||||
$this->maxAttempts = $this->assure_api
|
||||
? $this->assure_api->get_nbTentativeBiometrie($_SESSION['codeEntite'])
|
||||
: 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide un token de vérification
|
||||
*/
|
||||
public function validateToken($token) {
|
||||
$_SESSION['lienPhoto'] = "";
|
||||
try {
|
||||
$sql = "SELECT vr.*, a.nom, a.prenoms, a.photo_reference_path
|
||||
FROM facial_verification_requests vr
|
||||
JOIN assures a ON vr.assure_id = a.id
|
||||
WHERE vr.verification_token = ?
|
||||
AND vr.status = 'pending'
|
||||
AND vr.expires_at > NOW()";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$token]);
|
||||
$request = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$request = $this->assure_api->valider_token();
|
||||
|
||||
if (!$request) {
|
||||
return [
|
||||
'success' => false,
|
||||
|
|
@ -39,11 +36,13 @@ class FacialVerificationAPI {
|
|||
];
|
||||
}
|
||||
|
||||
$_SESSION['lienPhoto'] = $request['lienPhoto'];
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Token valide',
|
||||
'assure' => [
|
||||
'nom' => $request['nom'],
|
||||
'nom' => $request['nom'],
|
||||
'prenoms' => $request['prenoms']
|
||||
]
|
||||
];
|
||||
|
|
@ -62,322 +61,157 @@ class FacialVerificationAPI {
|
|||
* Utilisez Azure Face API, AWS Rekognition, ou une solution locale
|
||||
*/
|
||||
private function compareFaces($referenceImagePath, $capturedImageBase64) {
|
||||
// Option 1: Azure Face API (Recommandé)
|
||||
return $this->compareWithAzureFaceAPI($referenceImagePath, $capturedImageBase64);
|
||||
|
||||
// Option 2: AWS Rekognition
|
||||
// return $this->compareWithAWSRekognition($referenceImagePath, $capturedImageBase64);
|
||||
|
||||
// Option 3: Solution locale avec OpenCV/dlib (avancé)
|
||||
// return $this->compareWithLocalFaceRecognition($referenceImagePath, $capturedImageBase64);
|
||||
//AWS Rekognition
|
||||
return $this->compareWithAWSRekognition($referenceImagePath, $capturedImageBase64);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparaison avec Azure Face API
|
||||
*/
|
||||
private function compareWithAzureFaceAPI($referenceImagePath, $capturedImageBase64) {
|
||||
$endpoint = AZURE_FACE_ENDPOINT; // Ex: https://your-resource.cognitiveservices.azure.com
|
||||
$apiKey = AZURE_FACE_API_KEY;
|
||||
|
||||
try {
|
||||
// 1. Détecter le visage dans l'image de référence
|
||||
$referenceImageData = base64_encode(file_get_contents($referenceImagePath));
|
||||
$referenceFaceId = $this->detectFaceAzure($referenceImageData, $endpoint, $apiKey);
|
||||
|
||||
if (!$referenceFaceId) {
|
||||
return [
|
||||
'match' => false,
|
||||
'confidence' => 0,
|
||||
'error' => 'Aucun visage détecté dans la photo de référence'
|
||||
];
|
||||
}
|
||||
|
||||
// 2. Détecter le visage dans l'image capturée
|
||||
$capturedImageData = explode(',', $capturedImageBase64)[1]; // Enlever le préfixe data:image
|
||||
$capturedFaceId = $this->detectFaceAzure($capturedImageData, $endpoint, $apiKey);
|
||||
|
||||
if (!$capturedFaceId) {
|
||||
return [
|
||||
'match' => false,
|
||||
'confidence' => 0,
|
||||
'error' => 'Aucun visage détecté dans votre photo'
|
||||
];
|
||||
}
|
||||
|
||||
// 3. Comparer les deux visages
|
||||
$verifyUrl = $endpoint . '/face/v1.0/verify';
|
||||
|
||||
$data = [
|
||||
'faceId1' => $referenceFaceId,
|
||||
'faceId2' => $capturedFaceId
|
||||
];
|
||||
|
||||
$ch = curl_init($verifyUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Ocp-Apim-Subscription-Key: ' . $apiKey
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
throw new Exception("Azure API error: " . $response);
|
||||
}
|
||||
|
||||
$result = json_decode($response, true);
|
||||
|
||||
return [
|
||||
'match' => $result['isIdentical'],
|
||||
'confidence' => round($result['confidence'] * 100, 2),
|
||||
'error' => null
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Erreur Azure Face API: " . $e->getMessage());
|
||||
return [
|
||||
'match' => false,
|
||||
'confidence' => 0,
|
||||
'error' => 'Erreur lors de la vérification faciale'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Détecte un visage avec Azure Face API et retourne le faceId
|
||||
*/
|
||||
private function detectFaceAzure($imageBase64, $endpoint, $apiKey) {
|
||||
$detectUrl = $endpoint . '/face/v1.0/detect?returnFaceId=true';
|
||||
|
||||
$ch = curl_init($detectUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, base64_decode($imageBase64));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/octet-stream',
|
||||
'Ocp-Apim-Subscription-Key: ' . $apiKey
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$faces = json_decode($response, true);
|
||||
|
||||
if (empty($faces)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $faces[0]['faceId'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Comparaison avec AWS Rekognition (Alternative)
|
||||
*/
|
||||
private function compareWithAWSRekognition($referenceImagePath, $capturedImageBase64) {
|
||||
require_once 'vendor/autoload.php'; // AWS SDK
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
try {
|
||||
$rekognitionClient = new Aws\Rekognition\RekognitionClient([
|
||||
'version' => 'latest',
|
||||
'region' => AWS_REGION,
|
||||
'version' => 'latest',
|
||||
'region' => 'us-west-2',
|
||||
'credentials' => [
|
||||
'key' => AWS_ACCESS_KEY_ID,
|
||||
'secret' => AWS_SECRET_ACCESS_KEY
|
||||
'key' => 'AKIA2O2PTXQ7XN5OATO3',
|
||||
'secret' => 'Rzq5mKG80tqfePQYF6iFZ5AMCM/bY2l6i5IxxLzL'
|
||||
]
|
||||
]);
|
||||
|
||||
$referenceImageData = file_get_contents($referenceImagePath);
|
||||
$capturedImageData = base64_decode(explode(',', $capturedImageBase64)[1]);
|
||||
|
||||
|
||||
|
||||
$referenceImageData = $referenceImagePath;
|
||||
$capturedImageData = $capturedImageBase64;
|
||||
|
||||
$this->assure_api->init_traiterlaface('2');
|
||||
|
||||
$result = $rekognitionClient->compareFaces([
|
||||
'SourceImage' => ['Bytes' => $referenceImageData],
|
||||
'TargetImage' => ['Bytes' => $capturedImageData],
|
||||
'SourceImage' => ['Bytes' => base64_decode($capturedImageData)],
|
||||
'TargetImage' => ['Bytes' => base64_decode($referenceImageData)],
|
||||
'SimilarityThreshold' => 80
|
||||
]);
|
||||
|
||||
if (empty($result['FaceMatches'])) {
|
||||
|
||||
$this->assure_api->resultat_traitement_face($_SESSION['numeroBeneficiaire'], '0', "Les faces ne correspondent pas! / The faces do not match!");
|
||||
|
||||
return [
|
||||
'match' => false,
|
||||
'match' => false,
|
||||
'confidence' => 0,
|
||||
'error' => 'Les visages ne correspondent pas'
|
||||
'error' => 'Les visages ne correspondent pas'
|
||||
];
|
||||
}
|
||||
|
||||
$similarity = $result['FaceMatches'][0]['Similarity'];
|
||||
|
||||
// Face confirmée! / Face confirmed!
|
||||
$this->assure_api->resultat_traitement_face($_SESSION['numeroBeneficiaire'], '1', '');
|
||||
|
||||
return [
|
||||
'match' => $similarity >= 80,
|
||||
'match' => $similarity >= 80,
|
||||
'confidence' => round($similarity, 2),
|
||||
'error' => null
|
||||
'error' => null
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Erreur AWS Rekognition: " . $e->getMessage());
|
||||
|
||||
$this->assure_api->resultat_traitement_face($_SESSION['numeroBeneficiaire'], '9', $msgErreur);
|
||||
|
||||
return [
|
||||
'match' => false,
|
||||
'match' => false,
|
||||
'confidence' => 0,
|
||||
'error' => 'Erreur lors de la vérification faciale'
|
||||
'error' => 'Erreur lors de la vérification faciale',
|
||||
'error_kane' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre la photo capturée
|
||||
*/
|
||||
private function saveCapturedImage($assureId, $imageBase64) {
|
||||
$uploadDir = 'uploads/facial_verification/';
|
||||
|
||||
if (!file_exists($uploadDir)) {
|
||||
mkdir($uploadDir, 0755, true);
|
||||
}
|
||||
|
||||
$imageData = explode(',', $imageBase64)[1];
|
||||
$imageData = base64_decode($imageData);
|
||||
|
||||
$filename = $uploadDir . $assureId . '_' . time() . '.jpg';
|
||||
file_put_contents($filename, $imageData);
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le statut de la vérification
|
||||
*/
|
||||
private function updateVerificationStatus($token, $status, $matchResult = null, $capturedPhotoPath = null) {
|
||||
$sql = "UPDATE facial_verification_requests
|
||||
SET status = ?,
|
||||
verified_at = NOW(),
|
||||
match_confidence = ?,
|
||||
captured_photo_path = ?,
|
||||
attempts = attempts + 1
|
||||
WHERE verification_token = ?";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([
|
||||
$status,
|
||||
$matchResult ? $matchResult['confidence'] : null,
|
||||
$capturedPhotoPath,
|
||||
$token
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une session d'autorisation pour l'accès aux prestations
|
||||
*/
|
||||
private function createAuthorizationSession($assureId, $verificationRequestId) {
|
||||
$sessionToken = bin2hex(random_bytes(32));
|
||||
$expiresAt = date('Y-m-d H:i:s', time() + 3600); // 1 heure
|
||||
|
||||
$sql = "INSERT INTO prestation_authorization_sessions
|
||||
(assure_id, verification_request_id, session_token, expires_at, status)
|
||||
VALUES (?, ?, ?, ?, 'active')";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$assureId, $verificationRequestId, $sessionToken, $expiresAt]);
|
||||
|
||||
return $sessionToken;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Vérifie le visage capturé
|
||||
*/
|
||||
public function verifyFace($token, $capturedImageBase64) {
|
||||
try {
|
||||
// 1. Récupérer les infos de la demande
|
||||
$sql = "SELECT vr.*, a.photo_reference_path, a.id as assure_id
|
||||
FROM facial_verification_requests vr
|
||||
JOIN assures a ON vr.assure_id = a.id
|
||||
WHERE vr.verification_token = ?
|
||||
AND vr.status = 'pending'
|
||||
AND vr.expires_at > NOW()";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$token]);
|
||||
$request = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$request = $this->assure_api->valider_token();
|
||||
|
||||
if (!$request) {
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'match' => false,
|
||||
'message' => 'Demande expirée ou invalide'
|
||||
];
|
||||
}
|
||||
|
||||
// 2. Vérifier le nombre de tentatives
|
||||
if ($request['attempts'] >= $this->maxAttempts) {
|
||||
$this->updateVerificationStatus($token, 'failed');
|
||||
|
||||
$this->assure_api->maj_demandereconnaissancefaciale("2"); // Echec
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'match' => false,
|
||||
'message' => 'Nombre maximum de tentatives atteint'
|
||||
];
|
||||
}
|
||||
|
||||
// 3. Enregistrer la photo capturée
|
||||
$capturedPhotoPath = $this->saveCapturedImage($request['assure_id'], $capturedImageBase64);
|
||||
|
||||
// 4. Comparer les visages
|
||||
$comparisonResult = $this->compareFaces(
|
||||
$request['photo_reference_path'],
|
||||
$_SESSION['photoAssureCrypte'],
|
||||
$capturedImageBase64
|
||||
);
|
||||
|
||||
if ($comparisonResult['error']) {
|
||||
$this->updateVerificationStatus($token, 'error', $comparisonResult, $capturedPhotoPath);
|
||||
|
||||
$this->assure_api->maj_demandereconnaissancefaciale("2"); // Echec
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'message' => $comparisonResult['error'],
|
||||
'attempts_remaining' => $this->maxAttempts - ($request['attempts'] + 1)
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'message' => $comparisonResult['error'],
|
||||
// 'attempts_remaining'=> $this->maxAttempts - ($request['attempts'] + 1)
|
||||
'attempts_remaining'=> ($this->maxAttempts - $request['attempts'])
|
||||
];
|
||||
}
|
||||
|
||||
// 5. Seuil de confiance minimum (ex: 80%)
|
||||
// 5. Seuil de confiance minimum (80%)
|
||||
$confidenceThreshold = 80;
|
||||
$isMatch = $comparisonResult['match'] && $comparisonResult['confidence'] >= $confidenceThreshold;
|
||||
|
||||
if ($isMatch) {
|
||||
// Succès: créer une session d'autorisation
|
||||
$this->updateVerificationStatus($token, 'verified', $comparisonResult, $capturedPhotoPath);
|
||||
$sessionToken = $this->createAuthorizationSession($request['assure_id'], $request['id']);
|
||||
|
||||
$this->assure_api->maj_demandereconnaissancefaciale("1"); // Vérifié
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'match' => true,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Identité vérifiée avec succès',
|
||||
'session_token' => $sessionToken,
|
||||
'redirect_url' => 'saisie_prestations.php?token=' . $sessionToken
|
||||
'success' => true,
|
||||
'match' => true,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Identité vérifiée avec succès',
|
||||
];
|
||||
} else {
|
||||
// Échec de correspondance
|
||||
$attemptsRemaining = $this->maxAttempts - ($request['attempts'] + 1);
|
||||
|
||||
if ($attemptsRemaining > 0) {
|
||||
$this->updateVerificationStatus($token, 'pending', $comparisonResult, $capturedPhotoPath);
|
||||
|
||||
$this->assure_api->maj_demandereconnaissancefaciale("1"); // En attente
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Votre visage ne correspond pas',
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Votre visage ne correspond pas',
|
||||
'attempts_remaining' => $attemptsRemaining
|
||||
];
|
||||
} else {
|
||||
$this->updateVerificationStatus($token, 'failed', $comparisonResult, $capturedPhotoPath);
|
||||
|
||||
$this->assure_api->maj_demandereconnaissancefaciale("2"); // Echec
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Vérification échouée. Nombre maximum de tentatives atteint.',
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Vérification échouée. Nombre maximum de tentatives atteint.',
|
||||
'attempts_remaining' => 0
|
||||
];
|
||||
}
|
||||
|
|
@ -387,7 +221,7 @@ class FacialVerificationAPI {
|
|||
error_log("Erreur verifyFace: " . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'match' => false,
|
||||
'message' => 'Erreur lors de la vérification: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
|
|
@ -396,12 +230,11 @@ class FacialVerificationAPI {
|
|||
|
||||
// Traiter la requête
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$action = $input['action'] ?? null;
|
||||
|
||||
$db = new Database();
|
||||
$api = new FacialVerificationAPI($db->getConnection());
|
||||
|
||||
$api = new FacialVerificationAPI();
|
||||
|
||||
switch ($action) {
|
||||
case 'validate_token':
|
||||
$token = $input['token'] ?? null;
|
||||
|
|
@ -415,7 +248,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
case 'verify_face':
|
||||
$token = $input['token'] ?? null;
|
||||
$image = $input['image'] ?? null;
|
||||
|
||||
|
||||
if (!$token || !$image) {
|
||||
echo json_encode(['success' => false, 'message' => 'Token et image requis']);
|
||||
exit;
|
||||
|
|
@ -427,5 +260,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
echo json_encode(['success' => false, 'message' => 'Action invalide']);
|
||||
}
|
||||
} else {
|
||||
// echo json_encode(['success' => false, 'message' => 'Méthode non autorisée']);
|
||||
ob_end_clean(); // ✅ vide le buffer avant d'envoyer le JSON
|
||||
echo json_encode(['success' => false, 'message' => 'Méthode non autorisée']);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,103 +1,95 @@
|
|||
<?php //ICB0 71:0 81:d29 82:16fb ?><?php //002cd
|
||||
<?php //ICB0 71:0 81:c6e 82:1551 ?><?php //002cd
|
||||
if(extension_loaded('ionCube Loader')){die('The file '.__FILE__." is corrupted.\n");}echo("\nScript error: the ".(($cli=(php_sapi_name()=='cli')) ?'ionCube':'<a href="https://www.ioncube.com">ionCube</a>')." Loader for PHP needs to be installed.\n\nThe ionCube Loader is the industry standard PHP extension for running protected PHP code,\nand can usually be added easily to a PHP installation.\n\nFor Loaders please visit".($cli?":\n\nhttps://get-loader.ioncube.com\n\nFor":' <a href="https://get-loader.ioncube.com">get-loader.ioncube.com</a> and for')." an instructional video please see".($cli?":\n\nhttp://ioncu.be/LV\n\n":' <a href="http://ioncu.be/LV">http://ioncu.be/LV</a> ')."\n\n");exit(199);
|
||||
?>
|
||||
HR+cPtYd1J4dZVu6FX6G4iz1dVp/K6jj0JDUpj99vwoqWdCzwGp6DbXoMlG4QLUSH+U54yJIzBHT
|
||||
dyirzWKO5vLgvYWHCejG9I50gmjorua1MYWoybriEwNVlLtKlVDjfFZ7lu3Zh9Tkr8yST5sBJ2oI
|
||||
86nTgnKT7Kum5FsaXMZyVTEX6JwTyNPXXDNWlBgcmIaUSVpRIQfuB9O8eggHEue5pUB4bNIDeaDs
|
||||
EOyj322Wx5OKnT92GeYLI+bRfm8OR0EFpvmbsPhEWsQYLIy7KPtDX3Jlg5LePRMa8oGNwwpbvp+o
|
||||
m6WNLVy+ni5Z+dzyLbkJ+AP2yA4gunvd4RGTIoq7YriTc8x0hHfxTrfLFxhz3EKxTO0dL+D6T6qO
|
||||
laMY1bLISIsy5yfoywm+jbZNkbhxalCbXbiRcERWr+UgUST9g6aZXkK2eRj9aAoAa0GPy4uRh+Fd
|
||||
hmgPq4cyKQLmkyDFhMPMO2awLd6q7QvfslSs45KBYD2cCXt7G8XThskKxXI5mqHwx+qcAUcOnRlF
|
||||
WvCTdBZ79rMMM8OspbN2PJ+MPsp1ONOIL/hkA2QNvPq3ZI2SBekfUPylesYQo4wFJiYcWJGE0BYY
|
||||
+OdfOrvW+W8NfZ2iNOgs8Y91EV/gTPls9oVrszG849Lr5MFY7sgRasALa/tziw5PHCTnWvU+4fKl
|
||||
B0Tg+F2nb0V7Z1r3uLDhd0+QXExsuAM/1m4BoEda33ROqiOD9MhI2kZTszxoWjXNZp3yH4Y1Edpz
|
||||
VRMR55wigBpzbqLMs+oaXHOIZunmJRFBC9VJhSYeCLTgVzIDXHDD8Pozgwyj8Wolpm726jFjfe7q
|
||||
KHAjDp1yWSXbkM9g6+GtTsd2ZaPxjQ/7zlavgUclKnTsUlQKrj8hEM5s20zdgGBzWXRhpW+gt64H
|
||||
zGTH4SFLH19bs+N+OU/41KEC56c++s0lP7ru9/z6L8bbP1RUS/iEzWEx7ph+YkLhKyyZrXrBc4Dx
|
||||
KVoN97r8ArwnxWp/soQwi4JHQRHm3zjuz0Da1fy0RYGw/9vP7z7IIOo4n3XPoK8kGuK64gQbCPYK
|
||||
+AXE3jH6mfLpjyu1OgXYFWdF0aP/twoKR6i6IJgjqixNbDtSIL7RdPIA7drWHWsnQkVPeDFfdLZD
|
||||
NOjsJhmxkIKdk2Hv8pQYyTmfd74svZFj9xFFdawaDzYoCg8kP2fbtsRiwbaV/h/1SEIdu3uEX82E
|
||||
tWIxQiFJpdcLfPYRzsftu4jrz3OtQDCnyGX2qXu4elEFNXRZIFnqEvtrNdXavjlU55k63z0+zZil
|
||||
cvskB89f7h96gvaMsS9PclgKJlit8jj9mXsQHpDmrNUj0GnhvWKXL0GoDivEb71E+Z7xpQdEuDjR
|
||||
Z686ZVW9rEcvVpCz3X4Tjc8R9Pw26OtMsD9aR0NPgCZZ5ETF1WzFr5oPpbU8ZF2kFRo4+O1JJpHX
|
||||
XNDMOqiNh2eTQATGXpx58XNYCOBWIP33DqDX80Z/9v1BD2GN+y+1sHd3y3hGm/acemwyJZH2BqFA
|
||||
uuIGORHGD089CDiGwWXhaLrF8yVJrdfbAZgHqkqYOauPFnTZyPAhAAGZWN5MrktI+Z0u7qbevzWg
|
||||
118cxF7XHBSVxyGbJcOkd89a26v0i/mQD0bv9nYAiAgS3fVrz4Ouh4v/cVQB+HHh5oMXB/x0eu6X
|
||||
qmiJUD6j0USKU7HmsqL5E2B9/JtgbgWYSO7UYwKjYLS/grvw0y6H0qg6zzKnGtqmSD/tmJISSnQz
|
||||
96UD/DFDrZixWlQ05sbQXPiqndQ2sZNYUmNWH3s0gEb67QmMqdIDqvMwwYuXUgMm8hk2hQqcfQP4
|
||||
f+f6JIEqzo4mJEmfNFaGxVnq0Rm+YXQvUVHn8Ux7k6BFBVCdryy1mNhCOa1ePouYVIGJduCdauXY
|
||||
EhoaU/sFI1IvOZLprQTqTVnwc/6/HtD2kd0Yro8xzshImUwojukKVW5/tCQhuTk/fhuj9AC9LWHE
|
||||
RKNGwBztZ7VLav8HkqDivqRxNtyp0qdYqMQEOePnjKCeZvMkONMIvcxv3/Sx+pIL2yKVLMsYB4gd
|
||||
6WOAzgWlCRZZVqhRpOuD8qIChXW/49SN6OSCXHk3rTMKFsdzYCjIp0ORGPkYDVH69VLe+8iQQXsZ
|
||||
Zk3znquFkcxaiwyi53+Egg4gkG32ArO8hWHIxISRGg5IVbYt3VRP8Ezkyi5ch0LZHbOiP1VO5snm
|
||||
jhNVDFQcHaAkcHvkBmM3LtZ/89FzUxFgsfQKkHTfs77MkaUdyQ+Xp9CPsiI4Zgs74Tz1CwdLajaB
|
||||
Lv4Dz/oER0264sM8EbjhpbVEhrDTqO2uu+kvFbNVrMfpJZkicTivmXyqfwDBc/2UIu3+cdaZi6N/
|
||||
CNqfvWvPtO1dSGH2V0x4XRALkQpdQwDijzgCs0A8IryPDzmSfpZSDKubuT7m3jzMs+OIRZsRZCno
|
||||
0Lx07g3tCrcJejSubHKgEcNILwpe6+XdfCYBuaPtT+OBXD32iHCXQyWfDuQGsj1Udalpubu/Ofn+
|
||||
PgbIFx5TOx14nOU4WSnteAB407Mo0vV5j/MJiz+V5rTXTxQVMR3nHILh7fG0Hwd4x67K39IsWep/
|
||||
jAehVfImOjaXCRIDYTAGlkLNTri==
|
||||
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=
|
||||
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
|
||||
tzPK2sTgEiWU7VwUrPD7V7j87jqRUCGNislDCle==
|
||||
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
|
||||
h6T2h1NpQMwMztYhVGtjiD3Cwf4=
|
||||
1
Societes/envoyx/test
Normal file
1
Societes/envoyx/test
Normal file
|
|
@ -0,0 +1 @@
|
|||
tests
|
||||
1
Societes/fideliasn/test
Normal file
1
Societes/fideliasn/test
Normal file
|
|
@ -0,0 +1 @@
|
|||
tests
|
||||
1
Societes/generaliacm/test
Normal file
1
Societes/generaliacm/test
Normal file
|
|
@ -0,0 +1 @@
|
|||
tests
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
|
||||
require_once 'gabarit_queries.php';
|
||||
require_once 'gabarit_queries.php';
|
||||
$gabary = new Gabary();
|
||||
|
||||
$_SESSION['firstLevelMenu']='';
|
||||
|
|
@ -55,7 +54,6 @@
|
|||
<link href="Bootstrap/vendor/remixicon/remixicon.css" rel="stylesheet">
|
||||
<link href="Bootstrap/vendor/simple-datatables/style.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link href="Bootstrap_new/css/datatables.min.css" rel="stylesheet">
|
||||
<link href="Bootstrap_new/css/select2.min.css" rel="stylesheet">
|
||||
<link href="Bootstrap/css/style.css?ver=2024.03.14.00" rel="stylesheet">
|
||||
|
|
@ -189,8 +187,6 @@
|
|||
<!-- End Icons Navigation -->
|
||||
</header><!-- End Header -->
|
||||
|
||||
|
||||
|
||||
<!-- ======= Sidebar ======= -->
|
||||
|
||||
<aside id="sidebar" class="sidebar">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user