Files
maria_uslugi/public/choisir_abonnement.php
T
2026-03-05 10:25:39 +01:00

311 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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>