flutter_payment_plugin 1.0.16 copy "flutter_payment_plugin: ^1.0.16" to clipboard
flutter_payment_plugin: ^1.0.16 copied to clipboard

A Flutter plugin for Omniware Payment Gateway integration.

example/lib/main.dart

import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math';
import 'dart:async';
import 'dart:convert';

import 'package:flutter_payment_plugin/flutter_payment_plugin.dart';
import 'hash_generator.dart';

final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();

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

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

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

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';

  final _plugin = FlutterPaymentPlugin();
  final _hasher = HashGenerator();

  StreamSubscription<String>? _logSub;

  // ====== Defaults (prefilled) ======
  final _baseUrlCtl = TextEditingController(
    text: 'https://pgbiz.omniware.in',
  );

  // Merchant creds
  final _apiKeyCtl = TextEditingController(
    text: 'fb6bca86-b429-4abf-a42f-824bdd29022e',
  );

  final _saltCtl = TextEditingController(
    text: '80c67bfdf027da08de88ab5ba903fecafaab8f6d',
  );

  // Payment fields
  final _orderIdCtl = TextEditingController();

  final _amountCtl = TextEditingController(text: '2');

  final _nameCtl = TextEditingController(text: 'Tester Sharma');

  final _phoneCtl = TextEditingController(text: '9876543210');

  final _emailCtl = TextEditingController(
    text: 'tester@example.com',
  );

  final _returnUrlCtl = TextEditingController(
    text: 'https://pgbiz.omniware.in/tnpv2/return_page.php',
  );

  final _descCtl = TextEditingController(
    text: 'Payment Short Description',
  );

  final _currencyCtl = TextEditingController(text: 'INR');

  final _countryCtl = TextEditingController(text: 'IND');

  final _cityCtl = TextEditingController(text: 'Bangalore');

  final _stateCtl = TextEditingController(text: 'Karnataka');

  final _addr1Ctl = TextEditingController(text: 'ad1');

  final _addr2Ctl = TextEditingController(text: 'ad2');

  final _zipCtl = TextEditingController(text: '560001');

  final _enableAutoRefundCtl = TextEditingController(text: 'n');

  final _splitInfoCtl = TextEditingController(
    text:
        '{"vendors":[{"vendor_code":"VD1234561","split_amount_fixed":2}]}',
  );

  // UDF fields
  final _udf1Ctl = TextEditingController(text: 'ud1');
  final _udf2Ctl = TextEditingController(text: 'udf2');
  final _udf3Ctl = TextEditingController(text: 'udf3');
  final _udf4Ctl = TextEditingController(text: 'udf4');
  final _udf5Ctl = TextEditingController(text: 'udf5');

  String _mode = 'LIVE';

  String _hash = '';

  String _lastStatusTitle = '';

  String _lastResponseJson = '';

  @override
  void initState() {
    super.initState();

    _initPlatformVersion();

    _orderIdCtl.text = _generateOrderId();

    if (kIsWeb) {
      final origin = Uri.base.origin;
      if (origin.isNotEmpty) {
        _returnUrlCtl.text = '$origin/payment_return.html';
      }
    }

    _logSub = FlutterPaymentPlugin.logLines.listen((s) {
      debugPrint(s);
    });
  }

  Future<void> _initPlatformVersion() async {
    String pv;

    try {
      pv = await _plugin.getPlatformVersion() ??
          'Unknown platform version';
    } on PlatformException {
      pv = 'Failed to get platform version.';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = pv;
    });
  }

  @override
  void dispose() {
    _logSub?.cancel();

    super.dispose();
  }

  String _generateOrderId() {
    const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    const numbers = '0123456789';

    final r = Random();

    final letter = letters[r.nextInt(letters.length)];

    final digits = List.generate(
      3,
          (_) => numbers[r.nextInt(numbers.length)],
    ).join();

    return '$letter$digits';
  }

  void _regenOrderId() {
    setState(() {
      _orderIdCtl.text = _generateOrderId();
    });
  }

  void _generateHash() {
    final camel = <String, String>{
      'mode': _mode,
      'amount': _amountCtl.text,
      'name': _nameCtl.text,
      'phone': _phoneCtl.text,
      'email': _emailCtl.text,
      'returnURL': _returnUrlCtl.text,
      'description': _descCtl.text,
      'currency': _currencyCtl.text,
      'country': _countryCtl.text,
      'city': _cityCtl.text,
      'state': _stateCtl.text,
      'addressLine1': _addr1Ctl.text,
      'addressLine2': _addr2Ctl.text,
      'zipCode': _zipCtl.text,
      'enable_auto_refund': _enableAutoRefundCtl.text,
      'orderID': _orderIdCtl.text,
      'splitInfo': _splitInfoCtl.text.trim(),
      'udf1': _udf1Ctl.text,
      'udf2': _udf2Ctl.text,
      'udf3': _udf3Ctl.text,
      'udf4': _udf4Ctl.text,
      'udf5': _udf5Ctl.text,
    };

    final hash = _hasher.generateHash(
      salt: _saltCtl.text.trim(),
      apiKey: _apiKeyCtl.text.trim(),
      camelCaseInputs: camel,
    );

    setState(() {
      _hash = hash;
    });
  }

  Future<void> _startPayment() async {
    if (_baseUrlCtl.text.trim().isEmpty ||
        _apiKeyCtl.text.trim().isEmpty ||
        _saltCtl.text.trim().isEmpty) {
      _toast('Base URL, API Key and SALT are required');
      return;
    }

    if (_hash.isEmpty) {
      _generateHash();

      if (_hash.isEmpty) {
        _toast('Could not generate hash');
        return;
      }
    }

    final params = <String, String>{
      'api_key': _apiKeyCtl.text.trim(),
      'order_id': _orderIdCtl.text.trim(),
      'salt': _saltCtl.text.trim(),
      'hash': _hash,
      'mode': _mode,
      'amount': _amountCtl.text.trim(),
      'name': _nameCtl.text.trim(),
      'phone': _phoneCtl.text.trim(),
      'email': _emailCtl.text.trim(),
      'return_url': _returnUrlCtl.text.trim(),
      'description': _descCtl.text.trim(),
      'currency': _currencyCtl.text.trim(),
      'country': _countryCtl.text.trim(),
      'city': _cityCtl.text.trim(),
      'state': _stateCtl.text.trim(),
      'address_line_1': _addr1Ctl.text.trim(),
      'address_line_2': _addr2Ctl.text.trim(),
      'zip_code': _zipCtl.text.trim(),
      'udf1': _udf1Ctl.text.trim(),
      'udf2': _udf2Ctl.text.trim(),
      'udf3': _udf3Ctl.text.trim(),
      'udf4': _udf4Ctl.text.trim(),
      'udf5': _udf5Ctl.text.trim(),
      'enable_auto_refund': _enableAutoRefundCtl.text.trim(),
      'split_info': _splitInfoCtl.text.trim(),
    };

    try {
      final result = await FlutterPaymentPlugin.openPayment(
        url: _baseUrlCtl.text.trim(),
        params: params,
        title: 'Payment',
      );

      final message = result.cancelled
          ? 'Payment cancelled'
          : (result.success
          ? 'Payment success'
          : 'Payment finished with response');

      final pretty = _prettyJson(result.data);

      if (!mounted) return;

      setState(() {
        _lastStatusTitle = message;
        _lastResponseJson = pretty;
      });
    } on PlatformException catch (e) {
      if (!mounted) return;

      _toast(
        'Failed to open Payment WebView: ${e.message}',
      );
    }
  }

  String _prettyJson(String? s) {
    if (s == null || s.isEmpty) return '';

    try {
      final obj = jsonDecode(s);

      return const JsonEncoder.withIndent('  ').convert(obj);
    } catch (_) {
      return s;
    }
  }

  void _toast(String msg) {
    final sm = scaffoldMessengerKey.currentState;

    sm?.clearSnackBars();

    sm?.showSnackBar(
      SnackBar(content: Text(msg)),
    );
  }

  Widget _field(
      String label,
      TextEditingController ctl, {
        TextInputType? type,
        bool readOnly = false,
      }) {
    return TextField(
      controller: ctl,
      readOnly: readOnly,
      keyboardType: type,
      decoration: InputDecoration(
        labelText: label,
        border: const OutlineInputBorder(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final pad = const SizedBox(height: 12);

    return MaterialApp(
      scaffoldMessengerKey: scaffoldMessengerKey,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(
            'Omniware Payment Playground',
          ),
        ),
        body: SafeArea(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment:
              CrossAxisAlignment.stretch,
              children: [
                Text(
                  'Running on: $_platformVersion • Plugin: ${FlutterPaymentPlugin.version}',
                ),

                if (kIsWeb) ...[
                  pad,
                  const Text(
                    'Web: allow popups, use Return URL '
                    '…/payment_return.html, then Generate Hash before paying.',
                    style: TextStyle(fontSize: 12, color: Colors.deepOrange),
                  ),
                ],

                pad,

                _field('Base URL', _baseUrlCtl),

                pad,

                _field('API Key', _apiKeyCtl),

                pad,

                _field('SALT', _saltCtl),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'Order ID',
                        _orderIdCtl,
                      ),
                    ),

                    const SizedBox(width: 12),

                    ElevatedButton(
                      onPressed: _regenOrderId,
                      child: const Text(
                        'Generate Order ID',
                      ),
                    ),
                  ],
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: InputDecorator(
                        decoration:
                        const InputDecoration(
                          labelText: 'Mode',
                          border:
                          OutlineInputBorder(),
                        ),
                        child:
                        DropdownButtonHideUnderline(
                          child:
                          DropdownButton<String>(
                            isExpanded: true,
                            value: _mode,
                            items: const [
                              DropdownMenuItem(
                                value: 'LIVE',
                                child: Text('LIVE'),
                              ),
                              DropdownMenuItem(
                                value: 'TEST',
                                child: Text('TEST'),
                              ),
                            ],
                            onChanged: (v) {
                              setState(() {
                                _mode = v ?? 'LIVE';
                              });
                            },
                          ),
                        ),
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'Amount',
                        _amountCtl,
                        type:
                        TextInputType.number,
                      ),
                    ),
                  ],
                ),

                pad,

                _field('Name', _nameCtl),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'Phone',
                        _phoneCtl,
                        type:
                        TextInputType.phone,
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'Email',
                        _emailCtl,
                        type:
                        TextInputType.emailAddress,
                      ),
                    ),
                  ],
                ),

                pad,

                _field(
                  'Return URL',
                  _returnUrlCtl,
                  type: TextInputType.url,
                ),

                pad,

                _field(
                  'Description',
                  _descCtl,
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'Currency',
                        _currencyCtl,
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'Country',
                        _countryCtl,
                      ),
                    ),
                  ],
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'City',
                        _cityCtl,
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'State',
                        _stateCtl,
                      ),
                    ),
                  ],
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'Address Line 1',
                        _addr1Ctl,
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'Address Line 2',
                        _addr2Ctl,
                      ),
                    ),
                  ],
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'ZIP Code',
                        _zipCtl,
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'Enable Auto Refund (y/n)',
                        _enableAutoRefundCtl,
                      ),
                    ),
                  ],
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'UDF1',
                        _udf1Ctl,
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'UDF2',
                        _udf2Ctl,
                      ),
                    ),
                  ],
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'UDF3',
                        _udf3Ctl,
                      ),
                    ),

                    const SizedBox(width: 12),

                    Expanded(
                      child: _field(
                        'UDF4',
                        _udf4Ctl,
                      ),
                    ),
                  ],
                ),

                pad,

                _field(
                  'UDF5',
                  _udf5Ctl,
                ),

                pad,

                _field(
                  'Split Info (JSON)',
                  _splitInfoCtl,
                ),

                pad,

                Row(
                  children: [
                    Expanded(
                      child: _field(
                        'Generated Hash',
                        TextEditingController(
                          text: _hash,
                        ),
                        readOnly: true,
                      ),
                    ),

                    const SizedBox(width: 12),

                    ElevatedButton(
                      onPressed: _generateHash,
                      child: const Text(
                        'Generate Hash',
                      ),
                    ),
                  ],
                ),

                pad,

                ElevatedButton(
                  onPressed: _startPayment,
                  style:
                  ElevatedButton.styleFrom(
                    padding:
                    const EdgeInsets.symmetric(
                      vertical: 14,
                    ),
                  ),
                  child: const Text(
                    'Start Payment',
                  ),
                ),

                const SizedBox(height: 24),

                if (_lastStatusTitle.isNotEmpty ||
                    _lastResponseJson.isNotEmpty) ...[
                  Text(
                    _lastStatusTitle,
                    style: Theme.of(context)
                        .textTheme
                        .titleMedium,
                  ),

                  const SizedBox(height: 8),

                  Container(
                    padding:
                    const EdgeInsets.all(12),
                    decoration: BoxDecoration(
                      color: Colors.black87,
                      borderRadius:
                      BorderRadius.circular(8),
                    ),
                    child: SelectableText(
                      _lastResponseJson,
                      style: const TextStyle(
                        fontFamily: 'monospace',
                        color: Colors.white,
                      ),
                    ),
                  ),

                  const SizedBox(height: 24),
                ],
              ],
            ),
          ),
        ),
      ),
    );
  }
}