rootfi_tap_to_pay

Official RootFi Flutter SDK for NFC tap-to-pay — instant phone-to-phone payments between two RootFi accounts — plus a typed client for the RootFi Cards API.

The NFC handshake reads the payer/payee RootFi account numbers; settlement runs RootFi → RootFi automatically. You never touch bank codes, name-enquiry, or any settlement endpoint.

Install

dependencies:
  rootfi_tap_to_pay: ^0.1.1

Android — nothing to configure

This package ships its own Android manifest. The NFC permission and NFC/HCE feature flags are merged into your app automatically — you do not edit your AndroidManifest.xml. Min SDK 21.

Receiving (HCE broadcast) is Android only. Scanning/paying works on Android and iOS.

Authentication

Use your RootFi API key — the same key that authorises banking and cards. A sandbox key (rf_test_…) targets sandbox; a live key (rf_live_…) targets production. One key, everything.

final tap = RootFiTapToPay(apiKey: 'rf_live_xxx');

Tap-to-pay (event-driven)

Wire up the callbacks you care about, then start… / stop.

final tap = RootFiTapToPay(apiKey: 'rf_live_xxx')
  ..onStatus   = (s) => setState(() => _status = s)  // TapStatus enum
  ..onScanning = ()  => print('reading the other phone…')
  ..onSuccess  = (r) => print('Paid ${r.recipientName} — ${r.transferId}')
  ..onFailure  = (e) => print('Failed: $e');

Payee (receiving) — broadcast your RootFi account:

await tap.startReceiving(accountNumber: myAccountNumber);
// …show "waiting for payment" UI…
await tap.stop();

Payer (sending) — scan the payee's phone and settle:

await tap.startPaying(
  debitAccountNumber: myAccountNumber,
  amount: 1500,
  narration: 'Lunch',
);
// onSuccess / onFailure fire when done. Cancel anytime with:
await tap.stop();

Prefer await? Use scanAndPay(...) which returns a TapPaymentResult.

NFC capability checks

if (!await tap.isNfcSupported()) { /* hide the feature */ }
else if (!await tap.isNfcEnabled()) { await tap.openNfcSettings(); }

TapStatus lifecycle

idle → checkingNfc → (nfcUnsupported | nfcDisabled) | waitingForTap → scanning → settling → (success | failure); stop() → stopped.

Cards API

The same client wraps the RootFi Cards API (processor handled by RootFi):

final customer = await tap.client.call('POST', '/v1/cards/customers', body: {
  'type': 'individual',
  'name': 'Jane Doe',
  'phoneNumber': '+2348100000000',
  'status': 'active',
  'billingAddress': {'line1': '1 Marina', 'city': 'Lagos', 'country': 'NG'},
});

final card = await tap.client.call('POST', '/v1/cards', body: {
  'customerId': customer['data']['_id'],
  'type': 'virtual',
  'currency': 'NGN',
  'status': 'active',
});

// Render PAN/CVV/PIN securely — token never exposes data to your backend:
final token = await tap.client.call(
  'GET', '/v1/cards/${card['data']['_id']}/token');

Error handling

  • onFailure / RootFiApiException — auth (401/403) or transport failures.
  • onFailure / NfcUnavailableException — NFC off / unsupported / nothing read.
  • onFailure / TimeoutException — no device tapped in time.

A declined-but-delivered settlement is reported through onFailure with a RootFiApiException carrying the decline message (not a thrown crash).

License

MIT © RootFi

Libraries

rootfi_tap_to_pay
RootFi tap-to-pay SDK.