311 lines
11 KiB
PHP
311 lines
11 KiB
PHP
<?php
|
||
// /public/choisir_abonnement.php
|
||
require_once __DIR__ . '/includes/init_praticien.php';
|
||
|
||
$praticien_id = $_SESSION['reference_id'] ?? 0;
|
||
|
||
// CSRF
|
||
if (empty($_SESSION['csrf_abon'])) {
|
||
$_SESSION['csrf_abon'] = bin2hex(random_bytes(32));
|
||
}
|
||
$CSRF = $_SESSION['csrf_abon'];
|
||
|
||
// Abonnement gratuit (id=1)
|
||
$FREE_PLAN_ID = 1;
|
||
|
||
// Abonnements éligibles au renouvellement automatique (Stripe subscription)
|
||
$AUTO_ELIGIBLE_IDS = [3, 4, 5]; // mensuel, annuel, pack pro (id=5)
|
||
|
||
// Déjà utilisé ?
|
||
$hasUsedFree = false;
|
||
if ($praticien_id) {
|
||
$sql = "SELECT 1
|
||
FROM paiement
|
||
WHERE praticien_id = ?
|
||
AND abonnement_id = ?
|
||
AND (statut IS NULL OR statut <> 'échoué')
|
||
LIMIT 1";
|
||
$st = $pdo->prepare($sql);
|
||
$st->execute([$praticien_id, $FREE_PLAN_ID]);
|
||
$hasUsedFree = (bool)$st->fetchColumn();
|
||
}
|
||
|
||
// Récupérer les abonnements actifs (sauf plan 2 promo si actif=0)
|
||
$stmt = $pdo->query("SELECT * FROM abonnement WHERE actif = 1 ORDER BY prix ASC, duree_jours ASC");
|
||
$abonnements = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
// Si essai gratuit utilisé → retirer l'id=1
|
||
if ($hasUsedFree) {
|
||
$abonnements = array_values(array_filter($abonnements, function($ab){
|
||
return (int)$ab['id'] !== 1;
|
||
}));
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<title><?= htmlspecialchars($translations['seo_choisir_abonnement_title'] ?? 'Тарифы для практикующего — Услуги.фр') ?></title>
|
||
<meta name="description" content="<?= htmlspecialchars($translations['seo_choisir_abonnement_desc'] ?? '') ?>">
|
||
<meta name="keywords" content="<?= htmlspecialchars($translations['seo_choisir_abonnement_keywords'] ?? '') ?>">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
|
||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||
<script src="/js/sweetalert2@11.js"></script>
|
||
|
||
<style>
|
||
:root {
|
||
--brand-blue:#38378B;
|
||
--brand-red:#E03C3F;
|
||
--page-blue:#8CA9FF;
|
||
--border:#e5e7eb;
|
||
--radius:18px;
|
||
--shadow:0 8px 20px rgba(0,0,0,.06);
|
||
}
|
||
body {
|
||
background:var(--page-blue);
|
||
font-family:'Montserrat',sans-serif;
|
||
color:#111;
|
||
}
|
||
.card { border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow); }
|
||
.card-title { color:var(--brand-blue); font-weight:700; }
|
||
.btn-brand { background:var(--brand-red); border-color:var(--brand-red); color:#fff; border-radius:999px; font-weight:600; }
|
||
.btn-brand:hover { background:var(--brand-red); border-color:var(--brand-red); color:#fff; border-radius:999px; font-weight:600; }
|
||
|
||
/* Bouton principal (CONFIRMER) */
|
||
.swal2-confirm {
|
||
background-color: #E03C3F !important;
|
||
border-radius: 999px !important;
|
||
padding: 10px 28px !important;
|
||
font-weight: 600 !important;
|
||
color: #fff !important;
|
||
border: none !important;
|
||
}
|
||
.swal2-confirm:hover {
|
||
background-color: #E03C3F !important;
|
||
}
|
||
|
||
/* Bouton secondaire (ANNULER) */
|
||
.swal2-cancel {
|
||
background-color: #38378B !important; /* bleu foncé */
|
||
border-radius: 999px !important;
|
||
padding: 10px 28px !important;
|
||
font-weight: 600 !important;
|
||
color: #fff !important;
|
||
border: none !important;
|
||
}
|
||
.swal2-cancel:hover {
|
||
background-color: #38378B !important;
|
||
}
|
||
|
||
/* Champ texte */
|
||
.swal2-input {
|
||
border-radius: 12px !important;
|
||
border: 1px solid #d1d5db !important;
|
||
padding: 10px !important;
|
||
font-size: 15px !important;
|
||
}
|
||
|
||
/* Fenêtre principale */
|
||
.swal2-popup {
|
||
border-radius: 18px !important;
|
||
padding: 24px !important;
|
||
font-family: 'Montserrat', sans-serif !important;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container py-5">
|
||
|
||
<h2 class="text-center mb-4" style="color:#fff;">
|
||
<?= htmlspecialchars($translations['choisissez_votre_abonnement'] ?? 'Выберите ваш тариф') ?>
|
||
</h2>
|
||
|
||
<!-- Choix mode de paiement -->
|
||
<div class="d-flex justify-content-center mb-4">
|
||
<div class="btn-group" role="group" aria-label="<?= htmlspecialchars($translations['mode_paiement'] ?? 'Mode de paiement') ?>">
|
||
<input type="radio" class="btn-check" name="billing_mode_ui" id="bm_manual" value="manual">
|
||
<!--<label class="btn btn-outline-light" for="bm_manual">
|
||
<?= htmlspecialchars($translations['paiement_ponctuel'] ?? 'Paiement ponctuel') ?>
|
||
</label>-->
|
||
|
||
<input type="radio" class="btn-check" name="billing_mode_ui" id="bm_auto" value="auto" checked>
|
||
<label class="btn btn-outline-light" for="bm_auto">
|
||
<?= htmlspecialchars($translations['renouvellement_auto'] ?? 'Renouvellement automatique') ?>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if (!$abonnements): ?>
|
||
<div class="alert alert-warning text-center">Нет доступных тарифов.</div>
|
||
<?php else: ?>
|
||
<div class="row g-4 justify-content-center">
|
||
|
||
<?php foreach ($abonnements as $ab): ?>
|
||
<?php
|
||
$isFree = ((float)$ab['prix'] == 0.0);
|
||
$btnLabel = $isFree
|
||
? ($translations['essai_gratuit'] ?? 'Бесплатно')
|
||
: ($translations['btn_choisir_abonnement'] ?? 'Выбрать');
|
||
|
||
$prixTxt = $isFree
|
||
? ($translations['gratuit'] ?? 'Бесплатно')
|
||
: number_format((float)$ab['prix'], 2, ',', ' ') . ' €';
|
||
|
||
$nomKey = trim((string)($ab['nom'] ?? ''));
|
||
$descKey = trim((string)($ab['description'] ?? ''));
|
||
|
||
// Si la clé existe dans $translations → on affiche la traduction, sinon on affiche brut (fallback)
|
||
$nomTxt = $translations[$nomKey] ?? $nomKey;
|
||
$descTxt = $translations[$descKey] ?? $descKey;
|
||
|
||
$isAutoEligible = in_array((int)$ab['id'], $AUTO_ELIGIBLE_IDS, true);
|
||
?>
|
||
<div class="col-12 col-sm-10 col-md-6 col-lg-4 col-xl-3">
|
||
<div class="card h-100">
|
||
<div class="card-body d-flex flex-column">
|
||
<h5 class="card-title"><?= htmlspecialchars($nomTxt) ?></h5>
|
||
|
||
<?php if ($descKey !== ''): ?>
|
||
<p class="card-text"><?= nl2br(htmlspecialchars($descTxt)) ?></p>
|
||
<?php endif; ?>
|
||
|
||
<p class="fw-bold mb-1">
|
||
<?= $translations['col_duree'] ?? 'Длительность:' ?>
|
||
<?= (int)$ab['duree_jours'] ?>
|
||
<?= $translations['dnei'] ?? 'дней' ?>
|
||
</p>
|
||
|
||
<p class="fw-bold mb-3"><?= $prixTxt ?></p>
|
||
|
||
<form method="POST"
|
||
action="/public/souscrire_abonnement.php"
|
||
class="mt-auto js-abo-form"
|
||
data-abo-id="<?= (int)$ab['id'] ?>"
|
||
data-auto-eligible="<?= $isAutoEligible ? '1' : '0' ?>">
|
||
|
||
<input type="hidden" name="csrf" value="<?= htmlspecialchars($CSRF) ?>">
|
||
<input type="hidden" name="abonnement_id" value="<?= (int)$ab['id'] ?>">
|
||
<input type="hidden" name="billing_mode" value="manual">
|
||
<input type="hidden" name="promo_code" value="">
|
||
|
||
<button type="submit" class="btn btn-brand w-100"><?= htmlspecialchars($btnLabel) ?></button>
|
||
</form>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php endforeach; ?>
|
||
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<script>
|
||
const I18N_PROMO = {
|
||
title: <?= json_encode($translations['promo_title'] ?? 'Promo') ?>,
|
||
placeholder: <?= json_encode($translations['promo_placeholder'] ?? '') ?>,
|
||
confirm: <?= json_encode($translations['promo_confirm'] ?? 'OK') ?>,
|
||
cancel: <?= json_encode($translations['promo_cancel'] ?? 'Cancel') ?>,
|
||
|
||
autoIndispoTitle: <?= json_encode($translations['auto_indispo_title'] ?? 'Non disponible') ?>,
|
||
autoIndispoText: <?= json_encode($translations['auto_indispo_text'] ?? "Le renouvellement automatique n'est disponible que pour les offres mensuelle/annuelle.") ?>,
|
||
promoAutoNotSupported: <?= json_encode($translations['promo_auto_not_supported'] ?? "La promo n'est pas disponible en renouvellement automatique. Utilisez le paiement ponctuel pour cette offre.") ?>
|
||
};
|
||
</script>
|
||
|
||
<script>
|
||
// ------------------------
|
||
// SweetAlert PROMO LOGIC + Billing mode
|
||
// ------------------------
|
||
|
||
function getSelectedBillingMode() {
|
||
const el = document.querySelector('input[name="billing_mode_ui"]:checked');
|
||
return el ? el.value : 'manual';
|
||
}
|
||
|
||
document.querySelectorAll(".js-abo-form").forEach(function(form){
|
||
form.addEventListener("submit", function(e){
|
||
const aboId = parseInt(form.dataset.aboId, 10);
|
||
const selectedMode = getSelectedBillingMode();
|
||
const eligibleAuto = (form.dataset.autoEligible === '1');
|
||
|
||
// Pousser le mode choisi dans le champ hidden (important avant form.submit())
|
||
const bmInput = form.querySelector('input[name="billing_mode"]');
|
||
if (bmInput) bmInput.value = selectedMode;
|
||
|
||
// Si auto choisi mais offre non éligible -> bloquer
|
||
if (selectedMode === 'auto' && !eligibleAuto) {
|
||
e.preventDefault();
|
||
Swal.fire({
|
||
icon: 'warning',
|
||
title: I18N_PROMO.autoIndispoTitle,
|
||
text: I18N_PROMO.autoIndispoText,
|
||
confirmButtonText: I18N_PROMO.confirm
|
||
});
|
||
return;
|
||
}
|
||
|
||
// Promo uniquement pour l’abonnement PROFESSIONNEL (ID = 5)
|
||
if (aboId !== 5) return; // laisser fonctionner normalement
|
||
|
||
// Si l'utilisateur est en "auto", on bloque la promo (car promo bascule sur ID=2)
|
||
// et ID=2 n'est pas (encore) configuré avec un Price récurrent.
|
||
if (selectedMode === 'auto') {
|
||
e.preventDefault();
|
||
Swal.fire({
|
||
icon: 'info',
|
||
title: I18N_PROMO.title,
|
||
text: I18N_PROMO.promoAutoNotSupported,
|
||
confirmButtonText: I18N_PROMO.confirm
|
||
});
|
||
return;
|
||
}
|
||
|
||
e.preventDefault();
|
||
|
||
Swal.fire({
|
||
title: I18N_PROMO.title,
|
||
input: 'text',
|
||
inputPlaceholder: I18N_PROMO.placeholder,
|
||
showCancelButton: true,
|
||
confirmButtonText: I18N_PROMO.confirm,
|
||
cancelButtonText: I18N_PROMO.cancel
|
||
}).then(function(result){
|
||
|
||
// Annulé -> pas de code → abonnement 5
|
||
if (result.isDismissed) {
|
||
form.submit();
|
||
return;
|
||
}
|
||
|
||
const code = (result.value || '').trim();
|
||
|
||
// Le code correct
|
||
const CODE_VALID = "uslugiW@W2026";
|
||
|
||
if (code === CODE_VALID) {
|
||
// On remplace l’abonnement → on envoie ID=2 (promo)
|
||
form.querySelector('input[name="abonnement_id"]').value = 2;
|
||
// On garde en mémoire le code (optionnel)
|
||
form.querySelector('input[name="promo_code"]').value = code;
|
||
|
||
// Sécurité : on force le mode à "manual" car ID=2 n'a pas de price récurrent configuré
|
||
const bmInput2 = form.querySelector('input[name="billing_mode"]');
|
||
if (bmInput2) bmInput2.value = 'manual';
|
||
|
||
form.submit();
|
||
} else {
|
||
// Code faux → abonnement normal
|
||
form.submit();
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|