Connexion tocken, stripe 3D, tracking analytic

This commit is contained in:
Oleg Barykin committed 2026-03-11 21:44:19 +01:00
1 parent 1deb30fcd1
commit bd311da711
17 files changed
+3324 -455

No files matched your search

+58 -79
View File
@@ -1,36 +1,37 @@
<?php
declare(strict_types=1);
declare (strict_types = 1);
require_once __DIR__ . '/../includes/bootstrap_global.php';
/**
* UI helper: affiche une jolie carte centrée puis exit.
* $type: 'error' | 'info' | 'success'
*/
function ui_exit(string $title, string $message, int $status = 400, string $type = 'error', ?string $cta_href = '/', ?string $cta_label = null): void {
function ui_exit(string $title, string $message, int $status = 400, string $type = 'error', ?string $cta_href = '/', ?string $cta_label = null): void
{
http_response_code($status);
$title = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
$message = nl2br(htmlspecialchars($message, ENT_QUOTES, 'UTF-8'));
$cta_href = $cta_href ? htmlspecialchars($cta_href, ENT_QUOTES, 'UTF-8') : null;
$cta_label = $cta_label ?? 'OK';
// Styles d'icône selon le type
// Styles d'icône selon le type
$iconBorder = '#B91C1C'; // rouge par défaut (error)
$iconBg = 'rgba(185,28,28,.08)';
$iconRing = 'rgba(185,28,28,.25)';
$checkCSS = ''; // pour success
if ($type === 'info') {
$iconBorder = '#1D4ED8';
$iconBg = 'rgba(29,78,216,.08)';
$iconRing = 'rgba(29,78,216,.25)';
$iconBg = 'rgba(29,78,216,.08)';
$iconRing = 'rgba(29,78,216,.25)';
} elseif ($type === 'success') {
$iconBorder = '#1E8050';
$iconBg = 'rgba(30,128,80,.08)';
$iconRing = 'rgba(30,128,80,.20)';
$checkCSS = 'width:48px;height:36px;border:5px solid #1e8050;border-top:0;border-left:0;transform:rotate(45deg);';
$iconBg = 'rgba(30,128,80,.08)';
$iconRing = 'rgba(30,128,80,.20)';
$checkCSS = 'width:48px;height:36px;border:5px solid #1e8050;border-top:0;border-left:0;transform:rotate(45deg);';
}
echo '<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">';
echo '<title>'.$title.'</title>';
echo '<title>' . $title . '</title>';
echo '<style>
html,body{height:100%;margin:0}
body{display:flex;align-items:center;justify-content:center;background:#8CA9FF;font-family:Montserrat,system-ui,Segoe UI,Roboto,Arial}
@@ -44,26 +45,26 @@ function ui_exit(string $title, string $message, int $status = 400, string $type
</style></head><body><div class="card">';
// Icône
echo '<div style="width:102px;height:102px;border-radius:50%;background:'.$iconBg.';border:3px solid '.$iconRing.';display:flex;align-items:center;justify-content:center;margin:0 auto 20px;">';
echo '<div style="width:102px;height:102px;border-radius:50%;background:' . $iconBg . ';border:3px solid ' . $iconRing . ';display:flex;align-items:center;justify-content:center;margin:0 auto 20px;">';
if ($type === 'success') {
echo '<div style="'.$checkCSS.'"></div>';
echo '<div style="' . $checkCSS . '"></div>';
} elseif ($type === 'info') {
echo '<div style="width:14px;height:48px;border-left:5px solid '.$iconBorder.';position:relative;">
<div style="position:absolute;top:-18px;left:-5px;width:10px;height:10px;background:'.$iconBorder.';border-radius:50%;"></div>
echo '<div style="width:14px;height:48px;border-left:5px solid ' . $iconBorder . ';position:relative;">
<div style="position:absolute;top:-18px;left:-5px;width:10px;height:10px;background:' . $iconBorder . ';border-radius:50%;"></div>
</div>';
} else { // error
echo '<div style="position:relative;width:48px;height:48px;">
<div style="position:absolute;top:0;left:22px;width:5px;height:48px;background:'.$iconBorder.';transform:rotate(45deg);border-radius:2px;"></div>
<div style="position:absolute;top:0;left:22px;width:5px;height:48px;background:'.$iconBorder.';transform:rotate(-45deg);border-radius:2px;"></div>
<div style="position:absolute;top:0;left:22px;width:5px;height:48px;background:' . $iconBorder . ';transform:rotate(45deg);border-radius:2px;"></div>
<div style="position:absolute;top:0;left:22px;width:5px;height:48px;background:' . $iconBorder . ';transform:rotate(-45deg);border-radius:2px;"></div>
</div>';
}
echo '</div>';
echo '<h1>'.$title.'</h1>';
echo '<p>'.$message.'</p>';
echo '<h1>' . $title . '</h1>';
echo '<p>' . $message . '</p>';
if ($cta_href) {
echo '<div class="row"><a class="btn btn-outline" href="'.$cta_href.'">'.$cta_label.'</a></div>';
echo '<div class="row"><a class="btn btn-outline" href="' . $cta_href . '">' . $cta_label . '</a></div>';
}
echo '</div></body></html>';
@@ -73,10 +74,10 @@ function ui_exit(string $title, string $message, int $status = 400, string $type
// =====================
// 1) Token présent & valide
// =====================
$rawToken = trim((string)($_GET['token'] ?? ''));
$rawAL = trim((string)($_GET['al'] ?? ''));
if ($rawAL !== '' && !preg_match('/^[A-Fa-f0-9]{64}$/', $rawAL)) {
$rawAL = ''; // on ignore si format suspect
$rawToken = trim((string) ($_GET['token'] ?? ''));
$rawAL = trim((string) ($_GET['al'] ?? ''));
if ($rawAL !== '' && ! preg_match('/^[A-Fa-f0-9]{64}$/', $rawAL)) {
$rawAL = ''; // on ignore si format suspect
}
if ($rawToken === '') {
@@ -90,7 +91,7 @@ if ($rawToken === '') {
);
}
// Optionnel : on vérifie le format attendu (bin2hex(32) => 64 hexdigits)
if (!preg_match('/^[A-Fa-f0-9]{64}$/', $rawToken)) {
if (! preg_match('/^[A-Fa-f0-9]{64}$/', $rawToken)) {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_lien_invalide'] ?? 'Lien invalide.',
@@ -108,7 +109,7 @@ $stmt = $pdo->prepare("SELECT * FROM compte WHERE token_validation = :token LIMI
$stmt->execute(['token' => $rawToken]);
$compte = $stmt->fetch();
if (!$compte) {
if (! $compte) {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_token_invalide'] ?? 'Token invalide.',
@@ -123,56 +124,31 @@ if (!$compte) {
// 3) Validité temporelle (6h)
// =====================
try {
$created_at = new DateTime($compte['cree_le']);
if (empty($compte['token_validation_expire'])) {
throw new RuntimeException('token_validation_expire manquant');
}
$expireAt = new DateTime((string) $compte['token_validation_expire']);
} catch (Throwable $e) {
// Par prudence : si la date est illisible, on refuse
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_lien_invalide'] ?? 'Lien invalide.',
$translations['tvalidat_err_lien_invalide'] ?? $translations['err_lien_invalide'] ?? 'Lien invalide.',
400,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
$now = new DateTime();
$diffH = ($now->getTimestamp() - $created_at->getTimestamp()) / 3600;
if ($diffH > 6) {
// Supprimer les enregistrements expirés
try {
$pdo->beginTransaction();
$pdo->prepare("DELETE FROM compte WHERE id = :id")->execute(['id' => $compte['id']]);
$ref_id = (int)$compte['reference_id'];
if (($compte['role'] ?? '') === 'praticien') {
$pdo->prepare("DELETE FROM praticien WHERE id = :id")->execute(['id' => $ref_id]);
} else {
$pdo->prepare("DELETE FROM client WHERE id = :id")->execute(['id' => $ref_id]);
}
$pdo->commit();
} catch (Throwable $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
error_log('Validation email - purge expirée échouée: '.$e->getMessage());
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_serveur'] ?? 'Erreur serveur.',
500,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
if ($now > $expireAt) {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_lien_expire'] ?? 'Le lien de validation a expiré.',
$translations['tvalidat_err_lien_expire'] ?? $translations['err_lien_expire'] ?? 'Le lien de validation a expiré.',
410,
'info',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
'/connexion.php',
$translations['btn_se_connecter'] ?? 'Se connecter'
);
}
@@ -180,10 +156,16 @@ if ($diffH > 6) {
// 4) Valider l'email (maj compte)
// =====================
try {
$stmt = $pdo->prepare("UPDATE compte SET email_valide = 1, token_validation = NULL WHERE id = :id");
$stmt = $pdo->prepare("
UPDATE compte
SET email_valide = 1,
token_validation = NULL,
token_validation_expire = NULL
WHERE id = :id
");
$stmt->execute(['id' => $compte['id']]);
} catch (Throwable $e) {
error_log('Validation email - UPDATE échouée: '.$e->getMessage());
error_log('Validation email - UPDATE échouée: ' . $e->getMessage());
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_serveur'] ?? 'Erreur serveur.',
@@ -203,16 +185,16 @@ if ($rawAL !== '') {
FROM compte
WHERE id = :id
LIMIT 1");
$stmt->execute(['id' => (int)$compte['id']]);
$stmt->execute(['id' => (int) $compte['id']]);
$c = $stmt->fetch(PDO::FETCH_ASSOC);
if ($c && !empty($c['token_autologin']) && hash_equals((string)$c['token_autologin'], $rawAL)) {
if ($c && ! empty($c['token_autologin']) && hash_equals((string) $c['token_autologin'], $rawAL)) {
// Expiration (obligatoire)
$expOk = false;
if (!empty($c['token_autologin_expire'])) {
if (! empty($c['token_autologin_expire'])) {
try {
$exp = new DateTime((string)$c['token_autologin_expire']);
$exp = new DateTime((string) $c['token_autologin_expire']);
$expOk = (new DateTime()) <= $exp;
} catch (Throwable $e) {
$expOk = false;
@@ -224,23 +206,23 @@ if ($rawAL !== '') {
$pdo->prepare("UPDATE compte
SET token_autologin = NULL, token_autologin_expire = NULL
WHERE id = :id")
->execute(['id' => (int)$c['id']]);
->execute(['id' => (int) $c['id']]);
// Connexion session (même logique pour client/praticien)
session_regenerate_id(true);
$_SESSION['compte_id'] = (int)$c['id']; // <-- IMPORTANT (secure.php l'exige)
$_SESSION['role'] = (string)$c['role'];
$_SESSION['reference_id'] = (int)$c['reference_id'];
$_SESSION['compte_id'] = (int) $c['id']; // <-- IMPORTANT (secure.php l'exige)
$_SESSION['role'] = (string) $c['role'];
$_SESSION['reference_id'] = (int) $c['reference_id'];
if ($_SESSION['role'] === 'client') {
$_SESSION['client_id'] = (int)$c['reference_id'];
header('Location: /public/client/informations.php');
exit;
$_SESSION['client_id'] = (int) $c['reference_id'];
header('Location: /public/client/informations.php');
exit;
}
if ($_SESSION['role'] === 'praticien') {
$_SESSION['praticien_id'] = (int)$c['reference_id'];
header('Location: /public/praticien/informations.php');
exit;
$_SESSION['praticien_id'] = (int) $c['reference_id'];
header('Location: /public/praticien/informations.php');
exit;
}
// fallback si role inconnu
@@ -250,13 +232,12 @@ if ($rawAL !== '') {
}
}
// =====================
// 5) Écran final (succès)
// =====================
// Le texte de succès chez toi contient un lien HTML ; on affiche un message “texte” + un vrai bouton vers la connexion.
$successMsg = isset($translations['success_validation_texte'])
? strip_tags((string)$translations['success_validation_texte'])
? strip_tags((string) $translations['success_validation_texte'])
: 'Votre compte est confirmé. Vous pouvez maintenant vous connecter.';
ui_exit(
@@ -267,5 +248,3 @@ ui_exit(
'/connexion.php',
$translations['btn_se_connecter'] ?? 'Se connecter'
);