342 lines
11 KiB
PHP
342 lines
11 KiB
PHP
<?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']);
|
|
}
|