Fix: Corriger le type PDO dans StripeService et retirer getConnection()

This commit is contained in:
2025-09-01 15:23:48 +02:00
parent ece2f0006c
commit 0b541fbe4a
541 changed files with 123935 additions and 65116 deletions

View File

@@ -6,6 +6,7 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:geosector_app/core/data/models/amicale_model.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/services/api_service.dart';
import 'package:geosector_app/core/services/stripe_connect_service.dart';
import 'package:geosector_app/presentation/widgets/mapbox_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:image_picker/image_picker.dart';
@@ -66,6 +67,11 @@ class _AmicaleFormState extends State<AmicaleForm> {
final ImagePicker _picker = ImagePicker();
XFile? _selectedImage;
String? _logoUrl;
// Pour Stripe Connect
StripeConnectService? _stripeService;
bool _isCheckingStripeStatus = false;
StripeAccountStatus? _stripeStatus;
@override
void initState() {
@@ -97,6 +103,159 @@ class _AmicaleFormState extends State<AmicaleForm> {
_chkUserDeletePass = amicale?.chkUserDeletePass ?? false;
// Note : Le logo sera chargé dynamiquement depuis l'API
// Initialiser le service Stripe si API disponible
if (widget.apiService != null) {
_stripeService = StripeConnectService(apiService: widget.apiService!);
// Vérifier le statut Stripe si l'amicale a déjà un compte
if (_chkStripe && widget.amicale != null) {
_checkStripeStatus();
}
}
}
// Vérifier le statut du compte Stripe
Future<void> _checkStripeStatus() async {
if (_stripeService == null || widget.amicale == null) return;
setState(() => _isCheckingStripeStatus = true);
try {
final status = await _stripeService!.checkAccountStatus(widget.amicale!.id);
setState(() {
_stripeStatus = status;
if (status.accountId != null) {
_stripeIdController.text = status.accountId!;
}
});
} catch (e) {
debugPrint('Erreur vérification statut Stripe: $e');
} finally {
setState(() => _isCheckingStripeStatus = false);
}
}
// Configurer Stripe Connect
Future<void> _configureStripe() async {
if (_stripeService == null || widget.amicale == null) return;
// Vérifier que nous sommes sur Web
if (!kIsWeb) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Configuration Web requise'),
content: const Text(
'La configuration du compte Stripe doit être effectuée depuis un navigateur web.\n\n'
'Veuillez vous connecter depuis un ordinateur pour configurer les paiements par carte bancaire.',
),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Compris'),
),
],
),
);
return;
}
// Afficher un dialog de confirmation
final bool? confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Configuration Stripe'),
content: const Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Vous allez être redirigé vers Stripe pour :'),
SizedBox(height: 8),
Text('• Créer votre compte marchand'),
Text('• Configurer vos informations bancaires'),
Text('• Activer les paiements par carte'),
SizedBox(height: 16),
Text('Ce processus prend environ 5-10 minutes.'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Continuer'),
),
],
),
);
if (confirm != true) return;
// Afficher le loading
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Préparation de votre compte Stripe...'),
],
),
),
);
try {
// Créer ou récupérer le lien d'onboarding
final url = await _stripeService!.createStripeAccount(widget.amicale!);
// Fermer le loading
if (mounted) Navigator.of(context).pop();
if (url != null) {
// Lancer l'onboarding
final success = await _stripeService!.launchOnboarding(url);
if (success) {
// Activer la checkbox
setState(() => _chkStripe = true);
// Afficher un message de succès
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Configuration Stripe lancée. Revenez ici après avoir terminé.'),
duration: Duration(seconds: 5),
),
);
}
// Vérifier le statut après un délai
Future.delayed(const Duration(seconds: 5), _checkStripeStatus);
}
} else {
throw Exception('Impossible de créer le lien de configuration');
}
} catch (e) {
// Fermer le loading si encore ouvert
if (mounted && Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
// Afficher l'erreur
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur: ${e.toString()}'),
backgroundColor: Colors.red,
),
);
}
}
}
@override
@@ -744,7 +903,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
}
// Construire le formulaire principal
Widget _buildMainForm(ThemeData theme, bool restrictedFieldsReadOnly) {
Widget _buildMainForm(ThemeData theme, bool restrictedFieldsReadOnly, bool stripeReadOnly) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -976,78 +1135,133 @@ class _AmicaleFormState extends State<AmicaleForm> {
),
const SizedBox(height: 16),
// Stripe Checkbox et Stripe ID sur la même ligne
Row(
crossAxisAlignment: CrossAxisAlignment.center,
// Stripe Checkbox et configuration
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Checkbox Stripe
Checkbox(
value: _chkStripe,
onChanged: restrictedFieldsReadOnly
? null
: (value) {
if (value == true) {
// Afficher une boîte de dialogue de confirmation
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Confirmation'),
content: const Text(
'En acceptant les règlements par carte bancaire, des commissions de 1.4% seront prélevées sur les montants encaissés. Souhaitez-vous continuer ?'),
actions: [
TextButton(
onPressed: () {
// L'utilisateur a répondu "non"
setState(() {
_chkStripe = false;
});
Navigator.of(context).pop();
},
child: const Text('Non'),
),
ElevatedButton(
onPressed: () {
// L'utilisateur a répondu "oui"
setState(() {
_chkStripe = true;
});
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF20335E),
foregroundColor: Colors.white,
),
child: const Text('Oui'),
),
],
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Checkbox Stripe
Checkbox(
value: _chkStripe,
onChanged: stripeReadOnly
? null
: (value) {
setState(() {
_chkStripe = value ?? false;
});
// Si on active la checkbox et qu'on a une amicale, vérifier le statut
if (value == true && widget.amicale != null) {
_checkStripeStatus();
}
},
activeColor: const Color(0xFF20335E),
),
Text(
"Accepte les règlements en CB",
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 16),
// Bouton Configuration Stripe si checkbox cochée
if (_chkStripe && !stripeReadOnly && widget.amicale != null) ...[
ElevatedButton.icon(
onPressed: _isCheckingStripeStatus ? null : _configureStripe,
icon: _isCheckingStripeStatus
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Icon(kIsWeb ? Icons.settings : Icons.computer, size: 18),
label: Text(
!kIsWeb
? 'Config. Web requise'
: (_stripeStatus?.canAcceptPayments == true
? 'Compte actif'
: 'Configurer Stripe')
),
style: ElevatedButton.styleFrom(
backgroundColor: !kIsWeb
? Colors.grey
: (_stripeStatus?.statusColor ?? Colors.orange),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
),
const SizedBox(width: 8),
// Indicateur de statut
if (_stripeStatus != null)
Tooltip(
message: _stripeStatus!.statusMessage,
child: Icon(
_stripeStatus!.canAcceptPayments
? Icons.check_circle
: Icons.warning_amber_rounded,
color: _stripeStatus!.statusColor,
size: 20,
),
),
],
],
),
// Stripe ID (sur une ligne séparée)
const SizedBox(height: 8),
CustomTextField(
controller: _stripeIdController,
label: "ID Compte Stripe",
readOnly: true,
helperText: _chkStripe
? (_stripeStatus?.accountId != null
? "Compte Stripe Connect: ${_stripeStatus!.accountId}"
: "L'ID sera généré automatiquement lors de la configuration")
: "Activez les paiements CB pour configurer Stripe",
),
// Message d'information sur les commissions
if (_chkStripe)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _stripeStatus?.statusColor.withOpacity(0.1) ?? Colors.orange.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: _stripeStatus?.statusColor.withOpacity(0.3) ?? Colors.orange.withOpacity(0.3),
),
),
child: Row(
children: [
Icon(
_stripeStatus?.canAcceptPayments == true
? Icons.check_circle_outline
: Icons.info_outline,
color: _stripeStatus?.statusColor ?? Colors.orange,
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
_stripeStatus?.canAcceptPayments == true
? "✅ Compte Stripe configuré - Commission plateforme: 2.5% (min 0.50€)"
: _stripeStatus?.onboardingCompleted == false
? "⏳ Configuration Stripe en cours. Veuillez compléter le processus d'onboarding."
: "💳 Les paiements CB seront soumis à une commission de 2.5% (min 0.50€)",
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurface,
),
);
} else {
// Si l'utilisateur décoche la case, pas besoin de confirmation
setState(() {
_chkStripe = false;
});
}
},
activeColor: const Color(0xFF20335E),
),
Text(
"Accepte les règlements en CB",
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
const SizedBox(width: 16),
// Stripe ID
Expanded(
child: CustomTextField(
controller: _stripeIdController,
label: "ID Stripe Paiements CB",
readOnly: restrictedFieldsReadOnly,
helperText: "Les règlements par CB sont taxés d'une commission de 1.4%",
),
),
],
),
const SizedBox(height: 16),
@@ -1251,9 +1465,15 @@ class _AmicaleFormState extends State<AmicaleForm> {
// Déterminer si l'utilisateur peut modifier les champs restreints
final bool canEditRestrictedFields = userRole > 2;
// Pour Stripe, les admins d'amicale (rôle 2) peuvent aussi configurer
final bool canEditStripe = userRole >= 2;
// Lecture seule pour les champs restreints si l'utilisateur n'a pas les droits
final bool restrictedFieldsReadOnly = widget.readOnly || !canEditRestrictedFields;
// Lecture seule spécifique pour Stripe
final bool stripeReadOnly = widget.readOnly || !canEditStripe;
// Calculer la largeur maximale du formulaire pour les écrans larges
final screenWidth = MediaQuery.of(context).size.width;
@@ -1281,7 +1501,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
const SizedBox(height: 24),
// Formulaire principal
_buildMainForm(theme, restrictedFieldsReadOnly),
_buildMainForm(theme, restrictedFieldsReadOnly, stripeReadOnly),
],
),
),