novac_payment_plugin 1.0.0 copy "novac_payment_plugin: ^1.0.0" to clipboard
novac_payment_plugin: ^1.0.0 copied to clipboard

Official Flutter plugin for Novac Payment Gateway. Accept payments seamlessly in your Flutter apps with support for cards, bank transfers, and mobile money.

example/lib/main.dart

import 'dart:io' show Platform;

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:novac_payment_plugin/novac_payment_plugin.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isInitialized = false;
  bool _isLoading = false;
  String? _lastTransactionRef;
  String _statusMessage = 'Enter your API key to get started';

  final TextEditingController _apiKeyController = TextEditingController();
  final TextEditingController _amountController = TextEditingController(
    text: '5000',
  );
  final TextEditingController _emailController = TextEditingController(
    text: '[email protected]',
  );

  // Brand Colors
  static const Color primaryGreen = Color(0xFFADBD52);
  static const Color darkBg = Color(0xFF1A1A1A);
  static const Color cardBg = Color(0xFF2A2A2A);
  static const Color surfaceBg = Color(0xFF333333);

  // Pre-computed colors with alpha values
  static const Color primaryGreen05 = Color(0x0DADBD52); // 5% opacity
  static const Color primaryGreen15 = Color(0x26ADBD52); // 15% opacity
  static const Color primaryGreen20 = Color(0x33ADBD52); // 20% opacity
  static const Color primaryGreen30 = Color(0x4DADBD52); // 30% opacity
  static const Color primaryGreen50 = Color(0x80ADBD52); // 50% opacity
  static const Color orange15 = Color(0x26FF9800); // 15% opacity
  static const Color orange20 = Color(0x33FF9800); // 20% opacity
  static const Color orange30 = Color(0x4DFF9800); // 30% opacity
  static const Color white10 = Color(0x1AFFFFFF); // 10% opacity
  static const Color white30 = Color(0x4DFFFFFF); // 30% opacity
  static const Color white50 = Color(0x80FFFFFF); // 50% opacity
  static const Color white70 = Color(0xB3FFFFFF); // 70% opacity

  // Currency symbol - use NGN for Android, ₦ for iOS
  String get _currencySymbol => Platform.isAndroid ? 'NGN ' : '₦';

  @override
  void dispose() {
    _apiKeyController.dispose();
    _amountController.dispose();
    _emailController.dispose();
    super.dispose();
  }

  Future<void> _initializeSDK() async {
    final apiKey = _apiKeyController.text.trim();

    if (apiKey.isEmpty) {
      setState(() => _statusMessage = 'Please enter your API key');
      return;
    }

    setState(() => _isLoading = true);

    try {
      await NovacPaymentPlugin.initialize(
        apiKey: apiKey,
        primaryColor: '#ADBD52',
        backgroundColor: '#FFFFFF',
      );
      setState(() {
        _isInitialized = true;
        _statusMessage = 'SDK initialized successfully';
      });
    } catch (e) {
      setState(() => _statusMessage = 'Initialization failed: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _startPayment() async {
    if (!_isInitialized) {
      setState(() => _statusMessage = 'Please initialize SDK first');
      return;
    }

    final amount = double.tryParse(_amountController.text.trim());
    if (amount == null || amount <= 0) {
      setState(() => _statusMessage = 'Please enter a valid amount');
      return;
    }

    final email = _emailController.text.trim();
    if (email.isEmpty) {
      setState(() => _statusMessage = 'Please enter customer email');
      return;
    }

    setState(() => _isLoading = true);

    final reference = 'TXN-${DateTime.now().millisecondsSinceEpoch}';
    _lastTransactionRef = reference;

    try {
      final result = await NovacPaymentPlugin.launchCheckout(
        amount: amount,
        currency: 'NGN',
        redirectUrl: 'https://yourapp.com/payment-complete',
        transactionReference: reference,
        customerData: CustomerData(
          email: email,
          firstName: 'Test',
          lastName: 'User',
          phoneNumber: '+2348012345678',
        ),
        customizationData: CustomizationData(
          logoUrl: 'https://yourcompany.com/logo.png',
          paymentDescription: 'Test Payment',
          checkoutModalTitle: 'Complete Payment',
        ),
      );

      if (result.isSuccess) {
        setState(
          () =>
              _statusMessage =
                  'Payment successful! ID: ${result.transactionId}',
        );
      } else if (result.isCancelled) {
        setState(() => _statusMessage = 'Payment cancelled by user');
      } else {
        setState(
          () => _statusMessage = 'Payment failed: ${result.errorMessage}',
        );
      }
    } catch (e) {
      setState(() => _statusMessage = 'Payment error: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _verifyPayment() async {
    if (_lastTransactionRef == null) {
      setState(() => _statusMessage = 'No transaction to verify');
      return;
    }

    setState(() => _isLoading = true);

    try {
      final result = await NovacPaymentPlugin.verifyPayment(
        _lastTransactionRef!,
      );
      setState(() => _statusMessage = 'Payment status: ${result.status}');
    } catch (e) {
      setState(() => _statusMessage = 'Verification error: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  String _formatNaira(String amount) {
    final value = double.tryParse(amount) ?? 0;
    final formatted = value
        .toStringAsFixed(0)
        .replaceAllMapped(
          RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
          (Match m) => '${m[1]},',
        );
    return '$_currencySymbol$formatted';
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Novac Payment',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        textTheme: GoogleFonts.redHatDisplayTextTheme(
          ThemeData.dark().textTheme,
        ),
        brightness: Brightness.dark,
        scaffoldBackgroundColor: darkBg,
        primaryColor: primaryGreen,
        colorScheme: const ColorScheme.dark(
          primary: primaryGreen,
          secondary: primaryGreen,
          surface: cardBg,
        ),
        appBarTheme: const AppBarTheme(
          backgroundColor: darkBg,
          elevation: 0,
          centerTitle: true,
        ),
        inputDecorationTheme: InputDecorationTheme(
          filled: true,
          fillColor: surfaceBg,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: BorderSide.none,
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: BorderSide.none,
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: const BorderSide(color: primaryGreen, width: 2),
          ),
          labelStyle: GoogleFonts.redHatDisplay(color: Colors.white70),
          hintStyle: GoogleFonts.redHatDisplay(color: Colors.white38),
          prefixStyle: GoogleFonts.redHatDisplay(color: Colors.white),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: primaryGreen,
            foregroundColor: darkBg,
            padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12),
            ),
            textStyle: GoogleFonts.redHatDisplay(
              fontWeight: FontWeight.w600,
              fontSize: 16,
            ),
          ),
        ),
        cardTheme: CardTheme(
          color: cardBg,
          elevation: 0,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16),
          ),
        ),
      ),
      home: Scaffold(
        body: SafeArea(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(20),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                const SizedBox(height: 20),

                // Logo / Header with SVG
                Center(
                  child: Column(
                    children: [
                      Container(
                        width: 80,
                        height: 80,
                        decoration: BoxDecoration(
                          color: primaryGreen15,
                          borderRadius: BorderRadius.circular(20),
                        ),
                        padding: const EdgeInsets.all(16),
                        child: SvgPicture.asset(
                          'assets/novac_icon.svg',
                          colorFilter: const ColorFilter.mode(
                            primaryGreen,
                            BlendMode.srcIn,
                          ),
                        ),
                      ),
                      const SizedBox(height: 16),
                      Text(
                        'Novac Payment',
                        style: GoogleFonts.redHatDisplay(
                          fontSize: 28,
                          fontWeight: FontWeight.bold,
                          color: Colors.white,
                          letterSpacing: -0.5,
                        ),
                      ),
                      const SizedBox(height: 4),
                      Text(
                        'SDK Test Environment',
                        style: GoogleFonts.redHatDisplay(
                          fontSize: 14,
                          color: white50,
                        ),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 32),

                // Status Card
                Container(
                  padding: const EdgeInsets.all(16),
                  decoration: BoxDecoration(
                    color: _isInitialized ? primaryGreen15 : orange15,
                    borderRadius: BorderRadius.circular(12),
                    border: Border.all(
                      color: _isInitialized ? primaryGreen30 : orange30,
                    ),
                  ),
                  child: Row(
                    children: [
                      Container(
                        padding: const EdgeInsets.all(8),
                        decoration: BoxDecoration(
                          color: _isInitialized ? primaryGreen20 : orange20,
                          borderRadius: BorderRadius.circular(8),
                        ),
                        child: Icon(
                          _isInitialized
                              ? Icons.check_circle
                              : Icons.info_outline,
                          color: _isInitialized ? primaryGreen : Colors.orange,
                          size: 20,
                        ),
                      ),
                      const SizedBox(width: 12),
                      Expanded(
                        child: Text(
                          _statusMessage,
                          style: GoogleFonts.redHatDisplay(
                            color:
                                _isInitialized ? primaryGreen : Colors.orange,
                            fontWeight: FontWeight.w500,
                          ),
                        ),
                      ),
                      if (_isLoading)
                        SizedBox(
                          width: 20,
                          height: 20,
                          child: CircularProgressIndicator(
                            strokeWidth: 2,
                            color:
                                _isInitialized ? primaryGreen : Colors.orange,
                          ),
                        ),
                    ],
                  ),
                ),
                const SizedBox(height: 24),

                // API Key Section
                Text(
                  'API CONFIGURATION',
                  style: GoogleFonts.redHatDisplay(
                    fontSize: 12,
                    fontWeight: FontWeight.w600,
                    color: Colors.white54,
                    letterSpacing: 1.5,
                  ),
                ),
                const SizedBox(height: 12),

                TextField(
                  controller: _apiKeyController,
                  style: GoogleFonts.redHatDisplay(color: Colors.white),
                  decoration: InputDecoration(
                    labelText: 'API Key',
                    hintText: 'Paste your Novac API key',
                    prefixIcon: Icon(
                      Icons.key_rounded,
                      color: _isInitialized ? primaryGreen : Colors.white54,
                    ),
                    suffixIcon:
                        _isInitialized
                            ? const Icon(
                              Icons.check_circle,
                              color: primaryGreen,
                            )
                            : null,
                  ),
                  enabled: !_isInitialized,
                ),
                const SizedBox(height: 16),

                // Initialize Button
                ElevatedButton(
                  onPressed:
                      (_isLoading || _isInitialized) ? null : _initializeSDK,
                  style: ElevatedButton.styleFrom(
                    backgroundColor: _isInitialized ? surfaceBg : primaryGreen,
                    foregroundColor: _isInitialized ? Colors.white54 : darkBg,
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(
                        _isInitialized
                            ? Icons.check_circle
                            : Icons.rocket_launch_rounded,
                        size: 20,
                      ),
                      const SizedBox(width: 8),
                      Text(
                        _isInitialized ? 'SDK Initialized' : 'Initialize SDK',
                      ),
                    ],
                  ),
                ),

                if (_isInitialized) ...[
                  const SizedBox(height: 32),

                  // Divider
                  Row(
                    children: [
                      const Expanded(child: Divider(color: white10)),
                      Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 16),
                        child: Text(
                          'PAYMENT',
                          style: GoogleFonts.redHatDisplay(
                            fontSize: 12,
                            fontWeight: FontWeight.w600,
                            color: white30,
                            letterSpacing: 2,
                          ),
                        ),
                      ),
                      const Expanded(child: Divider(color: white10)),
                    ],
                  ),
                  const SizedBox(height: 24),

                  // Amount Card
                  Card(
                    child: Padding(
                      padding: const EdgeInsets.all(20),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              Container(
                                padding: const EdgeInsets.all(10),
                                decoration: BoxDecoration(
                                  color: primaryGreen15,
                                  borderRadius: BorderRadius.circular(10),
                                ),
                                child: const Icon(
                                  Icons.payments_rounded,
                                  color: primaryGreen,
                                  size: 24,
                                ),
                              ),
                              const SizedBox(width: 12),
                              Text(
                                'Payment Details',
                                style: GoogleFonts.redHatDisplay(
                                  fontSize: 18,
                                  fontWeight: FontWeight.w600,
                                  color: Colors.white,
                                ),
                              ),
                            ],
                          ),
                          const SizedBox(height: 20),

                          // Amount Input
                          TextField(
                            controller: _amountController,
                            style: GoogleFonts.redHatDisplay(
                              color: Colors.white,
                              fontSize: 24,
                              fontWeight: FontWeight.bold,
                            ),
                            decoration: InputDecoration(
                              labelText: 'Amount',
                              prefixText: _currencySymbol,
                              prefixStyle: GoogleFonts.redHatDisplay(
                                color: primaryGreen,
                                fontSize: 24,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                            keyboardType: TextInputType.number,
                            onChanged: (_) => setState(() {}),
                          ),
                          const SizedBox(height: 16),

                          // Email Input
                          TextField(
                            controller: _emailController,
                            style: GoogleFonts.redHatDisplay(
                              color: Colors.white,
                            ),
                            decoration: const InputDecoration(
                              labelText: 'Customer Email',
                              hintText: '[email protected]',
                              prefixIcon: Icon(
                                Icons.email_rounded,
                                color: Colors.white54,
                              ),
                            ),
                            keyboardType: TextInputType.emailAddress,
                          ),
                        ],
                      ),
                    ),
                  ),
                  const SizedBox(height: 16),

                  // Pay Button
                  ElevatedButton(
                    onPressed: _isLoading ? null : _startPayment,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        const Icon(Icons.lock_rounded, size: 20),
                        const SizedBox(width: 8),
                        Text('Pay ${_formatNaira(_amountController.text)}'),
                      ],
                    ),
                  ),
                  const SizedBox(height: 12),

                  // Verify Button
                  OutlinedButton(
                    onPressed:
                        (_isLoading || _lastTransactionRef == null)
                            ? null
                            : _verifyPayment,
                    style: OutlinedButton.styleFrom(
                      foregroundColor: Colors.white,
                      side: BorderSide(
                        color:
                            _lastTransactionRef != null
                                ? primaryGreen50
                                : Colors.white24,
                      ),
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(
                          Icons.verified_rounded,
                          size: 20,
                          color:
                              _lastTransactionRef != null
                                  ? primaryGreen
                                  : Colors.white38,
                        ),
                        const SizedBox(width: 8),
                        Text(
                          'Verify Last Payment',
                          style: GoogleFonts.redHatDisplay(),
                        ),
                      ],
                    ),
                  ),

                  if (_lastTransactionRef != null) ...[
                    const SizedBox(height: 16),
                    Container(
                      padding: const EdgeInsets.all(14),
                      decoration: BoxDecoration(
                        color: surfaceBg,
                        borderRadius: BorderRadius.circular(10),
                      ),
                      child: Row(
                        children: [
                          const Icon(
                            Icons.receipt_long_rounded,
                            color: white50,
                            size: 18,
                          ),
                          const SizedBox(width: 10),
                          Expanded(
                            child: Text(
                              _lastTransactionRef!,
                              style: GoogleFonts.redHatDisplay(
                                fontSize: 12,
                                color: white70,
                              ),
                              overflow: TextOverflow.ellipsis,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ],
                ],

                const SizedBox(height: 32),

                // Help Card
                Container(
                  padding: const EdgeInsets.all(20),
                  decoration: BoxDecoration(
                    gradient: const LinearGradient(
                      colors: [primaryGreen15, primaryGreen05],
                      begin: Alignment.topLeft,
                      end: Alignment.bottomRight,
                    ),
                    borderRadius: BorderRadius.circular(16),
                    border: Border.all(color: primaryGreen20),
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        children: [
                          Container(
                            padding: const EdgeInsets.all(8),
                            decoration: BoxDecoration(
                              color: primaryGreen20,
                              borderRadius: BorderRadius.circular(8),
                            ),
                            child: const Icon(
                              Icons.lightbulb_rounded,
                              color: primaryGreen,
                              size: 18,
                            ),
                          ),
                          const SizedBox(width: 10),
                          Text(
                            'Quick Start Guide',
                            style: GoogleFonts.redHatDisplay(
                              fontWeight: FontWeight.w600,
                              color: Colors.white,
                              fontSize: 16,
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 16),
                      _buildStep('1', 'Get your API key from Novac Dashboard'),
                      _buildStep('2', 'Paste key and tap Initialize SDK'),
                      _buildStep('3', 'Enter payment amount'),
                      _buildStep('4', 'Tap Pay to test checkout'),
                    ],
                  ),
                ),
                const SizedBox(height: 40),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildStep(String number, String text) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 10),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: 24,
            height: 24,
            decoration: BoxDecoration(
              color: primaryGreen20,
              borderRadius: BorderRadius.circular(6),
            ),
            child: Center(
              child: Text(
                number,
                style: GoogleFonts.redHatDisplay(
                  color: primaryGreen,
                  fontWeight: FontWeight.bold,
                  fontSize: 12,
                ),
              ),
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Text(
              text,
              style: GoogleFonts.redHatDisplay(
                color: white70,
                fontSize: 14,
                height: 1.4,
              ),
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
42
downloads

Publisher

unverified uploader

Weekly Downloads

Official Flutter plugin for Novac Payment Gateway. Accept payments seamlessly in your Flutter apps with support for cards, bank transfers, and mobile money.

Homepage
Repository (GitHub)
View/report issues

Topics

#payments #payment-gateway #fintech #checkout #novac

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on novac_payment_plugin

Packages that implement novac_payment_plugin