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

A Flutter plugin for Omniware Payment Gateway integration.

example/lib/main.dart

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 (user inputs)
  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');

  String _mode = 'LIVE'; // or TEST
  String _hash = '';
  String _lastStatusTitle = '';
  String _lastResponseJson = '';

  @override
  void initState() {
    super.initState();
    _initPlatformVersion();
    _orderIdCtl.text = _generateOrderId();
    _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() {
    // CamelCase inputs for hashing (MUST match Swift keys)
    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,
    };

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

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

  Future<void> _startPayment() async {
    // Basic field checks
    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(); // auto-generate if user forgot
      if (_hash.isEmpty) {
        _toast('Could not generate hash');
        return;
      }
    }

    // Snake_case params for POST form
    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': '',
      'udf2': '',
      'udf3': '',
      'udf4': '',
      'udf5': '',
      'enable_auto_refund': _enableAutoRefundCtl.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) {
    // Use the global key, not ScaffoldMessenger.of(context)
    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}',
                ),
                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(
                        'Generated Hash (auto-upper)',
                        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),
                ],
              ],
            ),
          ),
        ),
      ),
    );
  }
}