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

242 lines
10 KiB
PHP
Raw Permalink 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/paiement_simule.php
require_once __DIR__ . '/includes/init_praticien.php';
if (isset($pdo) && $pdo instanceof PDO) {
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
function logErr(Throwable $e): void {
$info = ($e instanceof PDOException && isset($e->errorInfo)) ? json_encode($e->errorInfo, JSON_UNESCAPED_UNICODE) : '';
@file_put_contents('/tmp/pay_errors.log',
'['.date('Y-m-d H:i:s')."] ".$e->getMessage()." ".$info."\n",
FILE_APPEND
);
}
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();
}
/**
* Retourne la date de fin d’accès si un abonnement est encore actif (manual/auto),
* en priorité via billing_subscriptions, sinon via praticien.date_expiration.
*
* @return array{until:string|null, mode:string|null, status:string|null, sub_id:string|null}
*/
function activeAccessInfo(PDO $pdo, int $praticien_id): array {
$out = ['until'=>null, 'mode'=>null, 'status'=>null, 'sub_id'=>null];
// 1) via billing_subscriptions si dispo
if (colExists($pdo, 'billing_subscriptions', 'praticien_id') && colExists($pdo, 'billing_subscriptions', 'current_period_end')) {
$sql = "SELECT billing_mode, status, provider_subscription_id, current_period_end
FROM billing_subscriptions
WHERE praticien_id = ?
AND (provider IS NULL OR provider = 'stripe')
ORDER BY id DESC
LIMIT 1";
$st = $pdo->prepare($sql);
$st->execute([$praticien_id]);
$row = $st->fetch(PDO::FETCH_ASSOC);
if ($row) {
$cpe = trim((string)($row['current_period_end'] ?? ''));
if ($cpe !== '') {
$ts = strtotime($cpe);
if ($ts !== false && $ts > time()) {
$out['until'] = $cpe;
$out['mode'] = (string)($row['billing_mode'] ?? '');
$out['status'] = (string)($row['status'] ?? '');
$out['sub_id'] = (string)($row['provider_subscription_id'] ?? '');
return $out;
}
}
}
}
// 2) fallback via praticien.date_expiration
if (colExists($pdo, 'praticien', 'date_expiration')) {
$st = $pdo->prepare("SELECT date_expiration FROM praticien WHERE id = ? LIMIT 1");
$st->execute([$praticien_id]);
$de = (string)($st->fetchColumn() ?: '');
if ($de !== '') {
$ts = strtotime($de);
if ($ts !== false && $ts > time()) {
$out['until'] = $de;
return $out;
}
}
}
return $out;
}
$err = '';
$msg = '';
// Données nécessaires déjà posées en session avant d’arriver ici
$praticien_id = $_SESSION['reference_id'] ?? null;
$abonnement_id = $_SESSION['paiement_abonnement_id'] ?? null;
$prix = $_SESSION['paiement_prix'] ?? null;
// Nouveau: mode de facturation (manual|auto) + provider_plan_id (Stripe price_id pour auto)
$billing_mode = (string)($_SESSION['paiement_billing_mode'] ?? 'manual');
$billing_mode = in_array($billing_mode, ['manual','auto'], true) ? $billing_mode : 'manual';
$provider = (string)($_SESSION['paiement_provider'] ?? 'stripe');
$provider_plan = (string)($_SESSION['paiement_provider_plan_id'] ?? ''); // price_id si auto
if (!$praticien_id || !$abonnement_id || $prix === null) {
$err = $translations['erreur_donnees_paiement'] ?? 'Données de paiement manquantes.';
}
// Sécurité: si mode auto mais pas de plan provider → erreur
if (!$err && $billing_mode === 'auto' && $provider === 'stripe' && $provider_plan === '') {
$err = $translations['erreur_abonnement_auto'] ?? "Configuration manquante pour le renouvellement automatique.";
}
/* CSRF */
if (empty($_SESSION['csrf_pay'])) {
$_SESSION['csrf_pay'] = bin2hex(random_bytes(32));
}
$CSRF = $_SESSION['csrf_pay'] ?? '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$err) {
try {
// Vérif CSRF
if (!hash_equals($CSRF, $_POST['csrf'] ?? '')) {
throw new RuntimeException('CSRF');
}
// Méthode choisie
$allowed = ['carte','paypal','virement'];
$methode = (string)($_POST['methode'] ?? '');
if (!in_array($methode, $allowed, true)) {
throw new InvalidArgumentException('BAD_METHOD');
}
// Carte bancaire → AVANT Stripe : bloquer si accès déjà actif
if ($methode === 'carte') {
$pid = (int)$praticien_id;
// Si un accès est encore actif, on bloque toute nouvelle tentative (manual/auto)
$active = activeAccessInfo($pdo, $pid);
if (!empty($active['until'])) {
// Message clair avec date (tu peux remplacer par une clé $translations si tu veux)
$until = $active['until'];
$modeTxt = ($active['mode'] === 'auto') ? 'auto' : (($active['mode'] === 'manual') ? 'manual' : '');
$extra = $modeTxt ? " (mode: ".$modeTxt.")" : '';
$err = ($translations['abonnement_deja_actif'] ?? "Un abonnement est déjà actif jusqu'au {date}. Vous ne pouvez pas en souscrire un autre avant expiration.")
;
$err = strtr($err, ['{date}' => $until]) . $extra;
}
if ($err) {
// on ne redirige pas, on affiche l’erreur dans la page
} else {
// OK -> redirection vers l’endpoint Stripe (création session Checkout)
// L’endpoint utilisera les variables de session (praticien_id, abonnement_id, prix, billing_mode, provider_plan_id)
$_SESSION['checkout_attempt_id'] = bin2hex(random_bytes(16));
header('Location: /plata/actions/create_checkout.php', true, 303);
exit;
}
}
// PayPal / Virement → à implémenter (on ne met pas à jour la BD ici)
if ($methode === 'paypal') {
$msg = $translations['paypal_pas_dispo'] ?? "PayPal n'est pas encore disponible. Merci de choisir Carte bancaire.";
} elseif ($methode === 'virement') {
$msg = $translations['virement_pas_dispo'] ?? "Virement non disponible en ligne. Contacte le support pour un règlement par virement.";
}
} catch (Throwable $e) {
logErr($e);
$code = $e->getMessage();
if ($code === 'CSRF') { $err = $translations['session_expiree'] ?? 'Session expirée. Recharge la page.'; }
elseif ($code === 'BAD_METHOD') { $err = $translations['methode_invalide'] ?? 'Méthode de paiement invalide.'; }
else { $err = $translations['erreur_traitement'] ?? 'Erreur serveur pendant le paiement.'; }
}
}
// Affichage
$montantAff = number_format((float)$prix, 2, ',', ' ') . ' €';
// Titre différent si auto
if ($billing_mode === 'auto') {
$titrePaiement = strtr(
$translations['paiement_abonnement_auto_de_montant'] ?? 'Abonnement automatique de {montant}',
['{montant}' => $montantAff]
);
} else {
$titrePaiement = strtr(
$translations['paiement_de_montant'] ?? 'Paiement de {montant}',
['{montant}' => $montantAff]
);
}
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title><?= htmlspecialchars($translations['paiement_en_ligne_title'] ?? 'Paiement en ligne') ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/css/bootstrap.min.css" rel="stylesheet">
<style>
:root{ --brand-blue:#38378B; --brand-red:#E03C3F; --brand-blue-hover:#2f2e73; --page-blue:#8CA9FF; --text-dark:#111; --border:#e5e7eb; --radius:18px; --shadow:0 8px 20px rgba(0,0,0,.08); }
html,body{height:100%}
body{background:var(--page-blue); font-family:'Montserrat',system-ui,-apple-system,Segoe UI,Roboto,'Helvetica Neue',Arial; color:var(--text-dark);}
h3{color:var(--text-dark); font-weight:700;}
.card{border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow); background:#fff;}
.form-label{font-weight:600; color:#0f172a;}
.form-select,.form-control{border-radius:12px; border-color:#d1d5db;}
.form-select:focus,.form-control:focus{border-color:var(--brand-blue); box-shadow:0 0 0 .25rem rgba(56,55,139,.15);}
.btn-primary{background:var(--brand-red); border-color:var(--brand-red); border-radius:999px; font-weight:600; box-shadow:0 8px 22px rgba(0,0,0,.18);}
.btn-primary:hover,.btn-primary:focus{background:var(--brand-red); border-color:var(--brand-red);}
</style>
</head>
<body class="d-flex justify-content-center align-items-center">
<div class="card p-4" style="min-width:360px; max-width:480px;">
<h3 class="mb-4 text-center"><?= htmlspecialchars($titrePaiement) ?></h3>
<?php if (!$err && $billing_mode === 'auto'): ?>
<div class="alert alert-light border" role="alert">
<?= htmlspecialchars($translations['info_abonnement_auto'] ?? "Le renouvellement automatique est activé. Vous pouvez résilier à tout moment depuis votre espace.") ?>
</div>
<?php endif; ?>
<?php if ($err): ?>
<div class="alert alert-danger" role="alert"><?= htmlspecialchars($err) ?><br>
<a href="/public/praticien/resiliation.php" class="text-decoration-none">
← <?= htmlspecialchars($translations['retour_resiliation'] ?? 'Retour à la résiliation') ?>
</a></div>
<?php endif; ?>
<?php if (!$err && $msg): ?>
<div class="alert alert-info" role="alert"><?= htmlspecialchars($msg) ?></div>
<?php endif; ?>
<form method="POST">
<input type="hidden" name="csrf" value="<?= htmlspecialchars($CSRF, ENT_QUOTES, 'UTF-8') ?>">
<div class="mb-3">
<label for="methode" class="form-label"><?= htmlspecialchars($translations['label_methode_paiement'] ?? 'Méthode de paiement') ?></label>
<select name="methode" id="methode" class="form-select" required>
<option value="carte"><?= htmlspecialchars($translations['methode_cb'] ?? 'Carte bancaire') ?></option>
<!--<option value="paypal"><?= htmlspecialchars($translations['methode_paypal'] ?? 'PayPal') ?></option>
<option value="virement"><?= htmlspecialchars($translations['methode_virement'] ?? 'Virement') ?></option>-->
</select>
</div>
<button type="submit" class="btn btn-primary w-100">
<?= htmlspecialchars($translations['btn_payer_maintenant'] ?? 'Payer maintenant') ?>
</button>
</form>
</div>
</body>
</html>