SDK Setup

Requirements, installation, environments and the credential provider for the Flutter SDK.

Before You Start

Read the following guides before proceeding:

GuideWhy
Flutter SDKSurfaces, isolation model and flows
AuthenticationThe user token accessToken() returns
Api BasicsX-Chain-Id and the identity headers the SDK sends

Requirements

RequirementValue
Flutter / DartFlutter 3.19 or later, Dart 3.3 or later
iOS15 or later, Xcode 26.5 or later
Android6.0 (API 23) or later, mavenCentral() among the repositories

Install

flutter pub add wirex_mobile_sdk
# pubspec.yaml
dependencies:
  wirex_mobile_sdk: ^0.3.15

The package installs the native SDKs at its own version: the WirexMobileSDK pod from CocoaPods trunk and
com.wirexapp.baas:mobile-sdk from Maven Central. Flutter templates already list mavenCentral(). The
Android artifact merges the <queries> block Google Pay needs on Android 11 and later into your manifest —
no manual manifest change is required.

One import covers every surface:

import 'package:wirex_mobile_sdk/wirex_mobile_sdk.dart';

Integration Values

ValueWhat it isWhere it goes
testModefalse — production, true — the Wirex test environment. Used by the card surfacesWirexReveal.instance.configure, WirexCollect.configure, WirexThreeDS.instance.configure
Environment nameThe same choice by name: 'production' or 'test', resolved with WirexWalletEnvironment.resolve(name). Used by the walletWirexWalletConfig(environment: …)
chainIdThe chain your integration operates on, sent as X-Chain-Id on every authenticated call. Required for Reveal and Collect — an empty value fails configure with invalid_environmentchainId: in configure(...)
clientIdOptional, wallet only. Selects your branding on the Wirex-hosted checkout pages. Omit it unless Wirex assigned one. It is not a credentialWirexWalletConfig(clientId: …)

The native SDK holds the environment hosts. No API takes a URL. An unknown environment name is an error —
the SDK never falls back to another environment.


Credential Provider

The SDK never issues credentials. You extend WirexCredentialProvider; the SDK calls it when it needs a
token, so tokens are never pushed in advance.

import 'package:wirex_mobile_sdk/wirex_mobile_sdk.dart';

class AppCredentials extends WirexCredentialProvider {
  @override
  Future<String> accessToken() async => fetchAccessToken();

  @override
  Future<String> actionToken(String scope) async {
    final bool approved = await promptStepUp(scope);
    if (!approved) throw const WirexDeclined();
    return fetchActionToken(scope);
  }

  @override
  String? get userEmail => null;

  @override
  String? get userWallet => '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18';
}
MemberReturnsUsed by
accessToken()The user token from User Token Issuance (Login as User), sent as Authorization: BearerReveal
actionToken(scope)An action_token for scope: 'cardData' (PAN, expiry, CVV) or 'pin'Reveal
userEmailSent as X-User-EmailReveal
userWalletSent as X-User-WalletReveal
  • Exactly one identity. Return userEmail or userWallet, never both. Both set is a 400 from the API.
  • The SDK sends the bearer as-is and never retries a 401. Check the token's expiry inside
    accessToken() and refresh before returning. A stale token surfaces as unauthorized.
  • Both action scopes are answered with a GetCardDetails action token. Mint it through
    POST /api/v1/confirmation/signature/verify or the SMS OTP flow in
    Card Details. The token is valid for 5 minutes.
  • Throw const WirexDeclined() from actionToken when the user backs out. The SDK stops the operation and
    reports it as declined, not as an error.

Collect Credentials

Collect needs no action token and takes no provider object. WirexCollect.configure takes an accessToken
callback plus the identity:

Future<void> configureCollect() => WirexCollect.configure(
      chainId: '<chain id>',
      accessToken: () async => fetchAccessToken(),
      userWallet: '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18',
      testMode: true,
    );

Configure Once Per Process

  • Cache the configure future and clear the cache on failure, so a later attempt retries.
  • Derive testMode from your runtime environment when your app switches backends at runtime.
  • Identity is read at configure time. Re-run configure when the signed-in user changes. A stale or
    absent identity header is a 401, not a soft failure.

Screen Protection

Revealed values and the card form are drawn in a capture-proof layer: FLAG_SECURE on Android, a secure
layer on iOS. The iOS Simulator renders that layer blank.

Important: dangerouslyDisableScreenProtection: true on configure(...) — and on
WirexThreeDS.instance.configure(...) for the challenge — turns this off for simulator development and
automated UI tests. It defeats PCI screen-capture protection. Never ship it enabled.


Quickstart

One credential provider, both card surfaces configured once, a top-up card collected and an issued card
revealed. Each surface is covered in detail on its own page.

import 'package:flutter/widgets.dart';
import 'package:wirex_mobile_sdk/wirex_mobile_sdk.dart';

abstract class Auth {
  Future<String> freshAccessToken();
  Future<bool> confirmStepUp();
  Future<String> mintActionToken();   // GetCardDetails, via POST /api/v1/confirmation/signature/verify
  String get walletAddress;
}

abstract class Backend {
  Future<void> topUp(String externalCardId);   // estimate + execute with external_card_id
}

class AppCredentials extends WirexCredentialProvider {
  AppCredentials(this._auth);
  final Auth _auth;

  @override
  Future<String> accessToken() => _auth.freshAccessToken();

  @override
  Future<String> actionToken(String scope) async {
    if (!await _auth.confirmStepUp()) throw const WirexDeclined();
    return _auth.mintActionToken();
  }

  @override
  String? get userEmail => null;
  @override
  String? get userWallet => _auth.walletAddress;
}

Future<void> configureSdk(Auth auth) async {
  final credentials = AppCredentials(auth);
  await WirexReveal.instance.configure(chainId: '<chain id>', credentials: credentials, testMode: true);
  await WirexCollect.configure(
    chainId: '<chain id>',
    accessToken: auth.freshAccessToken,
    userWallet: auth.walletAddress,
    testMode: true,
  );
}

class AddCard extends StatefulWidget {
  const AddCard({required this.backend, super.key});
  final Backend backend;
  @override
  State<AddCard> createState() => _AddCardState();
}

class _AddCardState extends State<AddCard> {
  WirexCollectFormController? _controller;
  WirexCollectState _state = const WirexCollectState.empty();

  Future<void> submit() async {
    final controller = _controller;
    if (controller == null || !_state.isComplete) return;
    final token = await controller.tokenizeTopUp(const WirexTopUpTokenizeParams(
      billingDetails: WirexBillingDetails(line1: '1 High Street', zipCode: 'N1 9GU', city: 'London', country: 'GB'),
    ));
    await widget.backend.topUp(token.cardId);
  }

  @override
  Widget build(BuildContext context) => WirexCollectFormView(
        profile: WirexCollectProfile.topup,
        onCreated: (c) => _controller = c,
        onChange: (s) => setState(() => _state = s),
      );
}

Future<WirexRevealSession> revealPan(String cardId) async {
  final session = await WirexReveal.instance.createSession(
    cardId: cardId,
    fields: const <WirexRevealField>[WirexRevealField.pan],
    cardholderName: 'ALEX GREY',
  );
  await session.load();
  return session;   // render WirexRevealFieldView(session: session, field: WirexRevealField.pan)
}

Did this page help you?