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

266 lines
9.5 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/souscrire_abonnement.php
require_once __DIR__ . '/includes/init_praticien.php';
// --- Helpers DB sûrs ---
function colExists(PDO $pdo, string $table, string $col): bool
{
$sql = "SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1";
$st = $pdo->prepare($sql);
$st->execute([$table, $col]);
return (bool) $st->fetchColumn();
}
function enumOptions(PDO $pdo, string $table, string $col): array
{
$sql = "SELECT COLUMN_TYPE FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1";
$st = $pdo->prepare($sql);
$st->execute([$table, $col]);
$type = (string) $st->fetchColumn();
if (stripos($type, 'enum(') !== 0) {
return [];
}
preg_match_all("/'([^']*)'/", $type, $m);
return $m[1] ?? [];
}
function logErr(Throwable $e): void
{
$info = ($e instanceof PDOException && isset($e->errorInfo)) ? json_encode($e->errorInfo, JSON_UNESCAPED_UNICODE) : '';
@file_put_contents('/tmp/souscrire_abonnement.log',
'[' . date('c') . "] " . $e->getMessage() . " " . $info . "\n",
FILE_APPEND
);
}
function flash(string $type, string $msg): void
{
$_SESSION['flash_' . $type] = $msg;
}
// --- Sécurité PDO ---
if (isset($pdo) && $pdo instanceof PDO) {
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
$praticien_id = (int) ($_SESSION['reference_id'] ?? 0);
if (! $praticien_id) {
header('Location: /public/login.php');exit;
}
// --- CSRF depuis choisir_abonnement.php ---
$method = $_SERVER['REQUEST_METHOD'] ?? '';
$sessionCSRF = (string) ($_SESSION['csrf_abon'] ?? '');
$postedCSRF = (string) ($_POST['csrf'] ?? ($_POST['csrf_abon'] ?? ''));
if ($method !== 'POST' || $sessionCSRF === '' || $postedCSRF === '' || ! hash_equals($sessionCSRF, $postedCSRF)) {
flash('error', $translations['erreur_csrf'] ?? 'Session expirée. Merci de réessayer.');
header('Location: /public/choisir_abonnement.php');exit;
}
// --- Billing mode (manual|auto) ---
$billing_mode = (string) ($_POST['billing_mode'] ?? 'manual');
$billing_mode = in_array($billing_mode, ['manual', 'auto'], true) ? $billing_mode : 'manual';
// --- Mapping Stripe (TEST / staging) pour les abonnements auto ---
// NOTE: on gardera ça ici pour l’instant (staging). Plus tard on déplacera vers /plata/config/stripe.php
// Mensuel (id=3) -> price_... (25€/mois)
// Annuel (id=4) -> price_... (275€/an)
// Pro (id=5) -> price_... (575€/an)
/*$AUTO_PRICE_MAP = [
3 => 'price_1SoksWFEDARPqDCE0Ynql83K',
4 => 'price_1Sokt8FEDARPqDCEecbxJCZU',
5 => 'price_1Sol4VFEDARPqDCEdDpjrfUE',
];
Live;*/
$AUTO_PRICE_MAP = [
3 => 'price_1SpFEMFEDARPqDCEfGsL0SIN',
4 => 'price_1SpFFLFEDARPqDCE1fLBFWbq',
5 => 'price_1SpFGMFEDARPqDCEYW7FGU0P',
];
// --- Lecture de l’abonnement choisi ---
$abonnement_id = (int) ($_POST['abonnement_id'] ?? 0);
if ($abonnement_id <= 0) {
flash('error', $translations['abonnement_invalide'] ?? 'Abonnement invalide.');
header('Location: /public/choisir_abonnement.php');exit;
}
$st = $pdo->prepare("SELECT id, nom, prix, duree_jours, actif FROM abonnement WHERE id = ? LIMIT 1");
$st->execute([$abonnement_id]);
$ab = $st->fetch(PDO::FETCH_ASSOC);
if (! $ab || ((int) $ab['actif'] !== 1 && (int) $ab['id'] !== 2)) {
flash('error', $translations['abonnement_introuvable'] ?? 'Abonnement introuvable ou inactif.');
header('Location: /public/choisir_abonnement.php');exit;
}
$prix = (float) $ab['prix']; // 0.00 si gratuit
$duree_jours = (int) $ab['duree_jours'];
$nomKey = trim((string) ($ab['nom'] ?? ''));
$nomAb = $translations[$nomKey] ?? $nomKey;
// --- Payant → flux Stripe (ponctuel ou auto) ---
if ($prix >= 0.5) {
// Auto autorisé uniquement sur les abonnements configurés (3,4,5)
if ($billing_mode === 'auto') {
if (! isset($AUTO_PRICE_MAP[$abonnement_id])) {
flash('error', $translations['auto_indispo_text'] ?? "Le renouvellement automatique n'est disponible que pour les offres mensuelle/annuelle.");
header('Location: /public/choisir_abonnement.php');exit;
}
// Sécurité : si on arrive ici avec l'ID promo (2), on force manuel (pas de price récurrent mappé)
// (Normalement ton choisir_abonnement bloque déjà, mais on double-sécurise côté serveur.)
if ($abonnement_id === 2) {
$billing_mode = 'manual';
}
}
// On garde tes sessions actuelles (compatibles avec paiement_simule.php et create_checkout.php)
$_SESSION['paiement_abonnement_id'] = $abonnement_id;
$_SESSION['paiement_prix'] = $prix;
$_SESSION['paiement_abonnement_nom'] = $nomAb;
$_SESSION['paiement_abonnement_key'] = $nomKey;
// On ajoute les sessions pour le nouveau mode
$_SESSION['paiement_billing_mode'] = $billing_mode; // manual|auto
$_SESSION['paiement_provider'] = 'stripe';
if ($billing_mode === 'auto') {
$_SESSION['paiement_provider_plan_id'] = $AUTO_PRICE_MAP[$abonnement_id]; // Stripe price_id (recurring)
} else {
unset($_SESSION['paiement_provider_plan_id']);
}
header('Location: /public/paiement_simule.php');exit;
}
// --- Gratuit → activation locale (une seule fois) ---
$FREE_PLAN_ID = 1; // adapte si besoin
// Anti double-usage (cohérent avec ton affichage)
$st = $pdo->prepare("
SELECT 1
FROM paiement
WHERE praticien_id = ?
AND abonnement_id = ?
AND (statut IS NULL OR statut <> 'échoué')
LIMIT 1
");
$st->execute([$praticien_id, $FREE_PLAN_ID]);
$hasUsedFree = (bool) $st->fetchColumn();
if ($abonnement_id === $FREE_PLAN_ID && $hasUsedFree) {
flash('error', $translations['essai_deja_utilise'] ?? "L'offre gratuite a déjà été utilisée.");
header('Location: /public/choisir_abonnement.php');exit;
}
try {
// Choisir un statut compatible avec l’ENUM de la table paiement
$statut = null;
if (colExists($pdo, 'paiement', 'statut')) {
$opts = enumOptions($pdo, 'paiement', 'statut'); // ex: ['payé','en_attente','annulé']
if ($opts) {
$prefs = ['payé', 'paye', 'paid', 'validé', 'valide', 'completed', 'ok'];
foreach ($prefs as $p) {
if (in_array($p, $opts, true)) {$statut = $p;
break;}
}
if (! $statut) {
$statut = $opts[0];
}
}
}
// Calculer l’expiration
$date_expiration = (new DateTime('now'))->modify("+$duree_jours days")->format('Y-m-d H:i:s');
$pdo->beginTransaction();
// INSERT dans paiement (montant 0, méthode 'gratuit'), uniquement les colonnes existantes
$fields = [];
if (colExists($pdo, 'paiement', 'praticien_id')) {
$fields['praticien_id'] = $praticien_id;
}
if (colExists($pdo, 'paiement', 'abonnement_id')) {
$fields['abonnement_id'] = $abonnement_id;
}
if (colExists($pdo, 'paiement', 'montant')) {
$fields['montant'] = 0.0;
}
if (colExists($pdo, 'paiement', 'methode')) {
$fields['methode'] = 'gratuit';
}
if (colExists($pdo, 'paiement', 'statut') && $statut) {
$fields['statut'] = $statut;
}
if (colExists($pdo, 'paiement', 'currency')) {
$fields['currency'] = 'eur';
}
if (colExists($pdo, 'paiement', 'created_at')) {
$fields['created_at'] = ['expr' => 'NOW()'];
}
if (colExists($pdo, 'paiement', 'date_paiement')) {
$fields['date_paiement'] = ['expr' => 'NOW()'];
}
if (colExists($pdo, 'paiement', 'abonnement_nom') && $nomAb !== '') {
$fields['abonnement_nom'] = $nomAb;
}
if (! empty($fields)) {
$cols = [];
$holders = [];
$vals = [];
foreach ($fields as $col => $val) {
$cols[] = $col;
if (is_array($val) && isset($val['expr'])) {
$holders[] = $val['expr'];
} else {
$holders[] = '?';
$vals[] = $val;
}
}
$sql = "INSERT INTO paiement (" . implode(',', $cols) . ") VALUES (" . implode(',', $holders) . ")";
$pdo->prepare($sql)->execute($vals);
}
// UPDATE du praticien (abonnement + expiration)
if (colExists($pdo, 'praticien', 'date_expiration')) {
$pdo->prepare("UPDATE praticien SET abonnement_id = ?, date_expiration = ? WHERE id = ?")
->execute([$abonnement_id, $date_expiration, $praticien_id]);
} elseif (colExists($pdo, 'praticien', 'abonnement_expire_le')) {
$pdo->prepare("UPDATE praticien SET abonnement_id = ?, abonnement_expire_le = ? WHERE id = ?")
->execute([$abonnement_id, $date_expiration, $praticien_id]);
} // sinon, on ne bloque pas (pas de colonne), mais rien à faire.
$pdo->commit();
// Nettoyage session paiement (au cas où)
unset($_SESSION['paiement_abonnement_id'], $_SESSION['paiement_prix'], $_SESSION['paiement_billing_mode'], $_SESSION['paiement_provider'], $_SESSION['paiement_provider_plan_id']);
// Redirection finale
flash('success', $translations['essai_active'] ?? "L'essai gratuit a été activé.");
header('Location: /public/praticien/factures.php'); // ou ton tableau de bord
exit;
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
logErr($e);
http_response_code(500);
exit(($translations['erreur_traitement'] ?? 'Erreur serveur') . ' (FREE)');
}