sure_pay_plugin 0.0.1 copy "sure_pay_plugin: ^0.0.1" to clipboard
sure_pay_plugin: ^0.0.1 copied to clipboard

PlatformAndroid

A Flutter plugin for integrating SurePay POS payment terminals over TCP/IP. Supports purchases, reversals, reconciliation, authorization, and refunds via a clean typed Dart API.

example/lib/main.dart

// example/lib/main.dart
//
// Full demo application for the sure_pay_plugin.
// Shows every supported terminal operation with a live log output.

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

void main() => runApp(const SurePayDemoApp());

// ─── App ──────────────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SurePay POS Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.teal, useMaterial3: true),
      home: const TerminalDemoPage(),
    );
  }
}

// ─── Page ─────────────────────────────────────────────────────────────────────

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

  @override
  State<TerminalDemoPage> createState() => _TerminalDemoPageState();
}

class _TerminalDemoPageState extends State<TerminalDemoPage> {
  // Connection fields
  final _ipCtrl = TextEditingController(text: '192.168.1.100');
  final _portCtrl = TextEditingController(text: '7070');

  // Log entries (newest first)
  final List<_LogEntry> _log = [];
  bool _busy = false;

  // ─── Repository factory ────────────────────────────────────────────────────

  SurePayRepository get _repo => SurePayRepository(
    ip: _ipCtrl.text.trim(),
    port: int.tryParse(_portCtrl.text.trim()) ?? 7070,
  );

  // ─── Logging ───────────────────────────────────────────────────────────────

  void _log$(String msg, {bool error = false}) {
    setState(() => _log.insert(0, _LogEntry(msg, error: error)));
  }

  // ─── Runner ────────────────────────────────────────────────────────────────

  Future<void> _run(String label, Future<void> Function() action) async {
    if (_busy) return;
    setState(() => _busy = true);
    _log$('▶ $label');
    try {
      await action();
    } on SurePayTerminalException catch (e) {
      _log$('❌ Terminal: ${e.code}', error: true);
    } on SurePayConnectionException catch (e) {
      _log$('🔌 Connection: ${e.code}', error: true);
    } on SurePayArgumentException catch (e) {
      _log$('⚠️  Args: ${e.message}', error: true);
    } catch (e) {
      _log$('💥 $e', error: true);
    } finally {
      setState(() => _busy = false);
    }
  }

  // ─── Formatters ────────────────────────────────────────────────────────────

  void _printTx(SurePayTransaction tx) {
    if (tx.isApproved) {
      _log$(
        '✅ APPROVED | ${tx.schemaName} ${tx.cardNumber} '
        '| auth=${tx.authorizeCode} | rrn=${tx.rrn} | ₺${tx.amount}',
      );
    } else {
      _log$('❌ ${tx.flag.name.toUpperCase()} | resp=${tx.responseCode}', error: true);
    }
  }

  // ─── Terminal operations ───────────────────────────────────────────────────

  void _getSdkVersion() => _run('getSdkVersion', () async {
    final v = await _repo.getSdkVersion();
    _log$('✅ SDK version: $v');
  });

  void _checkReady() => _run('checkTerminalReady', () async {
    final info = await _repo.checkTerminalReady();
    _log$('✅ Ready | model=${info.modelName} | id=${info.terminalId}');
  });

  void _getTerminalInfo() => _run('getTerminalInfo', () async {
    final info = await _repo.getTerminalInfo();
    _log$('✅ $info');
  });

  void _purchase() => _run('purchase SAR 10.25', () async {
    final tx = await _repo.purchase(amount: '10.25', reference: 'ORDER-001');
    _printTx(tx);
  });

  void _reverse() => _run('reverse', () async {
    final tx = await _repo.reverse();
    _printTx(tx);
  });

  void _getLastTrx() => _run('getLastTransaction', () async {
    final tx = await _repo.getLastTransaction();
    _printTx(tx);
  });

  void _reconciliation() => _run('reconciliation', () async {
    final r = await _repo.reconciliation();
    _log$('✅ Reconciliation: ${r.message ?? (r.isInBalance ? "In Balance" : "Out Of Balance")}');
  });

  void _authorization() => _run('authorization SAR 50.00', () async {
    final tx = await _repo.authorization(amount: '50.00');
    _printTx(tx);
  });

  void _authVoid() => _run('authorizationVoid', () async {
    final tx = await _repo.authorizationVoid(
      amount: '50.00',
      rrn: '123456789123',
      dateTime: '12102022',
      authCode: '123456',
    );
    _printTx(tx);
  });

  void _authExtension() => _run('authorizationExtension', () async {
    final tx = await _repo.authorizationExtension(rrn: '123456789123', dateTime: '12102022', authCode: '123456');
    _printTx(tx);
  });

  void _advicePartial() => _run('purchaseAdvicePartial', () async {
    final tx = await _repo.purchaseAdvicePartial(
      amount: '30.00',
      rrn: '123456789123',
      dateTime: '12102022',
      authCode: '123456',
    );
    _printTx(tx);
  });

  void _adviceFull() => _run('purchaseAdviceFull', () async {
    final tx = await _repo.purchaseAdviceFull(
      amount: '50.00',
      rrn: '123456789123',
      dateTime: '12102022',
      authCode: '123456',
    );
    _printTx(tx);
  });

  void _refundWithPwd() => _run('refundWithPassword SAR 5.00', () async {
    final tx = await _repo.refundWithPassword(amount: '5.00', rrn: '123456789123', date: '20221107', password: '14789');
    _printTx(tx);
  });

  void _refundClientRef() => _run('refundClientRefWithPassword', () async {
    final tx = await _repo.refundClientRefWithPassword(
      amount: '5.00',
      clientRef: 'SUREPAY-000123456789',
      rrn: '123456789123',
      date: '20221107',
      password: '14789',
    );
    _printTx(tx);
  });

  void _cancel() => _run('cancel', () async {
    await _repo.cancel();
    _log$('✅ Cancel request sent');
  });

  // ─── Build ─────────────────────────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('SurePay POS Plugin Demo'), centerTitle: true),
      body: Column(
        children: [
          // ── Connection inputs ──────────────────────────────────────────────
          _ConnectionBar(ipCtrl: _ipCtrl, portCtrl: _portCtrl, busy: _busy),

          const Divider(height: 1),

          // ── Operation buttons ──────────────────────────────────────────────
          Expanded(
            flex: 3,
            child: SingleChildScrollView(
              padding: const EdgeInsets.all(12),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  _section('Terminal'),
                  _buttonRow([
                    _btn('SDK Version', _getSdkVersion),
                    _btn('Check Ready', _checkReady),
                    _btn('Terminal Info', _getTerminalInfo),
                  ]),
                  _section('Purchase & History'),
                  _buttonRow([
                    _btn('Purchase', _purchase, color: Colors.teal),
                    _btn('Reverse', _reverse, color: Colors.orange),
                    _btn('Last Trx', _getLastTrx),
                    _btn('Reconcile', _reconciliation),
                  ]),
                  _section('Authorization'),
                  _buttonRow([
                    _btn('Auth', _authorization),
                    _btn('Auth Void', _authVoid),
                    _btn('Auth Extend', _authExtension),
                    _btn('Advice Part.', _advicePartial),
                    _btn('Advice Full', _adviceFull),
                  ]),
                  _section('Refund'),
                  _buttonRow([_btn('Refund + Pwd', _refundWithPwd), _btn('Refund ClientRef', _refundClientRef)]),
                  _section('Control'),
                  _buttonRow([_btn('Cancel', _cancel, color: Colors.red)]),
                ],
              ),
            ),
          ),

          const Divider(height: 1),

          // ── Log output ─────────────────────────────────────────────────────
          Expanded(
            flex: 2,
            child: _LogView(entries: _log, busy: _busy),
          ),
        ],
      ),
    );
  }

  // ─── UI helpers ────────────────────────────────────────────────────────────

  Widget _section(String title) => Padding(
    padding: const EdgeInsets.only(top: 8, bottom: 4),
    child: Text(
      title,
      style: const TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: Colors.grey),
    ),
  );

  Widget _buttonRow(List<Widget> children) => Wrap(spacing: 6, runSpacing: 6, children: children);

  Widget _btn(String label, VoidCallback onTap, {Color? color}) => ElevatedButton(
    onPressed: _busy ? null : onTap,
    style: ElevatedButton.styleFrom(
      backgroundColor: color,
      foregroundColor: color != null ? Colors.white : null,
      padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
      textStyle: const TextStyle(fontSize: 12),
    ),
    child: Text(label),
  );
}

// ─── Connection bar ───────────────────────────────────────────────────────────

class _ConnectionBar extends StatelessWidget {
  final TextEditingController ipCtrl;
  final TextEditingController portCtrl;
  final bool busy;

  const _ConnectionBar({required this.ipCtrl, required this.portCtrl, required this.busy});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: ipCtrl,
              enabled: !busy,
              decoration: const InputDecoration(
                labelText: 'Terminal IP',
                hintText: '192.168.1.100',
                border: OutlineInputBorder(),
                isDense: true,
                contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
              ),
            ),
          ),
          const SizedBox(width: 8),
          SizedBox(
            width: 80,
            child: TextField(
              controller: portCtrl,
              enabled: !busy,
              keyboardType: TextInputType.number,
              decoration: const InputDecoration(
                labelText: 'Port',
                hintText: '7070',
                border: OutlineInputBorder(),
                isDense: true,
                contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// ─── Log view ─────────────────────────────────────────────────────────────────

class _LogEntry {
  final String text;
  final bool error;
  final DateTime time;
  _LogEntry(this.text, {this.error = false}) : time = DateTime.now();
}

class _LogView extends StatelessWidget {
  final List<_LogEntry> entries;
  final bool busy;

  const _LogView({required this.entries, required this.busy});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: const Color(0xFF1A1A2E),
      child: Column(
        children: [
          if (busy) const LinearProgressIndicator(minHeight: 2) else const SizedBox(height: 2),
          Expanded(
            child: entries.isEmpty
                ? const Center(
                    child: Text('Tap a button to start', style: TextStyle(color: Colors.grey, fontSize: 13)),
                  )
                : ListView.builder(
                    padding: const EdgeInsets.all(8),
                    itemCount: entries.length,
                    itemBuilder: (_, i) {
                      final e = entries[i];
                      final hh = e.time.hour.toString().padLeft(2, '0');
                      final mm = e.time.minute.toString().padLeft(2, '0');
                      final ss = e.time.second.toString().padLeft(2, '0');
                      return Padding(
                        padding: const EdgeInsets.symmetric(vertical: 1),
                        child: Row(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              '$hh:$mm:$ss ',
                              style: const TextStyle(color: Colors.grey, fontSize: 10, fontFamily: 'monospace'),
                            ),
                            Expanded(
                              child: Text(
                                e.text,
                                style: TextStyle(
                                  color: e.error ? Colors.redAccent : Colors.greenAccent,
                                  fontSize: 11,
                                  fontFamily: 'monospace',
                                ),
                              ),
                            ),
                          ],
                        ),
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }
}
1
likes
160
points
10
downloads

Documentation

API reference

Publisher

verified publisherelsakr.site

Weekly Downloads

A Flutter plugin for integrating SurePay POS payment terminals over TCP/IP. Supports purchases, reversals, reconciliation, authorization, and refunds via a clean typed Dart API.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on sure_pay_plugin

Packages that implement sure_pay_plugin