a
This commit is contained in:
parent
99e2bf19e3
commit
35c4b74049
|
|
@ -573,7 +573,6 @@
|
|||
|
||||
function validateToken() {
|
||||
$.ajax({
|
||||
// url: 'verify_facial_api.php',
|
||||
url: '/Contestation/verify_facial_api.php',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
|
|
@ -583,8 +582,6 @@
|
|||
}),
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
// alert("succes => " + data);
|
||||
// console.log('Réponse JSON :', data);
|
||||
if (data.success) {
|
||||
showStep('step-instructions');
|
||||
} else {
|
||||
|
|
@ -592,8 +589,7 @@
|
|||
}
|
||||
},
|
||||
error: function(xhr, status, err) {
|
||||
//console.log("Demande :", xhr.responseText); // ✅ voir le HTML retourné
|
||||
showError('Demande : Erreur de connexion au serveur');
|
||||
showError('Erreur de connexion au serveur');
|
||||
// showError(xhr.responseText);
|
||||
}
|
||||
});
|
||||
|
|
@ -664,6 +660,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function verifyPhoto() {
|
||||
showStep('step-verifying');
|
||||
|
||||
|
|
@ -690,7 +687,38 @@
|
|||
showError('Erreur lors de la vérification: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
function verifyPhoto() {
|
||||
showStep('step-verifying');
|
||||
$.ajax({
|
||||
url: 'verify_facial_api.php',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
action: 'verify_face',
|
||||
token: verificationToken,
|
||||
image: capturedImage
|
||||
}),
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showSuccess(data) {
|
||||
document.getElementById('result-icon').textContent = '✅';
|
||||
document.getElementById('result-title').textContent = 'Identité vérifiée !';
|
||||
|
|
|
|||
751
Contestation/Demandereconnaissancefaciale_2026_02_23_08h.php
Executable file
751
Contestation/Demandereconnaissancefaciale_2026_02_23_08h.php
Executable file
|
|
@ -0,0 +1,751 @@
|
|||
<?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['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>");
|
||||
}
|
||||
|
||||
$_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>");
|
||||
}
|
||||
|
||||
$demande = $assure->checkdemandereconnaissancefaciale_id();
|
||||
|
||||
if(!$demande) {
|
||||
$message = $codeLangue == 'en_US' ? "No requests found!" : "Aucune demande trouvée!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
$idDemande = $demande["idDemande"];
|
||||
$codeEtat = $demande["codeEtat"];
|
||||
$demandeExpiree = $demande["demandeExpiree"];
|
||||
$dateExpirationFr = $demande["dateExpirationFr"];
|
||||
$dateExpirationEng = $demande["dateExpirationEng"];
|
||||
$etatDemande = $demande["etatDemande"];
|
||||
$etatDemandeEng = $demande["etatDemandeEng"];
|
||||
|
||||
$beneficiaire = $demande["beneficiaire"];
|
||||
$prestataire = $demande["prestataire"];
|
||||
|
||||
// var_dump($demande);
|
||||
/*
|
||||
idDemande
|
||||
codePrestataire
|
||||
idBeneficiaire
|
||||
codeEtat
|
||||
nbTentative
|
||||
lienPhotoWebcam
|
||||
dateExpiration
|
||||
dateExpirationFr
|
||||
dateExpirationEng
|
||||
demandeExpiree
|
||||
beneficiaire
|
||||
prestataire
|
||||
*/
|
||||
|
||||
/*
|
||||
var_dump(
|
||||
array(
|
||||
// "bdd" => $bdd,
|
||||
"idDemande" => $idDemande,
|
||||
"codeEtat" => $codeEtat,
|
||||
"demandeExpiree" => $demandeExpiree,
|
||||
"etatDemande" => $etatDemande,
|
||||
"etatDemandeEng" => $etatDemandeEng,
|
||||
|
||||
"beneficiaire" => $beneficiaire,
|
||||
"prestataire" => $prestataire,
|
||||
)
|
||||
|
||||
);
|
||||
*/
|
||||
|
||||
/*
|
||||
codeEtat libelle
|
||||
3 Expiré
|
||||
1 Vérifié
|
||||
2 Echec
|
||||
4 Erreur
|
||||
0 En attente
|
||||
*/
|
||||
|
||||
if($codeEtat=="3" || $demandeExpiree=="1") {
|
||||
$message = $codeLangue == 'en_US' ? "Request expired since $dateExpirationEng !" : "Demande expirée depuis $dateExpirationFr !";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
if($codeEtat=="1") {
|
||||
$message = $codeLangue == 'en_US' ? "Verification already completed!" : "Vérification déjà exffectuée!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
if($codeEtat=="2") {
|
||||
$message = $codeLangue == 'en_US' ? "Failure, please try again!" : "Echec, veuilez réessayer!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
if($codeEtat=="4") {
|
||||
$message = $codeLangue == 'en_US' ? "Error, please try again!" : "Erreur, veuilez réessayer!";
|
||||
afficherMessage("<strong>$message</strong>");
|
||||
}
|
||||
|
||||
/*
|
||||
$message = $codeLangue == 'en_US'
|
||||
? "Parameters successfully verified!"
|
||||
: "Paramètres vérifiés avec succès!";
|
||||
afficherMessage("<strong>$message</strong>", false);
|
||||
*/
|
||||
|
||||
$param_societe = $assure->get_parametres_societe($codeSociete);
|
||||
|
||||
$_SESSION['dureeToken'] = $param_societe["dureeTokenReconnaissanceFaciale"];
|
||||
$_SESSION['dossierPhoto'] = $param_societe["dossierPhoto"];
|
||||
$_SESSION['nbTentative'] = $param_societe["nbTentativeBiometrie"];
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// echo $message;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!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;">Demande 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 src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
|
||||
<script>
|
||||
let video = document.getElementById('video');
|
||||
let canvas = document.getElementById('canvas');
|
||||
let context = canvas.getContext('2d');
|
||||
let stream = null;
|
||||
let verificationToken = null;
|
||||
let capturedImage = null;
|
||||
|
||||
// Récupérer le token depuis l'URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
// verificationToken = urlParams.get('token');
|
||||
|
||||
verificationToken = <?= $_SESSION['idDemande'] ?> ;
|
||||
|
||||
// alert("verificationToken = "+verificationToken);
|
||||
|
||||
// Initialisation
|
||||
window.onload = function() {
|
||||
if (!verificationToken) {
|
||||
showError('Lien de vérification invalide');
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier la validité du token
|
||||
validateToken();
|
||||
};
|
||||
|
||||
function validateToken() {
|
||||
$.ajax({
|
||||
// url: 'verify_facial_api.php',
|
||||
url: '/Contestation/verify_facial_api.php',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
action: 'validate_token',
|
||||
token: verificationToken
|
||||
}),
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
// alert("succes => " + data);
|
||||
// console.log('Réponse JSON :', data);
|
||||
if (data.success) {
|
||||
showStep('step-instructions');
|
||||
} else {
|
||||
showError(data.message || 'Lien expiré ou invalide');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, err) {
|
||||
//console.log("Demande :", xhr.responseText); // ✅ voir le HTML retourné
|
||||
showError('Demande : Erreur de connexion au serveur');
|
||||
// showError(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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>
|
||||
|
|
@ -25,13 +25,6 @@ class FacialVerificationAPI {
|
|||
* Valide un token de vérification
|
||||
*/
|
||||
public function validateToken($token) {
|
||||
|
||||
/*
|
||||
$request = $this->assure_api->valider_token();
|
||||
var_dump($request);
|
||||
exit;
|
||||
*/
|
||||
|
||||
try {
|
||||
$request = $this->assure_api->valider_token();
|
||||
|
||||
|
|
@ -294,6 +287,10 @@ class FacialVerificationAPI {
|
|||
* Vérifie le visage capturé
|
||||
*/
|
||||
public function verifyFace($token, $capturedImageBase64) {
|
||||
$request = $this->assure_api->valider_token();
|
||||
var_dump($request);
|
||||
exit;
|
||||
|
||||
try {
|
||||
// 1. Récupérer les infos de la demande
|
||||
$sql = "SELECT vr.*, a.photo_reference_path, a.id as assure_id
|
||||
|
|
|
|||
433
Contestation/verify_facial_api_2026_02_23_08h.php
Normal file
433
Contestation/verify_facial_api_2026_02_23_08h.php
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
<?php
|
||||
ob_start();
|
||||
require_once "Assure.php";
|
||||
session_start();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
ob_end_clean();
|
||||
echo json_encode(['success' => false, 'message' => 'Méthode non autorisée']);
|
||||
exit;
|
||||
}
|
||||
|
||||
class FacialVerificationAPI {
|
||||
private $assure_api;
|
||||
private $maxAttempts = 3;
|
||||
|
||||
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) {
|
||||
|
||||
/*
|
||||
$request = $this->assure_api->valider_token();
|
||||
var_dump($request);
|
||||
exit;
|
||||
*/
|
||||
|
||||
try {
|
||||
$request = $this->assure_api->valider_token();
|
||||
|
||||
if (!$request) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Lien expiré ou invalide'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Token valide',
|
||||
'assure' => [
|
||||
'nom' => $request['nom'],
|
||||
'prenoms' => $request['prenoms']
|
||||
]
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Erreur validateToken: " . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Erreur serveur'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare deux visages avec l'API de reconnaissance faciale
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparaison avec Azure Face API
|
||||
*/
|
||||
private function compareWithAzureFaceAPI($referenceImagePath, $capturedImageBase64) {
|
||||
$endpoint = AZURE_FACE_ENDPOINT;
|
||||
$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];
|
||||
$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';
|
||||
|
||||
try {
|
||||
$rekognitionClient = new Aws\Rekognition\RekognitionClient([
|
||||
'version' => 'latest',
|
||||
'region' => AWS_REGION,
|
||||
'credentials' => [
|
||||
'key' => AWS_ACCESS_KEY_ID,
|
||||
'secret' => AWS_SECRET_ACCESS_KEY
|
||||
]
|
||||
]);
|
||||
|
||||
$referenceImageData = file_get_contents($referenceImagePath);
|
||||
$capturedImageData = base64_decode(explode(',', $capturedImageBase64)[1]);
|
||||
|
||||
$result = $rekognitionClient->compareFaces([
|
||||
'SourceImage' => ['Bytes' => $referenceImageData],
|
||||
'TargetImage' => ['Bytes' => $capturedImageData],
|
||||
'SimilarityThreshold' => 80
|
||||
]);
|
||||
|
||||
if (empty($result['FaceMatches'])) {
|
||||
return [
|
||||
'match' => false,
|
||||
'confidence' => 0,
|
||||
'error' => 'Les visages ne correspondent pas'
|
||||
];
|
||||
}
|
||||
|
||||
$similarity = $result['FaceMatches'][0]['Similarity'];
|
||||
|
||||
return [
|
||||
'match' => $similarity >= 80,
|
||||
'confidence' => round($similarity, 2),
|
||||
'error' => null
|
||||
];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Erreur AWS Rekognition: " . $e->getMessage());
|
||||
return [
|
||||
'match' => false,
|
||||
'confidence' => 0,
|
||||
'error' => 'Erreur lors de la vérification faciale'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
$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);
|
||||
|
||||
if (!$request) {
|
||||
return [
|
||||
'success' => 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');
|
||||
return [
|
||||
'success' => 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'],
|
||||
$capturedImageBase64
|
||||
);
|
||||
|
||||
if ($comparisonResult['error']) {
|
||||
$this->updateVerificationStatus($token, 'error', $comparisonResult, $capturedPhotoPath);
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'message' => $comparisonResult['error'],
|
||||
'attempts_remaining'=> $this->maxAttempts - ($request['attempts'] + 1)
|
||||
];
|
||||
}
|
||||
|
||||
// 5. Seuil de confiance minimum (80%)
|
||||
$confidenceThreshold = 80;
|
||||
$isMatch = $comparisonResult['match'] && $comparisonResult['confidence'] >= $confidenceThreshold;
|
||||
|
||||
if ($isMatch) {
|
||||
$this->updateVerificationStatus($token, 'verified', $comparisonResult, $capturedPhotoPath);
|
||||
$sessionToken = $this->createAuthorizationSession($request['assure_id'], $request['id']);
|
||||
|
||||
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
|
||||
];
|
||||
} else {
|
||||
$attemptsRemaining = $this->maxAttempts - ($request['attempts'] + 1);
|
||||
|
||||
if ($attemptsRemaining > 0) {
|
||||
$this->updateVerificationStatus($token, 'pending', $comparisonResult, $capturedPhotoPath);
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Votre visage ne correspond pas',
|
||||
'attempts_remaining' => $attemptsRemaining
|
||||
];
|
||||
} else {
|
||||
$this->updateVerificationStatus($token, 'failed', $comparisonResult, $capturedPhotoPath);
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'confidence' => $comparisonResult['confidence'],
|
||||
'message' => 'Vérification échouée. Nombre maximum de tentatives atteint.',
|
||||
'attempts_remaining' => 0
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Erreur verifyFace: " . $e->getMessage());
|
||||
return [
|
||||
'success' => false,
|
||||
'match' => false,
|
||||
'message' => 'Erreur lors de la vérification: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Traiter la requête
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$action = $input['action'] ?? null;
|
||||
|
||||
$api = new FacialVerificationAPI();
|
||||
|
||||
switch ($action) {
|
||||
case 'validate_token':
|
||||
$token = $input['token'] ?? null;
|
||||
if (!$token) {
|
||||
echo json_encode(['success' => false, 'message' => 'Token requis']);
|
||||
exit;
|
||||
}
|
||||
echo json_encode($api->validateToken($token));
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
echo json_encode($api->verifyFace($token, $image));
|
||||
break;
|
||||
|
||||
default:
|
||||
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']);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user