diff --git a/Contestation/Demandereconnaissancefaciale.php b/Contestation/Demandereconnaissancefaciale.php new file mode 100755 index 0000000..e7a3a5a --- /dev/null +++ b/Contestation/Demandereconnaissancefaciale.php @@ -0,0 +1,361 @@ + + + + + + Erreur + + + + + +
+
+
+ +
+

Erreur

+

{$message}

+
+
+ + "; + 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); +?> + + + + + + + <?= htmlspecialchars($title) ?> + + + + + + +
+
+
+

+ + +

+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ 0/1000 +
+
+ + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/Contestation/Demandereconnaissancefaciale_old.php b/Contestation/Demandereconnaissancefaciale_old.php new file mode 100755 index 0000000..e7a3a5a --- /dev/null +++ b/Contestation/Demandereconnaissancefaciale_old.php @@ -0,0 +1,361 @@ + + + + + + Erreur + + + + + +
+
+
+ +
+

Erreur

+

{$message}

+
+
+ + "; + 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); +?> + + + + + + + <?= htmlspecialchars($title) ?> + + + + + + +
+
+
+

+ + +

+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ 0/1000 +
+
+ + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/Contestation/check_verification_status.php b/Contestation/check_verification_status.php new file mode 100644 index 0000000..78bd991 --- /dev/null +++ b/Contestation/check_verification_status.php @@ -0,0 +1,58 @@ + 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'] +]); diff --git a/Contestation/facial_verification.php b/Contestation/facial_verification.php new file mode 100644 index 0000000..ca9effa --- /dev/null +++ b/Contestation/facial_verification.php @@ -0,0 +1,513 @@ + + + + + + INTER-SANTÉ - Vérification d'identité + + + +
+
+

🏥 INTER-SANTÉ

+

Vérification d'identité sécurisée

+
+ +
+ +
+
+

Vérification du lien...

+
+ + +
+
+

Instructions pour la vérification

+
    +
  • Positionnez votre visage dans l'ovale
  • +
  • Assurez-vous d'être dans un endroit bien éclairé
  • +
  • Regardez directement la caméra
  • +
  • Restez immobile lors de la capture
  • +
  • Retirez lunettes de soleil, casquette ou masque
  • +
+
+ +
+ + +
+
+ + +
+
+
+ Positionnez votre visage dans l'ovale +
+ +
+ + +
+

Confirmez votre photo

+ Votre photo + + +
+ + +
+
+

+ Vérification de votre identité en cours...
+ Merci de patienter +

+
+ + +
+
+
+

+

+
+
+
+ + +
+
+

❌ Erreur

+

+
+
+
+
+ + + + diff --git a/Contestation/integration_example.php b/Contestation/integration_example.php new file mode 100644 index 0000000..0a82b30 --- /dev/null +++ b/Contestation/integration_example.php @@ -0,0 +1,341 @@ +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) { + ?> + + + + + + Vérification en attente - INTER-SANTÉ + + + +
+

🏥 INTER-SANTÉ

+

Vérification d'identité en cours

+ +
+

+

Carte N°

+
+ +
+ +

+ Un lien de vérification a été envoyé à l'assuré.
+ En attente de la vérification faciale... +

+ +
+ +
✓ Email envoyé
+ +
✗ Email non envoyé
+ + + +
✓ WhatsApp envoyé
+ +
✗ WhatsApp non envoyé
+ +
+ +

+ Le lien de vérification est valable pendant 15 minutes +

+
+ + + + + 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']); +} diff --git a/Contestation/verify_facial_api.php b/Contestation/verify_facial_api.php new file mode 100644 index 0000000..63d5911 --- /dev/null +++ b/Contestation/verify_facial_api.php @@ -0,0 +1,431 @@ +db = $db; + } + + /** + * Valide un token de vérification + */ + public function validateToken($token) { + 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); + + 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; // 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 + + 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); // 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); + + 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 (ex: 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']); + + 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 { + // Échec de correspondance + $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; + + $db = new Database(); + $api = new FacialVerificationAPI($db->getConnection()); + + 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']); +}