Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
// /plata/actions/create_checkout.php
|
||||
|
||||
require_once realpath(__DIR__ . '/../../public/includes/init_praticien.php');
|
||||
require_once __DIR__ . '/../lib/stripe_client.php';
|
||||
// $pdo
|
||||
//require_once __DIR__ . '/../../public/includes/securite.php';
|
||||
|
||||
require_login_or_redirect(['praticien']);
|
||||
|
||||
$configAll = require __DIR__ . '/../config/stripe.php';
|
||||
$cfg = $configAll[$configAll['mode']] ?? null;
|
||||
|
||||
if (!$cfg || empty($cfg['secret_key'])) {
|
||||
http_response_code(500);
|
||||
exit('Config Stripe invalide.');
|
||||
}
|
||||
|
||||
$logFile = '/tmp/stripe_create_checkout.log';
|
||||
@touch($logFile);
|
||||
|
||||
function log_line(string $msg): void {
|
||||
global $logFile;
|
||||
@file_put_contents($logFile, '['.date('c').'] '.$msg."\n", FILE_APPEND);
|
||||
}
|
||||
|
||||
// ---- Données session ----
|
||||
$praticien_id = $_SESSION['reference_id'] ?? null;
|
||||
$abonnement_id = $_SESSION['paiement_abonnement_id'] ?? null;
|
||||
$prix_raw = $_SESSION['paiement_prix'] ?? null;
|
||||
|
||||
// Nouveau (manual|auto)
|
||||
$billing_mode = (string)($_SESSION['paiement_billing_mode'] ?? 'manual');
|
||||
$billing_mode = in_array($billing_mode, ['manual','auto'], true) ? $billing_mode : 'manual';
|
||||
|
||||
// Nouveau : Stripe price_id récurrent (si auto)
|
||||
$provider_plan_id = (string)($_SESSION['paiement_provider_plan_id'] ?? '');
|
||||
|
||||
if (!$praticien_id || !$abonnement_id || $prix_raw === null) {
|
||||
http_response_code(400);
|
||||
exit('Données paiement manquantes.');
|
||||
}
|
||||
// ---- Tentative de paiement (anti réutilisation de session Stripe) ----
|
||||
$attemptId = (string)($_SESSION['checkout_attempt_id'] ?? '');
|
||||
if ($attemptId === '') {
|
||||
$attemptId = bin2hex(random_bytes(16));
|
||||
$_SESSION['checkout_attempt_id'] = $attemptId;
|
||||
}
|
||||
|
||||
|
||||
// ---- Récupérer la clé de l’abonnement ----
|
||||
$abonnement_key = '';
|
||||
try {
|
||||
if (!isset($pdo) || !($pdo instanceof PDO)) {
|
||||
throw new RuntimeException('PDO indisponible');
|
||||
}
|
||||
|
||||
$st = $pdo->prepare("SELECT nom FROM abonnement WHERE id = ? LIMIT 1");
|
||||
$st->execute([(int)$abonnement_id]);
|
||||
$row = $st->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// nom = clé de traduction
|
||||
$abonnement_key = trim((string)($row['nom'] ?? ''));
|
||||
|
||||
} catch (Throwable $e) {
|
||||
log_line('WARN plan lookup failed: '.$e->getMessage());
|
||||
$abonnement_key = '';
|
||||
}
|
||||
|
||||
// Nom affiché : Abonnement #ID — traduction depuis fr.php / ru.php
|
||||
$display_name = 'Abonnement #'.(int)$abonnement_id;
|
||||
|
||||
$abonnement_nom = ''; // nom affichable (traduit)
|
||||
if ($abonnement_key !== '') {
|
||||
$label = $translations[$abonnement_key] ?? ''; // traduction UNIQUEMENT
|
||||
if ($label !== '') {
|
||||
$abonnement_nom = $label;
|
||||
if (function_exists('mb_strimwidth')) {
|
||||
$display_name .= ' — '.mb_strimwidth($label, 0, 120, '…', 'UTF-8');
|
||||
} else {
|
||||
$display_name .= ' — '.$label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Normalisation prix ----
|
||||
$prix_norm = str_replace(["\u{00A0}", ' '], '', (string)$prix_raw);
|
||||
$prix_norm = str_replace(',', '.', $prix_norm);
|
||||
if (!is_numeric($prix_norm)) {
|
||||
http_response_code(400);
|
||||
exit('Montant invalide.');
|
||||
}
|
||||
$prix = (float)$prix_norm;
|
||||
|
||||
if ($prix < 0.5) {
|
||||
http_response_code(400);
|
||||
exit('Montant trop faible (Stripe min 0,50 €).');
|
||||
}
|
||||
$amount = (int) round($prix * 100); // centimes
|
||||
|
||||
// ---- Email du praticien ----
|
||||
$userEmail = $_SESSION['user_email'] ?? null;
|
||||
if (!$userEmail || !filter_var($userEmail, FILTER_VALIDATE_EMAIL)) {
|
||||
try {
|
||||
if (isset($pdo) && $pdo instanceof PDO) {
|
||||
$st = $pdo->prepare("SELECT email FROM praticien WHERE id = ? LIMIT 1");
|
||||
$st->execute([(int)$praticien_id]);
|
||||
$row = $st->fetch(PDO::FETCH_ASSOC);
|
||||
$candidate = $row['email'] ?? null;
|
||||
if ($candidate && filter_var($candidate, FILTER_VALIDATE_EMAIL)) {
|
||||
$userEmail = $candidate;
|
||||
} else {
|
||||
$userEmail = null;
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
log_line('WARN email lookup failed: '.$e->getMessage());
|
||||
$userEmail = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Construire la session Checkout ----
|
||||
$successUrl = 'https://' . $_SERVER['HTTP_HOST'] . '/plata/views/success.php?session_id={CHECKOUT_SESSION_ID}';
|
||||
$cancelUrl = 'https://' . $_SERVER['HTTP_HOST'] . '/plata/views/cancel.php';
|
||||
|
||||
$params = [
|
||||
'success_url' => $successUrl,
|
||||
'cancel_url' => $cancelUrl,
|
||||
'payment_method_types' => ['card'],
|
||||
|
||||
'metadata' => [
|
||||
'praticien_id' => (string)(int)$praticien_id,
|
||||
'abonnement_id' => (string)(int)$abonnement_id,
|
||||
'billing_mode' => $billing_mode,
|
||||
'montant_cent' => (string)$amount,
|
||||
'abonnement_key' => $abonnement_key !== '' ? $abonnement_key : 'N/A',
|
||||
'abonnement_nom' => $abonnement_nom !== '' ? $abonnement_nom : 'N/A',
|
||||
'provider_plan_id' => $provider_plan_id !== '' ? $provider_plan_id : 'N/A',
|
||||
|
||||
],
|
||||
];
|
||||
$params['client_reference_id'] = (string)(int)$praticien_id;
|
||||
// Recommandé : éviter customer=null dans les events
|
||||
if ($userEmail && filter_var($userEmail, FILTER_VALIDATE_EMAIL)) {
|
||||
$params['customer_email'] = $userEmail;
|
||||
}
|
||||
// Email si dispo
|
||||
if ($userEmail && filter_var($userEmail, FILTER_VALIDATE_EMAIL)) {
|
||||
$params['customer_email'] = $userEmail;
|
||||
}
|
||||
|
||||
// ---- Mode ponctuel vs abonnement auto ----
|
||||
if ($billing_mode === 'auto') {
|
||||
// Sécurité : il faut absolument un price_id récurrent
|
||||
if ($provider_plan_id === '' || stripos($provider_plan_id, 'price_') !== 0) {
|
||||
http_response_code(400);
|
||||
exit('Plan récurrent manquant.');
|
||||
}
|
||||
|
||||
$params['mode'] = 'subscription';
|
||||
$params['line_items'] = [[
|
||||
'price' => $provider_plan_id,
|
||||
'quantity' => 1,
|
||||
]];
|
||||
|
||||
// Important: metadata aussi sur la subscription (utile pour les webhooks invoice/subscription)
|
||||
$params['subscription_data'] = [
|
||||
'metadata' => $params['metadata'],
|
||||
];
|
||||
} else {
|
||||
// Ponctuel (comportement actuel)
|
||||
$params['mode'] = 'payment';
|
||||
$params['line_items'] = [[
|
||||
'price_data' => [
|
||||
'currency' => $configAll['currency'] ?? 'eur',
|
||||
'product_data' => ['name' => $display_name],
|
||||
'unit_amount' => $amount,
|
||||
],
|
||||
'quantity' => 1,
|
||||
]];
|
||||
}
|
||||
|
||||
// ---- Génération d'une idempotency_key robuste ----
|
||||
$idem_material = [
|
||||
'praticien_id' => (int)$praticien_id,
|
||||
'abonnement_id' => (int)$abonnement_id,
|
||||
'billing_mode' => (string)$billing_mode,
|
||||
'provider_plan' => (string)$provider_plan_id,
|
||||
'amount' => (int)$amount,
|
||||
'currency' => (string)($configAll['currency'] ?? 'eur'),
|
||||
'name' => (string)$display_name,
|
||||
'success_url' => (string)$successUrl,
|
||||
'cancel_url' => (string)$cancelUrl,
|
||||
'attempt' => (string)$attemptId,
|
||||
];
|
||||
$idem_hash = hash('sha256', json_encode($idem_material, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES));
|
||||
$idempotency_key = 'chk_'.$idem_hash;
|
||||
|
||||
try {
|
||||
/** @var \Stripe\StripeClient $stripe */
|
||||
$session = $stripe->checkout->sessions->create($params, [
|
||||
'idempotency_key' => $idempotency_key,
|
||||
]);
|
||||
|
||||
header('Location: ' . $session->url, true, 303);
|
||||
exit;
|
||||
|
||||
} catch (\Stripe\Exception\ApiErrorException $e) {
|
||||
$httpStatus = method_exists($e, 'getHttpStatus') ? $e->getHttpStatus() : null;
|
||||
$requestId = method_exists($e, 'getRequestId') ? $e->getRequestId() : null;
|
||||
$stripeCode = method_exists($e, 'getStripeCode') ? $e->getStripeCode() : null;
|
||||
$httpBody = method_exists($e, 'getHttpBody') ? $e->getHttpBody() : null;
|
||||
$jsonBody = method_exists($e, 'getJsonBody') ? json_encode($e->getJsonBody()) : null;
|
||||
log_line("STRIPE_API_ERROR status=$httpStatus req=$requestId code=$stripeCode msg=".$e->getMessage()." body=".$httpBody." json=".$jsonBody);
|
||||
http_response_code(500);
|
||||
exit('Erreur Stripe');
|
||||
} catch (Throwable $e) {
|
||||
log_line('PHP_ERROR '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine());
|
||||
http_response_code(500);
|
||||
exit('Erreur Stripe');
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user