Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -0,0 +1,265 @@
|
||||
<?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)');
|
||||
}
|
||||
Reference in new issue
Block a user