Skip to main content

flutter_nmi 1.0.27 copy "flutter_nmi: ^1.0.27" to clipboard
flutter_nmi: ^1.0.27 copied to clipboard

PlatformAndroid

NMI Payment Integration.

example/lib/main.dart

import 'dart:async';
import 'dart:convert';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_beep_plus/flutter_beep_plus.dart';
import 'package:flutter_nmi/flutter_nmi.dart';

class NMIDevice {
  static const kGeniusMini = "Genius Mini";
  static const kBBPOS = "BBPOS";
  static const kMiura = "Miura";
  static const kOther = "Other";
}

class Action {
  static const kTransaction = "Transaction";
  static const kTMSUpdate = "TMSUpdate";
  static const kPinPadStatus = "PinPadStatus";
  static const kPreAuth = "PreAuth";
  static const kConfirm = "Confirm";
  static const kVoid = "Void";
}

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

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

class _MyAppState extends State<MyApp> {
  late FlutterNmi nmi;
  String transactionStatus = "Click below button to make payment";
  bool isLoading = false;
  bool isTMSUpdate = false;
  bool isPinPadStatus = false;
  bool mShowWarningDialog = false;
  BuildContext? mBuilderContext;
  Timer? timer;

  TextEditingController amountController =
      TextEditingController(text: "10");
  TextEditingController tipController = TextEditingController(text: "0");
  TextEditingController preAuthAmountController =
      TextEditingController(text: "10.00");
  TextEditingController confirmAmountController =
      TextEditingController(text: "12.00");
  TextEditingController confirmTipController = TextEditingController(text: "0");
  TextEditingController transactionIdController = TextEditingController();
  String? lastUserReference;

  final _flutterBeepPlusPlugin = FlutterBeepPlus();

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

  @override
  void dispose() {
    amountController.dispose();
    tipController.dispose();
    preAuthAmountController.dispose();
    confirmAmountController.dispose();
    confirmTipController.dispose();
    transactionIdController.dispose();
    timer?.cancel();
    super.dispose();
  }

  /// Returns a trimmed amount string if valid (> 0), otherwise null.
  String? validateAmount(String raw, {String label = "Amount"}) {
    final value = raw.trim();
    if (value.isEmpty) {
      setState(() {
        transactionStatus = "Enter $label";
      });
      return null;
    }
    final parsed = double.tryParse(value);
    if (parsed == null || parsed <= 0) {
      setState(() {
        transactionStatus = "Invalid $label";
      });
      return null;
    }
    return value;
  }

  initialize() async {
    final amount = validateAmount(amountController.text, label: "sale amount");
    if (amount == null) return;

    final tipRaw = tipController.text.trim();
    final tipParsed = tipRaw.isEmpty ? 0.0 : double.tryParse(tipRaw);
    if (tipParsed == null || tipParsed < 0) {
      setState(() {
        transactionStatus = "Invalid tip";
      });
      return;
    }

    setState(() {
      isLoading = true;
      transactionStatus = tipParsed > 0
          ? "Sale started \$$amount + tip \$$tipRaw"
          : "Sale started \$$amount";
    });

    String strRandomValue = getRandom4Chars();

    var params = {
      "action": Action.kTransaction,
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "signature_required": "1",
      "no_signature_below_amount": "3",
      "amount": amount,
      "tip": tipParsed > 0 ? tipRaw : "0",
      "surcharge": "0",
      "device_type": NMIDevice.kBBPOS,
      "currency": "USD",
      "transactionId": "",
      "userReference": "802301723469721-$strRandomValue",
      "deviceName": "IDTech 665",
      "deviceType": "USB",
    };
    //Service charge

    nmi = FlutterNmi.instance(
      onPinPadInitializationCompleted: onPinPadInitializationCompleted,
      onPinPadInitializationFailed: onPinPadInitializationFailed,
      onPinPadConnectionStarted: onPinPadConnectionStarted,
      onPinPadConnected: onPinPadConnected,
      onPinPadDisConnected: onPinPadDisConnected,
      onPinPadConnectionFailed: onPinPadConnectionFailed,
      onPinPadConfigurationUpdate: onPinPadConfigurationUpdate,
      onPinPadTMSUpdateCompleted: onPinPadTMSUpdateCompleted,
      onPinPadTMSUpdateFailed: onPinPadTMSUpdateFailed,
      onTransactionStatusUpdate: onTransactionStatusUpdate,
      onTransactionFailed: onTransactionFailed,
      onTransactionCompleted: onTransactionCompleted,
      onPinPadStatusUpdated: onPinPadStatusUpdated,
      onTransactionTerminated: onTransactionTerminated,
      onPartialApprovalRequired: onPartialApprovalRequired,
      onTransactionFinishedLog: onTransactionFinishedLog,
    );
    mShowWarningDialog = true;

    nmi.initialize(params);
  }

  requestTMSUpdate() {
    setState(() {
      isLoading = true;
    });

    isTMSUpdate = true;

    var params = {
      "action": Action.kTMSUpdate,
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "device_type": NMIDevice.kBBPOS,
    };

    nmi = FlutterNmi.instance(
      onPinPadInitializationCompleted: onPinPadInitializationCompleted,
      onPinPadInitializationFailed: onPinPadInitializationFailed,
      onPinPadConnectionStarted: onPinPadConnectionStarted,
      onPinPadConnected: onPinPadConnected,
      onPinPadDisConnected: onPinPadDisConnected,
      onPinPadConnectionFailed: onPinPadConnectionFailed,
      onPinPadConfigurationUpdate: onPinPadConfigurationUpdate,
      onPinPadTMSUpdateCompleted: onPinPadTMSUpdateCompleted,
      onPinPadTMSUpdateFailed: onPinPadTMSUpdateFailed,
    );

    nmi.initialize(params);
  }

  requestFullTMSUpdate() {
    setState(() {
      isLoading = true;
    });

    isTMSUpdate = true;

    var params = {
      "action": Action.kTMSUpdate,
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "device_type": NMIDevice.kBBPOS,
      "full_tms_update": "1",
    };

    nmi = FlutterNmi.instance(
      onPinPadInitializationCompleted: onPinPadInitializationCompleted,
      onPinPadInitializationFailed: onPinPadInitializationFailed,
      onPinPadConnectionStarted: onPinPadConnectionStarted,
      onPinPadConnected: onPinPadConnected,
      onPinPadDisConnected: onPinPadDisConnected,
      onPinPadConnectionFailed: onPinPadConnectionFailed,
      onPinPadConfigurationUpdate: onPinPadConfigurationUpdate,
      onPinPadTMSUpdateCompleted: onPinPadTMSUpdateCompleted,
      onPinPadTMSUpdateFailed: onPinPadTMSUpdateFailed,
    );

    nmi.initialize(params);
  }

  selectPinPad() {
    setState(() {
      isLoading = true;
    });

    nmi = FlutterNmi.instance(
      onPinPadInitializationCompleted: onPinPadInitializationCompleted,
      onPinPadInitializationFailed: onPinPadInitializationFailed,
      onPinPadConnectionStarted: onPinPadConnectionStarted,
      onPinPadConnected: onPinPadConnected,
      onPinPadDisConnected: onPinPadDisConnected,
      onPinPadConnectionFailed: onPinPadConnectionFailed,
      onPinPadConfigurationUpdate: onPinPadConfigurationUpdate,
      onPinPadTMSUpdateCompleted: onPinPadTMSUpdateCompleted,
      onPinPadTMSUpdateFailed: onPinPadTMSUpdateFailed,
    );

    nmi.selectPinPad();
  }

  getPinPadStatus() {
    setState(() {
      isLoading = true;
    });

    isPinPadStatus = true;

    var params = {
      "action": Action.kPinPadStatus,
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "device_type": NMIDevice.kBBPOS,
    };

    nmi = FlutterNmi.instance(
      onPinPadInitializationCompleted: onPinPadInitializationCompleted,
      onPinPadInitializationFailed: onPinPadInitializationFailed,
      onPinPadConnectionStarted: onPinPadConnectionStarted,
      onPinPadConnected: onPinPadConnected,
      onPinPadDisConnected: onPinPadDisConnected,
      onPinPadConnectionFailed: onPinPadConnectionFailed,
      onPinPadConfigurationUpdate: onPinPadConfigurationUpdate,
      onPinPadTMSUpdateCompleted: onPinPadTMSUpdateCompleted,
      onPinPadTMSUpdateFailed: onPinPadTMSUpdateFailed,
      onPinPadStatusUpdated: onPinPadStatusUpdated,
    );

    nmi.initialize(params);
  }

  requestRefund() {
    setState(() {
      isLoading = true;
    });

    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "device_type": NMIDevice.kBBPOS,
      "currency": "USD",
      "transactionId": transactionIdController.text,
      "amount": amountController.text,
    };

    nmi = FlutterNmi.instance(
      onTransactionStatusUpdate: onTransactionStatusUpdate,
      onTransactionFailed: onTransactionFailed,
      onTransactionCompleted: onTransactionCompleted,
    );
    mShowWarningDialog = false;

    nmi.requestRefund(params);
  }

  manualCreditPayment() {
    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "device_type": NMIDevice.kBBPOS,
      "sandbox": "1",
      "currency": "USD",
      "type": "credit",
      "ccnumber": "4111111111111111",
      "ccexp": "1025",
      "amount": "1.00",
    };

    nmi = FlutterNmi.instance(
      onTransactionStatusUpdate: onTransactionStatusUpdate,
      onTransactionFailed: onTransactionFailed,
      onTransactionCompleted: onTransactionCompleted,
    );
    mShowWarningDialog = false;

    nmi.manualCreditPayment(params);
  }

  manualKeyedPreAuth() {
    setState(() {
      isLoading = true;
    });

    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "ccnumber": "4111111111111111",
      "ccexp": "1025",
      "cvv": "999",
      "amount": amountController.text.trim().isEmpty
          ? "1.00"
          : amountController.text.trim(),
    };

    nmi = FlutterNmi.instance(
      onTransactionFailed: onTransactionFailed,
      onTransactionCompleted: onTransactionCompleted,
    );
    mShowWarningDialog = false;
    nmi.manualPreAuth(params);
  }

  manualKeyedCapture() {
    setState(() {
      isLoading = true;
    });

    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "transactionid": transactionIdController.text.trim(),
      "amount": amountController.text.trim().isEmpty
          ? "1.00"
          : amountController.text.trim(),
    };

    nmi = FlutterNmi.instance(
      onTransactionFailed: onTransactionFailed,
      onTransactionCompleted: onTransactionCompleted,
    );
    nmi.manualCapture(params);
  }

  manualKeyedVoid() {
    setState(() {
      isLoading = true;
    });

    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "transactionid": transactionIdController.text.trim(),
    };

    nmi = FlutterNmi.instance(
      onTransactionFailed: onTransactionFailed,
      onTransactionCompleted: onTransactionCompleted,
    );
    nmi.manualVoidAuth(params);
  }

  cancelTransaction() {
    nmi.cancelTransaction();
  }

  ensureNmi() {
    nmi = FlutterNmi.instance(
      onPinPadInitializationCompleted: onPinPadInitializationCompleted,
      onPinPadInitializationFailed: onPinPadInitializationFailed,
      onPinPadConnectionStarted: onPinPadConnectionStarted,
      onPinPadConnected: onPinPadConnected,
      onPinPadDisConnected: onPinPadDisConnected,
      onPinPadConnectionFailed: onPinPadConnectionFailed,
      onPinPadConfigurationUpdate: onPinPadConfigurationUpdate,
      onPinPadTMSUpdateCompleted: onPinPadTMSUpdateCompleted,
      onPinPadTMSUpdateFailed: onPinPadTMSUpdateFailed,
      onTransactionStatusUpdate: onTransactionStatusUpdate,
      onTransactionFailed: onTransactionFailed,
      onTransactionCompleted: onTransactionCompleted,
      onPinPadStatusUpdated: onPinPadStatusUpdated,
      onTransactionTerminated: onTransactionTerminated,
      onPartialApprovalRequired: onPartialApprovalRequired,
      onTransactionFinishedLog: onTransactionFinishedLog,
    );
  }

  onTransactionFinishedLog(value) {
    if (kDebugMode) {
      print("Flutter ==== transactionFinished: $value");
    }
  }

  /// ChipDna paused for merchant decision on reduced amount (Android).
  onPartialApprovalRequired(value) {
    if (kDebugMode) {
      print("Flutter ==== Partial Approval Required $value");
    }

    Map<String, dynamic> data = {};
    try {
      data = jsonDecode(value is String ? value : value.toString())
          as Map<String, dynamic>;
    } catch (_) {}

    final authorizedMinor =
        data["amountOnlineAuthorized"]?.toString() ??
            data["AMOUNT_ONLINE_AUTHORIZED"]?.toString() ??
            "";
    final responseRequired =
        data["responseRequired"]?.toString().toLowerCase() == "true";
    final authorizedDollars = authorizedMinor.isEmpty
        ? null
        : (int.tryParse(authorizedMinor) ?? 0) / 100.0;
    final authorizedLabel = authorizedDollars == null
        ? "?"
        : authorizedDollars.toStringAsFixed(2);

    setState(() {
      transactionStatus = "Partial approval: \$$authorizedLabel authorized";
    });

    if (!responseRequired || !mounted) return;

    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (ctx) => AlertDialog(
        title: const Text("Partial approval"),
        content: Text(
          "Issuer approved \$$authorizedLabel. Accept reduced amount?",
        ),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.of(ctx).pop();
              nmi.continuePartialApproval(accept: false);
            },
            child: const Text("Decline"),
          ),
          TextButton(
            onPressed: () {
              Navigator.of(ctx).pop();
              nmi.continuePartialApproval(accept: true);
            },
            child: const Text("Accept"),
          ),
        ],
      ),
    );
  }

  /// Pre-auth (open auth; native sets TippingType=END_OF_DAY; tip on confirm).
  requestPreAuth() {
    final amount =
        validateAmount(preAuthAmountController.text, label: "pre-auth amount");
    if (amount == null) return;

    String strRandomValue = getRandom4Chars();
    lastUserReference = "802301723469721-$strRandomValue";
    transactionIdController.text = lastUserReference!;

    setState(() {
      isLoading = true;
      transactionStatus = "Pre-auth started \$$amount";
    });

    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "signature_required": "1",
      "no_signature_below_amount": "3",
      "amount": amount,
      "surcharge": "0",
      "device_type": NMIDevice.kBBPOS,
      "currency": "USD",
      "userReference": lastUserReference,
      "deviceName": "IDTech 665",
      "deviceType": "USB",
      "TippingType": "END_OF_DAY",
    };

    ensureNmi();
    mShowWarningDialog = false;
    nmi.preAuth(params);
  }

  /// Confirm / post-auth with separate TipAmount (same rule as sale).
  requestConfirm() {
    final userRef =
        (lastUserReference ?? transactionIdController.text).trim();
    if (userRef.isEmpty) {
      setState(() {
        transactionStatus = "Run Pre-auth first or enter UserReference";
      });
      return;
    }

    final amount = validateAmount(confirmAmountController.text,
        label: "confirm amount");
    if (amount == null) return;

    final tipRaw = confirmTipController.text.trim();
    final tipParsed = tipRaw.isEmpty ? 0.0 : double.tryParse(tipRaw);
    if (tipParsed == null || tipParsed < 0) {
      setState(() {
        transactionStatus = "Invalid confirm tip";
      });
      return;
    }

    setState(() {
      isLoading = true;
      transactionStatus = tipParsed > 0
          ? "Confirm started \$$amount + tip \$$tipRaw ($userRef)"
          : "Confirm started \$$amount ($userRef)";
    });

    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "amount": amount,
      "tip": tipParsed > 0 ? tipRaw : "0",
      "currency": "USD",
      "userReference": userRef,
      "deviceName": "IDTech 665",
      "deviceType": "USB",
    };

    ensureNmi();
    mShowWarningDialog = false;
    nmi.confirmTransaction(params);
  }

  requestVoid() {
    final userRef =
        (lastUserReference ?? transactionIdController.text).trim();
    if (userRef.isEmpty) {
      setState(() {
        transactionStatus = "Run Pre-auth first or enter UserReference";
      });
      return;
    }

    setState(() {
      isLoading = true;
      transactionStatus = "Void started ($userRef)";
    });

    var params = {
      "api_key": "87CXUKtbNXpdE55Mp482N3qyuB5M6SfY",
      "sandbox": "1",
      "userReference": userRef,
      "deviceName": "IDTech 665",
      "deviceType": "USB",
    };

    ensureNmi();
    mShowWarningDialog = false;
    nmi.voidTransaction(params);
  }

  disconnectPinPad() {
    nmi.disconnectPinPad();
  }

  onPinPadInitializationFailed() {
    if (kDebugMode) {
      print("Flutter ==== onPinPad Initialization Failed");
    }

    setState(() {
      isLoading = false;
      transactionStatus = "PinPad Initialization Failed";
    });
  }

  onPinPadInitializationCompleted() {
    if (kDebugMode) {
      print("Flutter ==== onPinPad Initialization Completed");
    }

    setState(() {
      //isLoading = false;
      transactionStatus = "PinPad Initialization Completed";
    });
  }

  onPinPadConnected(value) {
    if (kDebugMode) {
      print("Flutter ==== onPinPad Connection Completed $value");
    }

    setState(() {
      transactionStatus = "PinPad Connected";
    });

    /*if (isTMSUpdate) {
      nmi.requestTMSUpdate();
    } else if (isPinPadStatus) {
      nmi.getPinPadStatus();
    }*/
  }

  onPinPadConnectionFailed([value]) {
    if (kDebugMode) {
      print("Flutter ==== onPinPad Connection Failed $value");
    }

    setState(() {
      isLoading = false;
      transactionStatus = value != null
          ? "PinPad Connection Failed\n$value"
          : "PinPad Connection Failed";
    });
  }

  onPinPadDisConnected() {
    if (kDebugMode) {
      print("Flutter ==== onPinPad DisConnected");
    }

    setState(() {
      isLoading = false;
      transactionStatus = "PinPad DisConnected";
    });
  }

  onPinPadConfigurationUpdate(value) {
    if (kDebugMode) {
      print("Flutter ==== onPinPad Configuration Update $value");
    }

    setState(() {
      transactionStatus = ((value as String).replaceAll("_", " "));
    });
  }

  onPinPadConnectionStarted() {
    if (kDebugMode) {
      print("Flutter ==== onPinPad Connection Started");
    }

    setState(() {
      transactionStatus = "Connecting to PIN Pad...";
    });
  }

  onTransactionStatusUpdate(value) {
    if (kDebugMode) {
      print("Flutter ==== onPinPad Status Update");
    }

    setState(() {
      transactionStatus = ((value as String).replaceAll("_", " "));
    });
  }

  onPinPadTMSUpdateCompleted() {
    if (kDebugMode) {
      print("Flutter ==== TMS Update Completed");
    }

    setState(() {
      isLoading = false;
      transactionStatus = "TMS Update Completed";
    });
  }

  onPinPadTMSUpdateFailed(value) {
    if (kDebugMode) {
      print("Flutter ==== TMS Update Failed $value");
    }

    setState(() {
      isLoading = false;
      transactionStatus = "TMS Update Failed. $value";
    });
  }

  onTransactionFailed(value) {
    if (kDebugMode) {
      print("Flutter ==== Transaction Failed $value");
    }

    setState(() {
      isLoading = false;
      transactionStatus = "Transaction Failed \n$value";
    });
  }

  onTransactionCompleted(value) {
    if (kDebugMode) {
      print("Flutter ==== Transaction Completed $value");
    }

    var data = jsonDecode(value);

    String strCardValue = data["CARD_HOLDER_LAST_NAME"] ?? "";
    final userRef = data["USER_REFERENCE"] ?? data["UserReference"];
    if (userRef is String && userRef.isNotEmpty) {
      lastUserReference = userRef;
      transactionIdController.text = userRef;
    }
    final txnId = data["transactionid"] ??
        data["transactionId"] ??
        data["TRANSACTION_ID"];
    if (txnId is String && txnId.isNotEmpty) {
      transactionIdController.text = txnId;
    }

    print("Last Name" + strCardValue);

    if (mShowWarningDialog) {
      showWarningDialog(mBuilderContext!);
    }

    setState(() {
      isLoading = false;
      transactionStatus = "Transaction Completed";
    });
  }

  onTransactionTerminated() {
    if (kDebugMode) {
      print("Flutter ==== Transaction Terminated");
    }

    setState(() {
      isLoading = false;
      transactionStatus = "Transaction Terminated";
    });
  }

  onPinPadStatusUpdated(value) {
    setState(() {
      isLoading = false;
      transactionStatus = "PinPad Status Updated";
    });

    if (kDebugMode) {
      print("Flutter ==== PinPad Status Updated $value");
    }

    selectPinPad();
  }

  showWarningDialog(BuildContext context) {
    timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
      _flutterBeepPlusPlugin.playSysSound(AndroidSoundID.TONE_CDMA_ABBR_ALERT);
    });

    Timer timerClose = Timer(const Duration(seconds: 5), () {
      timer!.cancel();
      Navigator.of(context).pop();
    });

    showDialog(
        context: context,
        builder: (_) {
          return Dialog(
            insetPadding: const EdgeInsets.symmetric(horizontal: 16),
            elevation: 0,
            backgroundColor: Colors.white,
            child: StatefulBuilder(
              builder: (BuildContext _, StateSetter _setState) {
                return Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    const SizedBox(
                      height: 30,
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: const [
                        Icon(
                          Icons.warning,
                          color: Colors.yellow,
                          size: 100,
                        ),
                      ],
                    ),
                    Row(
                      children: [
                        Expanded(
                          child: Container(
                            margin: const EdgeInsets.only(
                                top: 20, left: 15, right: 15),
                            child: const Text(
                              "Please don't forget to remove the card",
                              style: TextStyle(fontSize: 20),
                              textAlign: TextAlign.center,
                            ),
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(
                      height: 10,
                    ),
                    GestureDetector(
                      onTap: () {
                        Navigator.pop(context);
                      },
                      child: Container(
                        margin: const EdgeInsets.only(top: 20, bottom: 20),
                        padding: const EdgeInsets.only(
                            top: 5, bottom: 5, right: 20, left: 20),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10),
                          color: Colors.white,
                          border: Border.all(
                            color: Colors.black,
                            width: 1,
                          ),
                          boxShadow: const [
                            BoxShadow(
                              color: Colors.white,
                              spreadRadius: 5,
                              offset: Offset(0, 3),
                            ),
                          ],
                        ),
                        child: const Text("Close"),
                      ),
                    )
                  ],
                );
              },
            ),
          );
        }).then((value) {
      // dispose the timer in case something else has triggered the dismiss.
      timerClose.cancel();
      timer!.cancel();
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter NMI Demo'),
        ),
        body: Builder(
          builder: (context) {
            mBuilderContext = context;
            return SingleChildScrollView(
              child: Column(
                children: [
                  Visibility(
                    visible: isLoading,
                    child: Container(
                      margin: const EdgeInsets.only(top: 40),
                      child: const CircularProgressIndicator(
                        backgroundColor: Colors.grey,
                        color: Colors.purple,
                        strokeWidth: 5,
                      ),
                    ),
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  Text(transactionStatus),
                  const SizedBox(
                    height: 20,
                  ),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20),
                    child: Row(
                      children: [
                        Expanded(
                          child: TextField(
                            controller: amountController,
                            keyboardType: const TextInputType.numberWithOptions(
                                decimal: true),
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                              labelText: 'Sale / Refund amount',
                              hintText: 'e.g. 10',
                            ),
                          ),
                        ),
                        const SizedBox(width: 12),
                        Expanded(
                          child: TextField(
                            controller: tipController,
                            keyboardType: const TextInputType.numberWithOptions(
                                decimal: true),
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                              labelText: 'Sale tip (optional)',
                              hintText: 'e.g. 2.00',
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Pay"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        initialize();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Request TMS Update"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        requestTMSUpdate();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Request Full TMS Update"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        requestFullTMSUpdate();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Select PinPad"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        selectPinPad();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Cancel Transaction"),
                      color: Colors.amber,
                    ),
                    onTap: () {
                      cancelTransaction();
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  const Padding(
                    padding: EdgeInsets.symmetric(horizontal: 20),
                    child: Align(
                      alignment: Alignment.centerLeft,
                      child: Text(
                        'Auth / Capture',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 12),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20),
                    child: Row(
                      children: [
                        Expanded(
                          child: TextField(
                            controller: preAuthAmountController,
                            keyboardType: const TextInputType.numberWithOptions(
                                decimal: true),
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                              labelText: 'Pre-auth amount',
                              hintText: 'e.g. 10.00',
                            ),
                          ),
                        ),
                        const SizedBox(width: 12),
                        Expanded(
                          child: TextField(
                            controller: confirmAmountController,
                            keyboardType: const TextInputType.numberWithOptions(
                                decimal: true),
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                              labelText: 'Confirm amount',
                              hintText: 'e.g. 12.00',
                            ),
                          ),
                        ),
                        const SizedBox(width: 12),
                        Expanded(
                          child: TextField(
                            controller: confirmTipController,
                            keyboardType: const TextInputType.numberWithOptions(
                                decimal: true),
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                              labelText: 'Confirm tip',
                              hintText: 'e.g. 2.00',
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(height: 12),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20),
                    child: TextField(
                      controller: transactionIdController,
                      decoration: InputDecoration(
                        border: const OutlineInputBorder(),
                        labelText: 'UserReference (from pre-auth)',
                        hintText: lastUserReference ?? 'Filled after pre-auth',
                      ),
                    ),
                  ),
                  if (lastUserReference != null &&
                      lastUserReference!.isNotEmpty)
                    Padding(
                      padding: const EdgeInsets.fromLTRB(20, 8, 20, 0),
                      child: Align(
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Last ref: $lastUserReference',
                          style: const TextStyle(fontSize: 12),
                        ),
                      ),
                    ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Pre-auth"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        requestPreAuth();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Confirm (final amount)"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        requestConfirm();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Void"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        requestVoid();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 40,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Get PinPad Status"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        getPinPadStatus();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 40,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Disconnect PinPad"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        disconnectPinPad();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 40,
                  ),
                  const Padding(
                    padding: EdgeInsets.symmetric(horizontal: 20),
                    child: Align(
                      alignment: Alignment.centerLeft,
                      child: Text(
                        'Refund',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 12),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20),
                    child: Text(
                      'Uses Sale / Refund amount above and UserReference field '
                      '(or enter a transaction id for refund).',
                      style: TextStyle(fontSize: 12, color: Colors.grey[700]),
                    ),
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Request Refund"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        if (amountController.text.trim().isNotEmpty &&
                            transactionIdController.text.trim().isNotEmpty) {
                          requestRefund();
                        } else {
                          setState(() {
                            transactionStatus =
                                "Enter refund amount and transaction id / UserReference";
                          });
                        }
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Manual"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      manualCreditPayment();
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Manual Pre-Auth"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false) {
                        manualKeyedPreAuth();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Manual Capture"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false &&
                          transactionIdController.text.trim().isNotEmpty) {
                        manualKeyedCapture();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.all(20),
                      child: const Text("Manual Void Auth"),
                      color: (isLoading == true) ? Colors.grey : Colors.amber,
                    ),
                    onTap: () {
                      if (isLoading == false &&
                          transactionIdController.text.trim().isNotEmpty) {
                        manualKeyedVoid();
                      }
                    },
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  GestureDetector(
                    child: const Text("Open Dialog"),
                    onTap: () {
                      showWarningDialog(context);
                    },
                  ),
                  /*const SizedBox(
                  height: 40,
                ),
                GestureDetector(
                  child: Container(
                    padding: const EdgeInsets.all(20),
                    child: const Text("Open signature page"),
                    color: (isLoading == true) ? Colors.grey : Colors.amber,
                  ),
                  onTap: () {
                    goToSignaturePad();
                  },
                ),*/
                ],
              ),
            );
          },
        ),
      ),
    );
  }

  String getRandom4Chars() {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    final random = Random();

    return List.generate(4, (index) => chars[random.nextInt(chars.length)])
        .join();
  }

/*goToSignaturePad() {
    nmi = FlutterNmi.instance();
    nmi.goToSignaturePad();
    print("Flutter ==== Open SignaturePad");
  }*/
}
3
likes
150
points
729
downloads

Documentation

API reference

Publisher

verified publisherpatel-apps.com

Weekly Downloads

NMI Payment Integration.

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on flutter_nmi

Packages that implement flutter_nmi