lenco_flutter 2.3.1 copy "lenco_flutter: ^2.3.1" to clipboard
lenco_flutter: ^2.3.1 copied to clipboard

A production-ready Flutter package for integrating Lenco payment gateway. Support for accounts, transactions, payments, collections, virtual accounts, and mobile money.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:lenco_flutter/lenco_flutter.dart';

void main() {
  runApp(const LencoExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lenco Flutter Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const LencoExampleHome(),
    );
  }
}

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

  @override
  State<LencoExampleHome> createState() => _LencoExampleHomeState();
}

class _LencoExampleHomeState extends State<LencoExampleHome> {
  late LencoClient lenco;
  List<LencoAccount> accounts = [];
  List<LencoTransaction> transactions = [];
  List<Bank> banks = [];
  bool isLoading = false;
  String? errorMessage;
  String? successMessage;

  // Form controllers
  final _apiKeyController = TextEditingController();
  final _accountNumberController = TextEditingController();
  final _bankCodeController = TextEditingController();
  final _amountController = TextEditingController();
  final _narrationController = TextEditingController();
  final _phoneController = TextEditingController();
  final _operatorController = TextEditingController();
  final _countryController = TextEditingController(text: 'ZM');
  final _otpController = TextEditingController();

  @override
  void initState() {
    super.initState();
    // Initialize with sandbox API key for demo
    _apiKeyController.text = 'your-sandbox-api-key';
    _bankCodeController.text = '044'; // Access Bank
  }

  @override
  void dispose() {
    _apiKeyController.dispose();
    _accountNumberController.dispose();
    _bankCodeController.dispose();
    _amountController.dispose();
    _narrationController.dispose();
    _phoneController.dispose();
    _operatorController.dispose();
    _countryController.dispose();
    _otpController.dispose();
    lenco.close();
    super.dispose();
  }

  void _initializeClient() {
    if (_apiKeyController.text.isEmpty) {
      _showError('Please enter your API key');
      return;
    }

    try {
      lenco = LencoClient.sandbox(apiKey: _apiKeyController.text);
      _showSuccess('Client initialized successfully');
    } catch (e) {
      _showError('Failed to initialize client: $e');
    }
  }

  Future<void> _getAccounts() async {
    if (!_isClientInitialized()) return;

    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    try {
      final accountsList = await lenco.accounts.getAccounts();
      setState(() {
        accounts = accountsList;
        successMessage = 'Retrieved ${accountsList.length} accounts';
      });
    } on LencoException catch (e) {
      _showError('Failed to get accounts: ${e.message}');
    } catch (e) {
      _showError('Unexpected error: $e');
    } finally {
      setState(() {
        isLoading = false;
      });
    }
  }

  Future<void> _getTransactions() async {
    if (!_isClientInitialized() || accounts.isEmpty) {
      _showError('Please get accounts first');
      return;
    }

    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    try {
      final transactionsList = await lenco.transactions.getTransactions(
        limit: 10,
      );
      setState(() {
        transactions = transactionsList;
        successMessage = 'Retrieved ${transactionsList.length} transactions';
      });
    } on LencoException catch (e) {
      _showError('Failed to get transactions: ${e.message}');
    } catch (e) {
      _showError('Unexpected error: $e');
    } finally {
      setState(() {
        isLoading = false;
      });
    }
  }

  Future<void> _getBanks() async {
    if (!_isClientInitialized()) return;

    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    try {
      final banksList = await lenco.banks.getBanks();
      setState(() {
        banks = banksList;
        successMessage = 'Retrieved ${banksList.length} banks';
      });
    } on LencoException catch (e) {
      _showError('Failed to get banks: ${e.message}');
    } catch (e) {
      _showError('Unexpected error: $e');
    } finally {
      setState(() {
        isLoading = false;
      });
    }
  }

  Future<void> _verifyAccount() async {
    if (!_isClientInitialized()) return;

    if (_accountNumberController.text.isEmpty ||
        _bankCodeController.text.isEmpty) {
      _showError('Please enter account number and bank code');
      return;
    }

    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    try {
      final result = await lenco.resolve.bankAccount(
        accountNumber: _accountNumberController.text,
        bankCode: _bankCodeController.text,
      );
      final accountName = result['accountName'] as String? ?? 'Unknown';
      _showSuccess('Account verified: $accountName');
    } on LencoException catch (e) {
      _showError('Failed to verify account: ${e.message}');
    } catch (e) {
      _showError('Unexpected error: $e');
    } finally {
      setState(() {
        isLoading = false;
      });
    }
  }

  Future<void> _makePayment() async {
    if (!_isClientInitialized() || accounts.isEmpty) {
      _showError('Please initialize client and get accounts first');
      return;
    }

    if (_amountController.text.isEmpty ||
        _accountNumberController.text.isEmpty ||
        _bankCodeController.text.isEmpty) {
      _showError('Please fill in all required fields');
      return;
    }

    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    try {
      final payment = await lenco.payments.transferToBankAccount(
        accountId: accounts.first.id,
        amount: _amountController.text,
        currency: 'NGN', // Default currency, adjust as needed
        reference: 'PAY-${DateTime.now().millisecondsSinceEpoch}',
        recipientAccountNumber: _accountNumberController.text,
        recipientBankCode: _bankCodeController.text,
        narration: _narrationController.text.isNotEmpty
            ? _narrationController.text
            : 'Test payment',
      );
      _showSuccess(
          'Payment initiated: ${payment.reference} (Status: ${payment.status})');
    } on LencoException catch (e) {
      _showError('Failed to make payment: ${e.message}');
    } catch (e) {
      _showError('Unexpected error: $e');
    } finally {
      setState(() {
        isLoading = false;
      });
    }
  }

  Future<void> _collectMobileMoneyV2() async {
    if (!_isClientInitialized()) return;

    if (_amountController.text.isEmpty ||
        _phoneController.text.isEmpty ||
        _operatorController.text.isEmpty) {
      _showError('Enter amount, phone and operator');
      return;
    }

    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    try {
      final req = CollectionRequest(
        amount: _amountController.text,
        currency: 'USD',
        reference: 'ORDER-${DateTime.now().millisecondsSinceEpoch}',
      );
      final col = await lenco.collections.createMobileMoneyCollection(
        request: req,
        phone: _phoneController.text,
        operator: _operatorController.text,
        country:
            _countryController.text.isNotEmpty ? _countryController.text : 'ZM',
      );
      _showSuccess('Collection created: ${col.id} (${col.status})');
    } on LencoException catch (e) {
      _showError('Collection failed: ${e.message}');
    } catch (e) {
      _showError('Unexpected error: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }

  Future<void> _submitMobileMoneyOtp() async {
    if (!_isClientInitialized()) return;
    if (_otpController.text.isEmpty) {
      _showError('Enter OTP and collection ID');
      return;
    }
    setState(() {
      isLoading = true;
      errorMessage = null;
    });
    try {
      final id = successMessage != null && successMessage!.contains(':')
          ? successMessage!.split(':').last.trim().split(' ').first
          : '';
      if (id.isEmpty) {
        _showError('No recent collection id found');
      } else {
        final res = await lenco.collections.submitMobileMoneyOtp(
          collectionId: id,
          otp: _otpController.text,
        );
        _showSuccess('OTP submitted. Status: ${res.status}');
      }
    } on LencoException catch (e) {
      _showError('OTP submit failed: ${e.message}');
    } catch (e) {
      _showError('Unexpected error: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }

  Future<void> _listSettlements() async {
    if (!_isClientInitialized()) return;
    setState(() {
      isLoading = true;
      errorMessage = null;
    });
    try {
      final list = await lenco.settlements.getSettlements(limit: 5);
      _showSuccess('Settlements: ${list.length}');
    } on LencoException catch (e) {
      _showError('Failed to get settlements: ${e.message}');
    } finally {
      setState(() => isLoading = false);
    }
  }

  Future<void> _listRecipients() async {
    if (!_isClientInitialized()) return;
    setState(() {
      isLoading = true;
      errorMessage = null;
    });
    try {
      final list = await lenco.recipients.getRecipients();
      _showSuccess('Recipients: ${list.length}');
    } on LencoException catch (e) {
      _showError('Failed to get recipients: ${e.message}');
    } finally {
      setState(() => isLoading = false);
    }
  }

  bool _isClientInitialized() {
    try {
      return lenco.config.apiKey.isNotEmpty;
    } catch (e) {
      return false;
    }
  }

  void _showError(String message) {
    setState(() {
      errorMessage = message;
      successMessage = null;
    });
  }

  void _showSuccess(String message) {
    setState(() {
      successMessage = message;
      errorMessage = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Lenco Flutter Example'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // API Key Configuration
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Configuration',
                      style:
                          TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _apiKeyController,
                      decoration: const InputDecoration(
                        labelText: 'API Key',
                        hintText: 'Enter your Lenco API key',
                        border: OutlineInputBorder(),
                      ),
                      obscureText: true,
                    ),
                    const SizedBox(height: 16),
                    ElevatedButton(
                      onPressed: _initializeClient,
                      child: const Text('Initialize Client'),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),

            // Action Buttons
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'API Operations',
                      style:
                          TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 16),
                    Wrap(
                      spacing: 8,
                      runSpacing: 8,
                      children: [
                        ElevatedButton(
                          onPressed: isLoading ? null : _getAccounts,
                          child: const Text('Get Accounts'),
                        ),
                        ElevatedButton(
                          onPressed: isLoading ? null : _getTransactions,
                          child: const Text('Get Transactions'),
                        ),
                        ElevatedButton(
                          onPressed: isLoading ? null : _getBanks,
                          child: const Text('Get Banks'),
                        ),
                        ElevatedButton(
                          onPressed: isLoading ? null : _listRecipients,
                          child: const Text('Recipients (v2)'),
                        ),
                        ElevatedButton(
                          onPressed: isLoading ? null : _listSettlements,
                          child: const Text('Settlements (v2)'),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),

            // Payment Form
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Payment Operations',
                      style:
                          TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _accountNumberController,
                      decoration: const InputDecoration(
                        labelText: 'Recipient Account Number',
                        hintText: '1234567890',
                        border: OutlineInputBorder(),
                      ),
                      keyboardType: TextInputType.number,
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _bankCodeController,
                      decoration: const InputDecoration(
                        labelText: 'Bank Code',
                        hintText: '044 (Access Bank)',
                        border: OutlineInputBorder(),
                      ),
                      keyboardType: TextInputType.number,
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _amountController,
                      decoration: const InputDecoration(
                        labelText: 'Amount (in kobo)',
                        hintText: '10000',
                        border: OutlineInputBorder(),
                      ),
                      keyboardType: TextInputType.number,
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _narrationController,
                      decoration: const InputDecoration(
                        labelText: 'Narration (Optional)',
                        hintText: 'Payment description',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _phoneController,
                      decoration: const InputDecoration(
                        labelText: 'Phone (MSISDN input accepted)',
                        hintText: '+260971234567 or 0971234567',
                        border: OutlineInputBorder(),
                      ),
                      keyboardType: TextInputType.phone,
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _operatorController,
                      decoration: const InputDecoration(
                        labelText: 'Operator',
                        hintText: 'mtn | airtel | zamtel',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _countryController,
                      decoration: const InputDecoration(
                        labelText: 'Country',
                        hintText: 'ZM',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _otpController,
                      decoration: const InputDecoration(
                        labelText: 'OTP (for mobile money)',
                        hintText: '123456',
                        border: OutlineInputBorder(),
                      ),
                      keyboardType: TextInputType.number,
                    ),
                    const SizedBox(height: 16),
                    Wrap(
                      spacing: 8,
                      runSpacing: 8,
                      children: [
                        ElevatedButton(
                          onPressed: isLoading ? null : _verifyAccount,
                          child: const Text('Verify Account'),
                        ),
                        ElevatedButton(
                          onPressed: isLoading ? null : _makePayment,
                          child: const Text('Make Payment'),
                        ),
                        ElevatedButton(
                          onPressed: isLoading ? null : _collectMobileMoneyV2,
                          child: const Text('Collect (Mobile Money v2)'),
                        ),
                        ElevatedButton(
                          onPressed: isLoading ? null : _submitMobileMoneyOtp,
                          child: const Text('Submit OTP (v2)'),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),

            // Loading Indicator
            if (isLoading)
              const Card(
                child: Padding(
                  padding: EdgeInsets.all(16.0),
                  child: Center(
                    child: CircularProgressIndicator(),
                  ),
                ),
              ),

            // Error Message
            if (errorMessage != null)
              Card(
                color: Colors.red.shade50,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Row(
                    children: [
                      Icon(Icons.error, color: Colors.red.shade700),
                      const SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          errorMessage!,
                          style: TextStyle(color: Colors.red.shade700),
                        ),
                      ),
                    ],
                  ),
                ),
              ),

            // Success Message
            if (successMessage != null)
              Card(
                color: Colors.green.shade50,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Row(
                    children: [
                      Icon(Icons.check_circle, color: Colors.green.shade700),
                      const SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          successMessage!,
                          style: TextStyle(color: Colors.green.shade700),
                        ),
                      ),
                    ],
                  ),
                ),
              ),

            // Accounts List
            if (accounts.isNotEmpty)
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Accounts',
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(height: 16),
                      ...accounts.map((account) => ListTile(
                            title: Text(account.name),
                            subtitle: Text(
                                '${account.bankAccount.accountNumber} - ${account.bankAccount.bank.name}'),
                            trailing: Text(
                                '${account.currency} ${account.availableBalance}'),
                            leading: const Icon(Icons.account_balance),
                          )),
                    ],
                  ),
                ),
              ),

            // Transactions List
            if (transactions.isNotEmpty)
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Recent Transactions',
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(height: 16),
                      ...transactions.map((transaction) => ListTile(
                            title: Text(transaction.description),
                            subtitle: Text(
                                '${transaction.type.toUpperCase()} - ${transaction.reference}'),
                            trailing: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.end,
                              children: [
                                Text('₦${transaction.amount}'),
                                Text(
                                  transaction.status,
                                  style: TextStyle(
                                    color: transaction.status == 'completed'
                                        ? Colors.green
                                        : Colors.orange,
                                    fontSize: 12,
                                  ),
                                ),
                              ],
                            ),
                            leading: Icon(
                              transaction.type == 'credit'
                                  ? Icons.arrow_downward
                                  : Icons.arrow_upward,
                              color: transaction.type == 'credit'
                                  ? Colors.green
                                  : Colors.red,
                            ),
                          )),
                    ],
                  ),
                ),
              ),

            // Banks List
            if (banks.isNotEmpty)
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Supported Banks',
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(height: 16),
                      ...banks.take(10).map((bank) => ListTile(
                            title: Text(bank.name),
                            subtitle: Text('Code: ${bank.code}'),
                            leading: const Icon(Icons.account_balance),
                          )),
                      if (banks.length > 10)
                        Text('... and ${banks.length - 10} more banks'),
                    ],
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}
1
likes
160
points
401
downloads

Publisher

unverified uploader

Weekly Downloads

A production-ready Flutter package for integrating Lenco payment gateway. Support for accounts, transactions, payments, collections, virtual accounts, and mobile money.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#payment #gateway #lenco #fintech #api

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

equatable, flutter, http, json_annotation, meta

More

Packages that depend on lenco_flutter