end2endpay_flutter_sdk 1.0.3 copy "end2endpay_flutter_sdk: ^1.0.3" to clipboard
end2endpay_flutter_sdk: ^1.0.3 copied to clipboard

A Flutter SDK for End2EndPay payment integration with secure WebView support.

example/lib/main.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:end2endpay_flutter_sdk/end2endpay_flutter_sdk.dart';

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

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

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

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final _formKey = GlobalKey<FormState>();
  final _amountController = TextEditingController(text: '100.00');
  final _currencyController = TextEditingController(text: 'NGN');
  final _emailController = TextEditingController(text: 'customer@example.com');
  final _apiKeyController = TextEditingController(text: '<api key>');
  final _accessTokenController = TextEditingController(text: '<accessToken>');

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

  void _makePayment() {
    if (_formKey.currentState!.validate()) {
      Navigator.push(
        context,
        MaterialPageRoute(
          builder:
              (context) => PaymentScreen(
                apiKey: _apiKeyController.text,
                accessToken: _accessTokenController.text,
                amount: double.parse(_amountController.text),
                currency: _currencyController.text,
                email: _emailController.text,
              ),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('End2EndPay Demo'), elevation: 2),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              const SizedBox(height: 20),
              const Icon(Icons.payment, size: 80, color: Colors.blue),
              const SizedBox(height: 20),
              const Text(
                'Payment Configuration',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 30),
              TextFormField(
                controller: _apiKeyController,
                decoration: const InputDecoration(
                  labelText: 'API Key',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.key),
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter API key';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: _accessTokenController,
                decoration: const InputDecoration(
                  labelText: 'Access Token',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.lock),
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter access token';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: _amountController,
                decoration: const InputDecoration(
                  labelText: 'Amount',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.attach_money),
                ),
                keyboardType: const TextInputType.numberWithOptions(
                  decimal: true,
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter amount';
                  }
                  final amount = double.tryParse(value);
                  if (amount == null || amount <= 0) {
                    return 'Please enter valid amount';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: _currencyController,
                decoration: const InputDecoration(
                  labelText: 'Currency',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.currency_exchange),
                  hintText: 'USD, EUR, GBP',
                ),
                maxLength: 3,
                textCapitalization: TextCapitalization.characters,
                validator: (value) {
                  if (value == null || value.length != 3) {
                    return 'Please enter 3-letter currency code';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: _emailController,
                decoration: const InputDecoration(
                  labelText: 'Payer Email',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.email),
                ),
                keyboardType: TextInputType.emailAddress,
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter email';
                  }
                  if (!RegExp(
                    r'^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$',
                  ).hasMatch(value)) {
                    return 'Please enter valid email';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 30),
              ElevatedButton(
                onPressed: _makePayment,
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.symmetric(vertical: 16),
                  textStyle: const TextStyle(fontSize: 18),
                ),
                child: const Text('Proceed to Payment'),
              ),
              const SizedBox(height: 20),
              const Card(
                child: Padding(
                  padding: EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Note:',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 16,
                        ),
                      ),
                      SizedBox(height: 8),
                      Text(
                        '• Replace API Key and Access Token with your actual credentials\n'
                        '• All fields are required\n'
                        '• Amount must be greater than 0\n'
                        '• Currency must be a valid 3-letter code',
                        style: TextStyle(fontSize: 14),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class PaymentScreen extends StatelessWidget {
  final String apiKey;
  final String accessToken;
  final double amount;
  final String currency;
  final String email;

  const PaymentScreen({
    super.key,
    required this.apiKey,
    required this.accessToken,
    required this.amount,
    required this.currency,
    required this.email,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Complete Payment'),
        leading: IconButton(
          icon: const Icon(Icons.close),
          onPressed: () => Navigator.pop(context),
        ),
      ),
      body: End2EndPayWebView(
        apiKey: apiKey,
        accessToken: accessToken,
        amount: amount,
        currency: currency,
        reference: 'ORDER-${DateTime.now().millisecondsSinceEpoch}',
        payerEmail: email,
        onSuccess: (response) {
          debugPrint('✅ Payment Success: $response');

          // Try to parse the response
          String displayMessage = response;
          try {
            final data = jsonDecode(response);
            displayMessage =
                'Transaction ID: ${data['transactionId'] ?? 'N/A'}\n'
                'Status: ${data['status'] ?? 'Success'}';
          } catch (e) {
            // If parsing fails, use the raw response
          }

          // Navigate to success screen
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(
              builder:
                  (context) => SuccessScreen(
                    message: displayMessage,
                    amount: amount,
                    currency: currency,
                  ),
            ),
          );
        },
        onError: (error) {
          debugPrint('❌ Payment Error: $error');

          // Show error dialog
          showDialog(
            context: context,
            barrierDismissible: false,
            builder:
                (ctx) => AlertDialog(
                  title: const Row(
                    children: [
                      Icon(Icons.error, color: Colors.red),
                      SizedBox(width: 8),
                      Text('Payment Failed'),
                    ],
                  ),
                  content: Text(error),
                  actions: [
                    TextButton(
                      onPressed: () {
                        Navigator.pop(ctx);
                        Navigator.pop(context);
                      },
                      child: const Text('Close'),
                    ),
                    ElevatedButton(
                      onPressed: () {
                        Navigator.pop(ctx);
                        // The WebView will remain and can retry
                      },
                      child: const Text('Retry'),
                    ),
                  ],
                ),
          );
        },
        onCancel: (message) {
          debugPrint('⚠️ Payment Cancelled: $message');

          Navigator.pop(context);
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: const Text('Payment was cancelled'),
              action: SnackBarAction(
                label: 'Retry',
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder:
                          (context) => PaymentScreen(
                            apiKey: apiKey,
                            accessToken: accessToken,
                            amount: amount,
                            currency: currency,
                            email: email,
                          ),
                    ),
                  );
                },
              ),
            ),
          );
        },
      ),
    );
  }
}

class SuccessScreen extends StatelessWidget {
  final String message;
  final double amount;
  final String currency;

  const SuccessScreen({
    super.key,
    required this.message,
    required this.amount,
    required this.currency,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Payment Complete'),
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(24.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Icon(Icons.check_circle, color: Colors.green, size: 100),
              const SizedBox(height: 24),
              const Text(
                'Payment Successful!',
                style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 16),
              Text(
                '$currency ${amount.toStringAsFixed(2)}',
                style: const TextStyle(
                  fontSize: 36,
                  fontWeight: FontWeight.bold,
                  color: Colors.blue,
                ),
              ),
              const SizedBox(height: 24),
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Transaction Details',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      const SizedBox(height: 12),
                      Text(message, style: const TextStyle(fontSize: 14)),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 32),
              ElevatedButton.icon(
                onPressed: () {
                  Navigator.popUntil(context, (route) => route.isFirst);
                },
                icon: const Icon(Icons.home),
                label: const Text('Back to Home'),
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 32,
                    vertical: 16,
                  ),
                  textStyle: const TextStyle(fontSize: 18),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
2
likes
160
points
17
downloads

Publisher

verified publisherend2endpay.com

Weekly Downloads

A Flutter SDK for End2EndPay payment integration with secure WebView support.

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

build_runner, flutter, mockito, webview_flutter, webview_flutter_android, webview_flutter_wkwebview

More

Packages that depend on end2endpay_flutter_sdk