feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles
- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD) * DEV: Clés TEST Pierre (mode test) * REC: Clés TEST Client (mode test) * PROD: Clés LIVE Client (mode live) - Ajout de la gestion des bases de données immeubles/bâtiments * Configuration buildings_database pour DEV/REC/PROD * Service BuildingService pour enrichissement des adresses - Optimisations pages et améliorations ergonomie - Mises à jour des dépendances Composer - Nettoyage des fichiers obsolètes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,9 @@ namespace App\Controllers;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Services\StripeService;
|
||||
use App\Services\LogService;
|
||||
use App\Services\FileService;
|
||||
use App\Services\ApiService;
|
||||
use Session;
|
||||
use Exception;
|
||||
|
||||
@@ -77,7 +80,7 @@ class StripeController extends Controller {
|
||||
$this->requireAuth();
|
||||
|
||||
// Log du début de la requête
|
||||
\LogService::log('Début createOnboardingLink', [
|
||||
LogService::log('Début createOnboardingLink', [
|
||||
'account_id' => $accountId,
|
||||
'user_id' => Session::getUserId()
|
||||
]);
|
||||
@@ -98,7 +101,7 @@ class StripeController extends Controller {
|
||||
$returnUrl = $data['return_url'] ?? '';
|
||||
$refreshUrl = $data['refresh_url'] ?? '';
|
||||
|
||||
\LogService::log('URLs reçues', [
|
||||
LogService::log('URLs reçues', [
|
||||
'return_url' => $returnUrl,
|
||||
'refresh_url' => $refreshUrl
|
||||
]);
|
||||
@@ -110,7 +113,7 @@ class StripeController extends Controller {
|
||||
|
||||
$result = $this->stripeService->createOnboardingLink($accountId, $returnUrl, $refreshUrl);
|
||||
|
||||
\LogService::log('Résultat createOnboardingLink', [
|
||||
LogService::log('Résultat createOnboardingLink', [
|
||||
'success' => $result['success'] ?? false,
|
||||
'has_url' => isset($result['url'])
|
||||
]);
|
||||
@@ -127,7 +130,7 @@ class StripeController extends Controller {
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
\LogService::log('Erreur createOnboardingLink', [
|
||||
LogService::log('Erreur createOnboardingLink', [
|
||||
'level' => 'error',
|
||||
'message' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
@@ -190,7 +193,7 @@ class StripeController extends Controller {
|
||||
|
||||
// Vérifier que le passage existe et appartient à l'utilisateur
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT p.*, o.fk_entite
|
||||
SELECT p.*, o.fk_entite, o.id as operation_id
|
||||
FROM ope_pass p
|
||||
JOIN operations o ON p.fk_operation = o.id
|
||||
WHERE p.id = ? AND p.fk_user = ?
|
||||
@@ -210,13 +213,15 @@ class StripeController extends Controller {
|
||||
}
|
||||
|
||||
// Vérifier que le montant correspond (passage.montant est en euros, amount en centimes)
|
||||
$expectedAmount = (int)($passage['montant'] * 100);
|
||||
$expectedAmount = (int)round($passage['montant'] * 100);
|
||||
if ($amount !== $expectedAmount) {
|
||||
$this->sendError("Le montant ne correspond pas au passage (attendu: {$expectedAmount} centimes, reçu: {$amount} centimes)", 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$entiteId = $passage['fk_entite'];
|
||||
$operationId = $passage['operation_id'];
|
||||
$fkUser = $passage['fk_user']; // ope_users.id
|
||||
|
||||
// Déterminer le type de paiement (Tap to Pay ou Web)
|
||||
$paymentMethodTypes = $data['payment_method_types'] ?? ['card_present'];
|
||||
@@ -230,14 +235,16 @@ class StripeController extends Controller {
|
||||
'payment_method_types' => $paymentMethodTypes,
|
||||
'capture_method' => $data['capture_method'] ?? 'automatic',
|
||||
'passage_id' => $passageId,
|
||||
'amicale_id' => $data['amicale_id'] ?? $entiteId,
|
||||
'member_id' => $data['member_id'] ?? Session::getUserId(),
|
||||
'fk_entite' => $data['amicale_id'] ?? $entiteId,
|
||||
'fk_user' => $data['member_id'] ?? $fkUser,
|
||||
'stripe_account' => $data['stripe_account'] ?? null,
|
||||
'metadata' => array_merge(
|
||||
[
|
||||
'passage_id' => (string)$passageId,
|
||||
'operation_id' => (string)$operationId,
|
||||
'amicale_id' => (string)($data['amicale_id'] ?? $entiteId),
|
||||
'member_id' => (string)($data['member_id'] ?? Session::getUserId()),
|
||||
'fk_user' => (string)$fkUser,
|
||||
'created_at' => (string)time(),
|
||||
'type' => $isTapToPay ? 'tap_to_pay' : 'web'
|
||||
],
|
||||
$data['metadata'] ?? []
|
||||
@@ -291,11 +298,12 @@ class StripeController extends Controller {
|
||||
$stmt = $this->db->prepare("
|
||||
SELECT p.*, o.fk_entite,
|
||||
e.encrypted_name as entite_nom,
|
||||
u.first_name as user_prenom, u.sect_name as user_nom
|
||||
ou.first_name as user_prenom, u.sect_name as user_nom
|
||||
FROM ope_pass p
|
||||
JOIN operations o ON p.fk_operation = o.id
|
||||
LEFT JOIN entites e ON o.fk_entite = e.id
|
||||
LEFT JOIN users u ON p.fk_user = u.id
|
||||
LEFT JOIN ope_users ou ON p.fk_user = ou.id
|
||||
LEFT JOIN users u ON ou.fk_user = u.id
|
||||
WHERE p.stripe_payment_id = :pi_id
|
||||
");
|
||||
$stmt->execute(['pi_id' => $paymentIntentId]);
|
||||
@@ -330,7 +338,7 @@ class StripeController extends Controller {
|
||||
$entiteNom = '';
|
||||
if (!empty($passage['entite_nom'])) {
|
||||
try {
|
||||
$entiteNom = \ApiService::decryptData($passage['entite_nom']);
|
||||
$entiteNom = ApiService::decryptData($passage['entite_nom']);
|
||||
} catch (Exception $e) {
|
||||
$entiteNom = 'Entité inconnue';
|
||||
}
|
||||
@@ -400,6 +408,7 @@ class StripeController extends Controller {
|
||||
$this->sendSuccess([
|
||||
'has_account' => false,
|
||||
'account_id' => null,
|
||||
'location_id' => null,
|
||||
'charges_enabled' => false,
|
||||
'payouts_enabled' => false,
|
||||
'onboarding_completed' => false
|
||||
@@ -415,6 +424,7 @@ class StripeController extends Controller {
|
||||
$this->sendSuccess([
|
||||
'has_account' => true,
|
||||
'account_id' => $account['stripe_account_id'],
|
||||
'location_id' => $account['stripe_location_id'] ?? null,
|
||||
'charges_enabled' => false,
|
||||
'payouts_enabled' => false,
|
||||
'onboarding_completed' => false,
|
||||
@@ -440,6 +450,7 @@ class StripeController extends Controller {
|
||||
$this->sendSuccess([
|
||||
'has_account' => true,
|
||||
'account_id' => $account['stripe_account_id'],
|
||||
'location_id' => $account['stripe_location_id'] ?? null,
|
||||
'charges_enabled' => $stripeAccount->charges_enabled,
|
||||
'payouts_enabled' => $stripeAccount->payouts_enabled,
|
||||
'onboarding_completed' => $stripeAccount->details_submitted,
|
||||
@@ -529,17 +540,17 @@ class StripeController extends Controller {
|
||||
public function getPublicConfig(): void {
|
||||
try {
|
||||
$this->requireAuth();
|
||||
|
||||
|
||||
$this->sendSuccess([
|
||||
'public_key' => $this->stripeService->getPublicKey(),
|
||||
'test_mode' => $this->stripeService->isTestMode()
|
||||
]);
|
||||
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->sendError('Erreur: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GET /api/stripe/stats
|
||||
* Récupérer les statistiques de paiement
|
||||
@@ -613,9 +624,164 @@ class StripeController extends Controller {
|
||||
'to' => $dateTo
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->sendError('Erreur: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/stripe/payment-links
|
||||
* Créer un Payment Link Stripe pour paiement par QR Code
|
||||
*
|
||||
* Payload:
|
||||
* {
|
||||
* "amount": 2500,
|
||||
* "currency": "eur",
|
||||
* "description": "Calendrier pompiers",
|
||||
* "passage_id": 789,
|
||||
* "metadata": {...}
|
||||
* }
|
||||
*/
|
||||
public function createPaymentLink(): void {
|
||||
try {
|
||||
$this->requireAuth();
|
||||
|
||||
$data = $this->getJsonInput();
|
||||
|
||||
// Validation
|
||||
if (!isset($data['amount']) || !isset($data['passage_id'])) {
|
||||
$this->sendError('Montant et passage_id requis', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$amount = (int)$data['amount'];
|
||||
$passageId = (int)$data['passage_id'];
|
||||
|
||||
// Validation du montant (doit être > 0)
|
||||
if ($amount <= 0) {
|
||||
$this->sendError('Le montant doit être supérieur à 0', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier que le passage appartient à l'utilisateur ou à son entité
|
||||
$userId = Session::getUserId();
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT p.*, o.fk_entite, ou.fk_user as ope_user_id
|
||||
FROM ope_pass p
|
||||
JOIN operations o ON p.fk_operation = o.id
|
||||
JOIN ope_users ou ON p.fk_user = ou.id
|
||||
WHERE p.id = ?
|
||||
');
|
||||
$stmt->execute([$passageId]);
|
||||
$passage = $stmt->fetch();
|
||||
|
||||
if (!$passage) {
|
||||
$this->sendError('Passage non trouvé', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier les droits : soit l'utilisateur est le créateur du passage, soit il appartient à la même entité
|
||||
$userEntityId = Session::getEntityId();
|
||||
if ($passage['ope_user_id'] != $userId && $passage['fk_entite'] != $userEntityId) {
|
||||
$this->sendError('Passage non autorisé', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier qu'il n'y a pas déjà un paiement ou un payment link pour ce passage
|
||||
if (!empty($passage['stripe_payment_id'])) {
|
||||
$this->sendError('Un paiement Stripe existe déjà pour ce passage', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($passage['stripe_payment_link_id'])) {
|
||||
$this->sendError('Un Payment Link existe déjà pour ce passage', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier que le montant correspond (passage.montant est en euros, amount en centimes)
|
||||
$expectedAmount = (int)round($passage['montant'] * 100);
|
||||
if ($amount !== $expectedAmount) {
|
||||
$this->sendError("Le montant ne correspond pas au passage (attendu: {$expectedAmount} centimes, reçu: {$amount} centimes)", 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Préparer les paramètres
|
||||
$params = [
|
||||
'amount' => $amount,
|
||||
'currency' => $data['currency'] ?? 'eur',
|
||||
'description' => $data['description'] ?? 'Calendrier pompiers',
|
||||
'passage_id' => $passageId,
|
||||
'metadata' => $data['metadata'] ?? []
|
||||
];
|
||||
|
||||
// Créer le Payment Link
|
||||
$result = $this->stripeService->createPaymentLink($params);
|
||||
|
||||
if ($result['success']) {
|
||||
$this->sendSuccess([
|
||||
'payment_link_id' => $result['payment_link_id'],
|
||||
'url' => $result['url'],
|
||||
'amount' => $result['amount'],
|
||||
'passage_id' => $passageId,
|
||||
'type' => 'qr_code'
|
||||
]);
|
||||
} else {
|
||||
$this->sendError($result['message'], 400);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->sendError('Erreur: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/stripe/locations
|
||||
* Créer une Location Stripe Terminal pour une entité (nécessaire pour Tap to Pay)
|
||||
*/
|
||||
public function createLocation(): void {
|
||||
try {
|
||||
$this->requireAuth();
|
||||
|
||||
// Vérifier le rôle de l'utilisateur
|
||||
$userId = Session::getUserId();
|
||||
$stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?');
|
||||
$stmt->execute([$userId]);
|
||||
$result = $stmt->fetch();
|
||||
$userRole = $result ? (int)$result['fk_role'] : 0;
|
||||
|
||||
if ($userRole < 2) {
|
||||
$this->sendError('Droits insuffisants - Admin amicale minimum requis', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->getJsonInput();
|
||||
$entiteId = $data['fk_entite'] ?? Session::getEntityId();
|
||||
|
||||
if (!$entiteId) {
|
||||
$this->sendError('ID entité requis', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier les droits sur cette entité
|
||||
if (Session::getEntityId() != $entiteId && $userRole < 3) {
|
||||
$this->sendError('Non autorisé pour cette entité', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->stripeService->createLocation($entiteId);
|
||||
|
||||
if ($result['success']) {
|
||||
$this->sendSuccess([
|
||||
'location_id' => $result['location_id'],
|
||||
'message' => $result['message']
|
||||
]);
|
||||
} else {
|
||||
$this->sendError($result['message'], 400);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->sendError('Erreur lors de la création de la location: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user