welloo_sdk 0.0.54 copy "welloo_sdk: ^0.0.54" to clipboard
welloo_sdk: ^0.0.54 copied to clipboard

Package de transaction Welloo

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:welloo_sdk/welloo_sdk.dart';
import 'package:welloo_sdk/src/shared/config/transaction_verification_config.dart';
import 'package:welloo_sdk/src/shared/services/logger_service.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Load environment variables
  try {
    await dotenv.load(fileName: '.env');
  } catch (e) {
    debugPrint('⚠️ .env file not found. Please create one from .env.example');
  }

  // Configure logger
  LoggerService().configure(
    minLevel: LogLevel.debug,
    enabledInProduction: true,
  );

  // Initialize Welloo SDK
  await WellooSdk.init();

  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welloo SDK - Nouveau Système',
      theme: ThemeData(
        colorSchemeSeed: Colors.blue,
        useMaterial3: true,
      ),
      home: const DemoWello(),
    );
  }
}

class DemoWello extends StatefulWidget {
  const DemoWello({super.key});

  @override
  State<DemoWello> createState() => _DemoWelloState();
}

class _DemoWelloState extends State<DemoWello> with WidgetsBindingObserver {
  String? _lastTransactionRef;
  final _sdk = WellooSdk();

  // Configurations disponibles
  TransactionVerificationConfig _currentConfig = TransactionVerificationConfig.waveConfig;
  String _configName = 'Wave Config (2s, 150x)';

  // Status en temps réel
  String _currentStatus = 'Aucune transaction en cours';
  bool _isRegistered = false;
  String? _currentUserName;

  String get _accessToken => dotenv.env['ACCESS_TOKEN'] ?? '';
  String get _refreshToken => dotenv.env['REFRESH_TOKEN'] ?? '';

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _printWelcomeMessage();
    _checkSession();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    // Le SDK gère automatiquement le cycle de vie
    // Les tokens restent en cache même en arrière-plan
    debugPrint('📱 App lifecycle state: ${state.name}');
  }

  Future<void> _checkSession() async {
    // Vérifier si le SDK est initialisé
    if (!WellooSdk.isInitialized) {
      setState(() {
        _currentStatus = '❌ SDK non initialisé';
      });
      return;
    }

    // Vérifier si des tokens valides sont présents
    final hasTokens = await WellooSdk.hasValidTokens();

    if (hasTokens) {
      final result = await _sdk.getCurrentClient();

      if (result.isSuccess && result.data != null) {
        final client = result.data!;
        setState(() {
          _isRegistered = true;
          _currentUserName = client.nom;
          _currentStatus = '✅ Session active: ${client.nom}';
        });
        debugPrint('✅ Session restaurée: ${client.nom}');
      } else {
        setState(() {
          _isRegistered = false;
          _currentStatus = '⚠️ Session expirée';
        });
        debugPrint('⚠️ Session expirée: ${result.error}');
      }
    } else {
      setState(() {
        _isRegistered = false;
        _currentStatus = '📝 Aucune session trouvée';
      });
      debugPrint('📝 Aucune session trouvée');
    }
  }

  Future<void> _registerClient() async {
    if (_accessToken.isEmpty || _refreshToken.isEmpty) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
            'Please configure ACCESS_TOKEN and REFRESH_TOKEN in .env file',
          ),
          backgroundColor: Colors.red,
          duration: Duration(seconds: 3),
        ),
      );
      return;
    }

    debugPrint('\n📝 Enregistrement du client...');

    final result = await _sdk.registerClient(
      accessToken: _accessToken,
      refreshToken: _refreshToken,
    );

    if (result.isSuccess && result.data != null) {
      final client = result.data!;
      setState(() {
        _isRegistered = true;
        _currentUserName = client.nom;
        _currentStatus = '✅ Client enregistré: ${client.nom}';
      });
      debugPrint('✅ Client enregistré: ${client.nom}');

      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Client enregistré: ${client.nom}'),
          backgroundColor: Colors.green,
        ),
      );
    } else {
      setState(() {
        _currentStatus = '❌ Erreur: ${result.error}';
      });
      debugPrint('❌ Erreur: ${result.error}');

      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Erreur: ${result.error}'),
          backgroundColor: Colors.red,
        ),
      );
    }
  }

  Future<void> _logout() async {
    debugPrint('\n👋 Déconnexion...');

    final result = await _sdk.logout();

    if (result.isSuccess) {
      setState(() {
        _isRegistered = false;
        _currentUserName = null;
        _currentStatus = '👋 Déconnecté';
        _lastTransactionRef = null;
      });
      debugPrint('✅ Déconnexion réussie');

      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Déconnexion réussie'),
          backgroundColor: Colors.green,
        ),
      );
    } else {
      debugPrint('❌ Erreur lors de la déconnexion: ${result.error}');
    }
  }

  void _printWelcomeMessage() {
    debugPrint('\n${'=' * 60}');
    debugPrint('🚀 WELLOO SDK - NOUVEAU SYSTÈME DE VÉRIFICATION');
    debugPrint('=' * 60);
    debugPrint('Configuration: $_configName');
    debugPrint('Stratégie: ${_currentConfig.strategy.name}');
    debugPrint('Polling: ${_currentConfig.pollingConfig.maxAttempts}x @ ${_currentConfig.pollingConfig.interval.inSeconds}s');
    debugPrint('Deeplink: ${_currentConfig.deeplinkConfig.enabled ? "Activé" : "Désactivé"}');
    debugPrint('Circuit Breaker: ${_currentConfig.enableCircuitBreaker ? "Activé" : "Désactivé"}');
    debugPrint('Retry Policy: Max ${_currentConfig.maxRetries} tentatives');
    debugPrint('=' * 60);
    debugPrint('\n');
  }

  Future<void> _handleDeposit() async {
    // Check if tokens are configured
    if (_accessToken.isEmpty || _refreshToken.isEmpty) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
            'Please configure ACCESS_TOKEN and REFRESH_TOKEN in .env file',
          ),
          backgroundColor: Colors.red,
          duration: Duration(seconds: 3),
        ),
      );
      return;
    }

    debugPrint('\n${'=' * 60}');
    debugPrint('📦 INITIATION DÉPÔT');
    debugPrint('=' * 60);

    Navigator.of(context).push<TransactionResult>(
      MaterialPageRoute(
        builder: (_) => WellooDeposit(
          packageName: 'com.example.welloo',
          accessToken: _accessToken,
          refreshToken: _refreshToken,

          waitResponse: (response) {
            _handleTransactionResponse(Map<String, dynamic>.from(response));
          },

          onError: (error) {
            _handleTransactionError(Map<String, dynamic>.from(error));
          },
        ),
      ),
    );
  }

  void _handleTransactionResponse(Map<String, dynamic> response) {
    debugPrint('\n✅ RÉPONSE TRANSACTION:');
    debugPrint('Status: ${response['status']}');
    debugPrint('Reference: ${response['reference_transaction']}');
    debugPrint('Data: $response');

    if (response['reference_transaction'] != null) {
      setState(() {
        _lastTransactionRef = response['reference_transaction'];
        _currentStatus = 'Transaction ${response['status']}';
      });
    }

    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Transaction ${response['status']}'),
        backgroundColor: response['status'] == 'SUCCEEDED' ? Colors.green : Colors.orange,
      ),
    );
  }

  void _handleTransactionError(Map<String, dynamic> error) {
    debugPrint('\n❌ ERREUR TRANSACTION:');
    debugPrint('Description: ${error['description']}');
    debugPrint('Details: $error');

    setState(() {
      _currentStatus = 'Erreur: ${error['description']}';
    });

    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Erreur: ${error['description']}'),
        backgroundColor: Colors.red,
      ),
    );
  }

  Future<void> _checkTransactionStatus() async {
    if (_lastTransactionRef == null) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('No transaction to check')),
      );
      return;
    }

    debugPrint('\n🔍 VÉRIFICATION MANUELLE: $_lastTransactionRef');

    try {
      final result = await _sdk.checkDepositStatus(
        reference: _lastTransactionRef!,
      );

      if (!mounted) return;

      if (result.isSuccess && result.data != null) {
        final transaction = result.data!;
        debugPrint('Status: ${transaction.status}');
        debugPrint('Reference: ${transaction.reference}');

        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(
              'Status: ${transaction.status}\nReference: ${transaction.reference}',
            ),
            backgroundColor:
                transaction.status == 'success' ? Colors.green : Colors.orange,
          ),
        );
      } else {
        debugPrint('❌ Error: ${result.error}');
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Error: ${result.error ?? "Unknown error"}'),
            backgroundColor: Colors.red,
          ),
        );
      }
    } catch (e) {
      debugPrint('❌ Exception: $e');
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error: $e')),
      );
    }
  }

  void _changeConfiguration() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Choisir une Configuration'),
        content: SizedBox(
          width: double.maxFinite,
          child: ListView(
            shrinkWrap: true,
            children: [
              _buildConfigOption('Wave Config (2s, 150x)', TransactionVerificationConfig.waveConfig),
              _buildConfigOption('Wello Config (3s, 60x)', TransactionVerificationConfig.welloConfig),
              _buildConfigOption('Production (5s, 60x)', TransactionVerificationConfig.productionConfig),
              _buildConfigOption('Development (1s, 30x)', TransactionVerificationConfig.developmentConfig),
              _buildConfigOption('Polling Only', TransactionVerificationConfig.pollingOnlyConfig),
              _buildConfigOption('Deeplink Only', TransactionVerificationConfig.deeplinkOnlyConfig),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildConfigOption(String name, TransactionVerificationConfig config) {
    return ListTile(
      title: Text(name),
      subtitle: Text(
        'Stratégie: ${config.strategy.name}\n'
        'Polling: ${config.pollingConfig.maxAttempts}x @ ${config.pollingConfig.interval.inSeconds}s',
      ),
      selected: _configName == name,
      onTap: () {
        setState(() {
          _configName = name;
          _currentConfig = config;
        });
        Navigator.pop(context);
        _printWelcomeMessage();
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Welloo SDK - Nouveau Système'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: _changeConfiguration,
            tooltip: 'Changer la configuration',
          ),
        ],
      ),
      backgroundColor: Colors.white,
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                // Configuration actuelle
                Card(
                  color: Colors.blue[50],
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Column(
                      children: [
                        const Icon(Icons.settings, size: 40, color: Colors.blue),
                        const SizedBox(height: 8),
                        Text(
                          _configName,
                          style: const TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.bold,
                          ),
                          textAlign: TextAlign.center,
                        ),
                        const SizedBox(height: 4),
                        Text(
                          'Stratégie: ${_currentConfig.strategy.name}',
                          style: TextStyle(fontSize: 12, color: Colors.grey[700]),
                        ),
                        Text(
                          'Polling: ${_currentConfig.pollingConfig.maxAttempts}x @ ${_currentConfig.pollingConfig.interval.inSeconds}s',
                          style: TextStyle(fontSize: 12, color: Colors.grey[700]),
                        ),
                      ],
                    ),
                  ),
                ),

                const SizedBox(height: 20),

                // Status actuel
                Card(
                  color: Colors.grey[50],
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Row(
                      children: [
                        const Icon(Icons.info_outline, color: Colors.blue),
                        const SizedBox(width: 12),
                        Expanded(
                          child: Text(
                            _currentStatus,
                            style: const TextStyle(fontSize: 14),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),

                const SizedBox(height: 20),

                // Bouton principal
                ElevatedButton.icon(
                  onPressed: _handleDeposit,
                  icon: const Icon(Icons.account_balance_wallet),
                  label: const Text("Initier un Dépôt"),
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size(250, 50),
                    backgroundColor: Colors.blue,
                    foregroundColor: Colors.white,
                  ),
                ),

                const SizedBox(height: 20),

                if (_lastTransactionRef != null) ...[
                  // Dernière transaction
                  Card(
                    color: Colors.orange[50],
                    child: Padding(
                      padding: const EdgeInsets.all(12.0),
                      child: Column(
                        children: [
                          Text(
                            'Dernière transaction:',
                            style: TextStyle(
                              fontSize: 11,
                              color: Colors.grey[600],
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 4),
                          SelectableText(
                            _lastTransactionRef!,
                            style: TextStyle(
                              fontSize: 12,
                              color: Colors.grey[800],
                              fontFamily: 'monospace',
                            ),
                            textAlign: TextAlign.center,
                          ),
                        ],
                      ),
                    ),
                  ),

                  const SizedBox(height: 10),

                  // Vérification manuelle
                  ElevatedButton.icon(
                    onPressed: _checkTransactionStatus,
                    icon: const Icon(Icons.refresh),
                    label: const Text("Vérifier le Statut"),
                    style: ElevatedButton.styleFrom(
                      minimumSize: const Size(250, 50),
                      backgroundColor: Colors.orange,
                      foregroundColor: Colors.white,
                    ),
                  ),

                  const SizedBox(height: 8),

                  Text(
                    'Utilisez ce bouton si le deeplink ne fonctionne pas\nou pour vérifier manuellement',
                    style: TextStyle(
                      fontSize: 11,
                      color: Colors.grey[500],
                      fontStyle: FontStyle.italic,
                    ),
                    textAlign: TextAlign.center,
                  ),
                ],

                const SizedBox(height: 30),

                // Aide
                Card(
                  color: Colors.green[50],
                  child: Padding(
                    padding: const EdgeInsets.all(12.0),
                    child: Column(
                      children: [
                        const Icon(Icons.help_outline, color: Colors.green, size: 30),
                        const SizedBox(height: 8),
                        Text(
                          'Nouveau Système de Vérification',
                          style: TextStyle(
                            fontSize: 13,
                            fontWeight: FontWeight.bold,
                            color: Colors.green[900],
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text(
                          '✓ 3 stratégies: Polling, Deeplink, Hybrid\n'
                          '✓ 8 configurations prédéfinies\n'
                          '✓ Circuit Breaker & Retry Policy\n'
                          '✓ Métriques & Events détaillés\n'
                          '✓ Logs structurés en console',
                          style: TextStyle(
                            fontSize: 11,
                            color: Colors.green[800],
                          ),
                          textAlign: TextAlign.left,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}