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

Paygic Payment Plugin - Native UPI payments with popup UI for Indian payment apps

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Paygic UPI Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF2B1966),
        ),
        useMaterial3: true,
      ),
      home: const PaymentDemoPage(),
    );
  }
}

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

  @override
  State<PaymentDemoPage> createState() => _PaymentDemoPageState();
}

class _PaymentDemoPageState extends State<PaymentDemoPage> {
  // Form controllers
  final _midController = TextEditingController(text: '');
  final _tokenController = TextEditingController(text: '');
  final _amountController = TextEditingController(text: '1');
  final _orderIdController = TextEditingController();
  final _nameController = TextEditingController(text: 'Test Customer');
  final _emailController = TextEditingController(text: '[email protected]');
  final _mobileController = TextEditingController(text: '9876543210');
  
  PaymentResult? _lastResult;
  bool _isLoading = false;
  bool _hasUpiApp = false;

  @override
  void initState() {
    super.initState();
    _checkUpiAvailability();
    _generateOrderId();
  }

  void _generateOrderId() {
    _orderIdController.text = 'ORDER_${DateTime.now().millisecondsSinceEpoch}';
  }

  Future<void> _checkUpiAvailability() async {
    final hasUpi = await PaygicUpi.hasUpiApp();
    setState(() {
      _hasUpiApp = hasUpi;
    });
  }

  Future<void> _initiatePayment() async {
    // Validate inputs
    if (_midController.text.isEmpty) {
      _showError('Please enter Merchant ID (MID)');
      return;
    }
    if (_tokenController.text.isEmpty) {
      _showError('Please enter API Token');
      return;
    }

    setState(() {
      _isLoading = true;
      _lastResult = null;
    });

    try {
      // NEW SIMPLIFIED API - Just call initiatePayment!
      final result = await PaygicUpi.initiatePayment(
        context: context,
        mid: _midController.text.trim(),
        token: _tokenController.text.trim(),
        amount: double.tryParse(_amountController.text) ?? 1.0,
        merchantReferenceId: _orderIdController.text.trim(),
        customerName: _nameController.text.trim(),
        customerEmail: _emailController.text.trim(),
        customerMobile: _mobileController.text.trim(),

      );

      setState(() {
        _lastResult = result;
        _isLoading = false;
      });

      // Handle result
      if (mounted) {
        _handleResult(result);
      }
      
      // Generate new order ID for next payment
      _generateOrderId();
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      
      if (mounted) {
        _showError('Error: $e');
      }
    }
  }

  void _handleResult(PaymentResult result) {
    if (result.isSuccess) {
      // Payment successful!
      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          title: const Row(
            children: [
              Icon(Icons.check_circle, color: Colors.green, size: 32),
              SizedBox(width: 12),
              Text('Payment Successful'),
            ],
          ),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _buildResultRow('UTR', result.utr ?? 'N/A'),
              _buildResultRow('Amount', '₹${result.amount?.toStringAsFixed(2) ?? 'N/A'}'),
              _buildResultRow('Status', result.txnStatus ?? 'SUCCESS'),
            ],
          ),
          actions: [
            ElevatedButton(
              onPressed: () => Navigator.pop(context),
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.green,
                foregroundColor: Colors.white,
              ),
              child: const Text('Done'),
            ),
          ],
        ),
      );
    } else if (result.isFailed) {
      // Payment failed
      _showError(result.errorMessage ?? 'Payment failed');
    } else if (result.isCancelled) {
      // User cancelled - show subtle message
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Payment cancelled'),
          duration: Duration(seconds: 2),
        ),
      );
    }
  }

  Widget _buildResultRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
          Text(value),
        ],
      ),
    );
  }

  void _showError(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: Colors.red,
        duration: const Duration(seconds: 4),
      ),
    );
  }

  Future<void> _checkInstalledApps() async {
    final apps = await PaygicUpi.getInstalledUpiApps();
    
    if (mounted) {
      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          title: const Text('Installed UPI Apps'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: apps.isEmpty
                ? [const Text('No UPI apps found')]
                : apps.map((app) => ListTile(
                    leading: const Icon(Icons.payment),
                    title: Text(app.displayName),
                  )).toList(),
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('OK'),
            ),
          ],
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Paygic UPI Demo'),
        backgroundColor: const Color(0xFF2B1966),
        foregroundColor: Colors.white,
        actions: [
          IconButton(
            icon: const Icon(Icons.apps),
            onPressed: _checkInstalledApps,
            tooltip: 'Check UPI Apps',
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // UPI Availability indicator
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: _hasUpiApp ? Colors.green.shade50 : Colors.orange.shade50,
                borderRadius: BorderRadius.circular(8),
              ),
              child: Row(
                children: [
                  Icon(
                    _hasUpiApp ? Icons.check_circle : Icons.warning,
                    color: _hasUpiApp ? Colors.green : Colors.orange,
                  ),
                  const SizedBox(width: 8),
                  Text(
                    _hasUpiApp ? 'UPI apps available' : 'No UPI apps detected',
                    style: TextStyle(
                      color: _hasUpiApp ? Colors.green.shade700 : Colors.orange.shade700,
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 24),

            // Merchant Credentials Section
            const Text(
              'Merchant Credentials',
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
            ),
            const SizedBox(height: 8),
            TextField(
              controller: _midController,
              decoration: const InputDecoration(
                labelText: 'Merchant ID (MID)',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.business),
              ),
            ),
            const SizedBox(height: 12),
            TextField(
              controller: _tokenController,
              decoration: const InputDecoration(
                labelText: 'API Token',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.key),
              ),
              obscureText: true,
            ),
            const SizedBox(height: 24),

            // Payment Details Section
            const Text(
              'Payment Details',
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
            ),
            const SizedBox(height: 8),
            Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _amountController,
                    decoration: const InputDecoration(
                      labelText: 'Amount (₹)',
                      border: OutlineInputBorder(),
                      prefixIcon: Icon(Icons.currency_rupee),
                    ),
                    keyboardType: TextInputType.number,
                  ),
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: TextField(
                    controller: _orderIdController,
                    decoration: InputDecoration(
                      labelText: 'Order ID',
                      border: const OutlineInputBorder(),
                      suffixIcon: IconButton(
                        icon: const Icon(Icons.refresh),
                        onPressed: _generateOrderId,
                      ),
                    ),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24),

            // Customer Details Section
            const Text(
              'Customer Details',
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
            ),
            const SizedBox(height: 8),
            TextField(
              controller: _nameController,
              decoration: const InputDecoration(
                labelText: 'Customer Name',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.person),
              ),
            ),
            const SizedBox(height: 12),
            TextField(
              controller: _emailController,
              decoration: const InputDecoration(
                labelText: 'Customer Email',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.email),
              ),
              keyboardType: TextInputType.emailAddress,
            ),
            const SizedBox(height: 12),
            TextField(
              controller: _mobileController,
              decoration: const InputDecoration(
                labelText: 'Customer Mobile',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.phone),
              ),
              keyboardType: TextInputType.phone,
            ),
            const SizedBox(height: 24),

            // Pay button
            ElevatedButton(
              onPressed: _isLoading ? null : _initiatePayment,
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xFF2B1966),
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(vertical: 16),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
              ),
              child: _isLoading
                  ? const SizedBox(
                      height: 20,
                      width: 20,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                        valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                      ),
                    )
                  : const Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.payment),
                        SizedBox(width: 8),
                        Text(
                          'Pay Now',
                          style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                        ),
                      ],
                    ),
            ),
            const SizedBox(height: 24),

            // Last result
            if (_lastResult != null) ...[
              Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: _lastResult!.isSuccess 
                      ? Colors.green.shade50 
                      : (_lastResult!.isCancelled 
                          ? Colors.grey.shade100 
                          : Colors.red.shade50),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Last Payment: ${_lastResult!.status.name.toUpperCase()}',
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                    if (_lastResult!.utr != null)
                      Text('UTR: ${_lastResult!.utr}'),
                    if (_lastResult!.amount != null)
                      Text('Amount: ₹${_lastResult!.amount?.toStringAsFixed(2)}'),
                    if (_lastResult!.errorMessage != null)
                      Text('Message: ${_lastResult!.errorMessage}'),
                  ],
                ),
              ),
            ],

            const SizedBox(height: 24),

            // Instructions
            Container(
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.blue.shade50,
                borderRadius: BorderRadius.circular(8),
              ),
              child: const Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Icon(Icons.info, color: Colors.blue),
                      SizedBox(width: 8),
                      Text(
                        'How it works',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          color: Colors.blue,
                        ),
                      ),
                    ],
                  ),
                  SizedBox(height: 8),
                  Text(
                    '1. Enter your Paygic MID and Token\n'
                    '2. Fill payment and customer details\n'
                    '3. Click "Pay Now"\n'
                    '4. Choose UPI app and complete payment\n'
                    '5. Result returned automatically!',
                    style: TextStyle(fontSize: 13),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _midController.dispose();
    _tokenController.dispose();
    _amountController.dispose();
    _orderIdController.dispose();
    _nameController.dispose();
    _emailController.dispose();
    _mobileController.dispose();
    super.dispose();
  }
}
0
likes
0
points
143
downloads

Publisher

verified publisherpaygic.in

Weekly Downloads

Paygic Payment Plugin - Native UPI payments with popup UI for Indian payment apps

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, http, plugin_platform_interface, qr_flutter, socket_io_client, url_launcher

More

Packages that depend on paygic

Packages that implement paygic