Reveal Card Details
Show a cardholder their own PAN, expiry, CVV and PIN in sealed native rows that never pass values to Dart.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Flutter SDK | Surfaces, isolation model and flows |
| SDK Setup | Configure, credential provider, action token scopes |
| Card Details | Confirmation methods and action-token rules |
| Card Issuance | How the card id is produced |
Overview
WirexReveal.instance opens a session for one card; each WirexRevealFieldView is a native row the SDK draws
the value into. The session calls the same endpoints documented in Card Details:
| Field | Endpoint the SDK calls |
|---|---|
WirexRevealField.pan, expiry | POST /api/v1/cards/{cardId}/details |
WirexRevealField.cvv | POST /api/v1/cards/{cardId}/cvv |
WirexRevealField.pin | POST /api/v1/cards/{cardId}/pin |
WirexRevealField.cardholderName | None — drawn from the cardholderName you pass |
Before the calls, the SDK asks your credential provider for an action token (actionToken('cardData') or
actionToken('pin')). Return a GetCardDetails token — see
Credential Provider.
Virtual cards have no PIN.
POST /api/v1/cards/{cardId}/pinrejects a virtual retail card. Omit
WirexRevealField.pinfromfieldsfor a virtual card.
Step 1: Create a Session and Load
import 'package:flutter/widgets.dart';
import 'package:wirex_mobile_sdk/wirex_mobile_sdk.dart';
Future<WirexRevealSession> openReveal(WirexCredentialProvider credentials, String cardId) async {
await WirexReveal.instance.configure(chainId: '<chain id>', credentials: credentials, testMode: true);
final WirexRevealSession session = await WirexReveal.instance.createSession(
cardId: cardId,
fields: const <WirexRevealField>[
WirexRevealField.pan, WirexRevealField.expiry, WirexRevealField.cvv, WirexRevealField.pin,
],
cardholderName: 'ALEX GREY',
);
session.onSnapshot((WirexRevealSnapshot snapshot) {
if (snapshot.state == WirexRevealState.error) debugPrint('${snapshot.error?.code}');
});
await session.load();
return session;
}| Parameter | Type | Description |
|---|---|---|
cardId | String | The issued card's id |
fields | List<WirexRevealField> | Rows to load: cardholderName, pan, expiry, cvv, pin. Drives which endpoints are called |
cardholderName | String | Drawn as-is in the cardholderName row |
One session per open details view. The session holds the retrieved values natively; destroy it when the view
closes.
Step 2: Render the Rows
class RevealRow extends StatelessWidget {
const RevealRow({required this.session, required this.field, super.key});
final WirexRevealSession session;
final WirexRevealField field;
@override
Widget build(BuildContext context) => WirexRevealFieldView(
session: session,
field: field,
strings: const WirexRevealStrings(
fieldLabels: <WirexRevealField, String>{WirexRevealField.pan: 'Card number'},
copy: 'Copy',
copied: 'Copied',
),
onMaskedChange: (bool masked, bool screenshotProtectionActive) {},
);
}The row sizes itself from the native content. Give it a width and let it take its natural height — a box
shorter than the content clips the value.
Session State
| Property | Values |
|---|---|
snapshot.state | WirexRevealState.idle, loading, ready, error |
snapshot.outcomeFor(field) | WirexRevealFieldOutcome.idle, loading, loaded, failed |
snapshot.loaded / snapshot.failed | Sets of fields in that outcome |
snapshot.error | WirexRevealError { code, retryable } |
One failed field does not hide the others. The row shows only that it failed; log the failed fields with the
error code, because the row never says why.
| Method | Effect |
|---|---|
session.load() | Loads the requested fields. Idempotent. Resolves with the snapshot and never throws for a request failure — failures land in the snapshot |
session.refresh() | Reloads |
session.clear() | Wipes the values |
session.destroy() | Releases the native session |
WirexReveal.instance.destroyAll() | Tears down every session |
Values are wiped two minutes after the first load.
Masking
pan, cvv and pin start masked.
| Parameter | Description |
|---|---|
masked | Controls masking. Pass false to unmask immediately when the user has just confirmed the step-up |
onMaskedChange | (bool masked, bool screenshotProtectionActive) |
masksWhenDetached | Default true — a row re-masks when detached |
onCopyResult | (bool copied) — fires when the user copies a copyable field |
A row masked because the app left the foreground draws again on return while the session still holds the
value. Past the session's 120-second lifetime the row stays masked until load() runs again.
Sign Once
The user confirms a reveal before the details view opens. Mint the action token at that point, hand it to the
provider one-shot, and revoke it if the flow is abandoned — otherwise the SDK asks for a second confirmation
for the same action:
class ProductionCredentials extends WirexCredentialProvider {
ProductionCredentials(this._auth);
final AuthSession _auth;
({String scope, String token})? _offered;
void offerStepUpToken(String scope, String token) => _offered = (scope: scope, token: token);
void revokeOfferedStepUpToken() => _offered = null;
@override
Future<String> accessToken() => _auth.freshAccessToken();
@override
Future<String> actionToken(String scope) async {
final offered = _offered;
if (offered != null && offered.scope == scope) {
_offered = null;
return offered.token;
}
return _auth.signAction(scope);
}
@override
String? get userEmail => null;
@override
String? get userWallet => _auth.walletAddress;
}Errors
| Symptom | Cause | Resolution |
|---|---|---|
| Row blank on iOS Simulator | Secure layer renders blank on the Simulator | Test on a device, or dangerouslyDisableScreenProtection in development only |
| Row blank on device | Row box shorter than its content | Let the row take its natural height |
unauthorized in the snapshot | Stale bearer — the SDK never retries a 401 | Refresh inside accessToken() |
| Operation reported as declined | actionToken threw WirexDeclined | None — the user backed out |
pin field failed | Virtual card, or card not Active | Omit WirexRevealField.pin for virtual cards |
Full error handling: Errors, Logging and Troubleshooting.
Updated about 3 hours ago

