Reveal Card Details
Show a cardholder their own PAN, expiry, CVV and PIN in sealed native rows that never pass values to JavaScript.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| React Native 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 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 |
|---|---|
pan, expiry | POST /api/v1/cards/{cardId}/details |
cvv | POST /api/v1/cards/{cardId}/cvv |
pin | POST /api/v1/cards/{cardId}/pin |
cardholderName | None — drawn from the cardholderName you pass |
Before the calls, the SDK asks your credential provider for an action token (getActionToken('cardData') or
getActionToken('pin')). Return a GetCardDetails token — see
Credential Provider.
Virtual cards have no PIN.
POST /api/v1/cards/{cardId}/pinrejects a virtual retail card. Omit
'pin'fromfieldsfor a virtual card.
Step 1: Create a Session
import { WirexReveal, type WirexRevealSession } from '@wirexapp/mobile-sdk';
export async function openReveal(cardId: string): Promise<WirexRevealSession> {
await WirexReveal.configure({ testMode: true, chainId: '<chain id>', credentials });
return WirexReveal.createSession({
cardId,
fields: ['pan', 'expiry', 'cvv', 'pin'],
cardholderName: 'ALEX GREY',
});
}| Parameter | Type | Description |
|---|---|---|
cardId | string | The issued card's id |
fields | 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 and Load
import * as React from 'react';
import { useEffect, useState } from 'react';
import {
WirexRevealFieldView,
type WirexRevealField, type WirexRevealSession, type WirexRevealSnapshot,
} from '@wirexapp/mobile-sdk';
export function RevealRow({ session, field }: { session: WirexRevealSession; field: WirexRevealField }) {
const [snapshot, setSnapshot] = useState<WirexRevealSnapshot>(session.snapshot);
const [height, setHeight] = useState<number | undefined>();
useEffect(() => {
const unsubscribe = session.onSnapshot(setSnapshot);
void session.load();
return () => { unsubscribe(); void session.destroy(); };
}, [session]);
return (
<WirexRevealFieldView
session={session}
field={field}
strings={{ fieldLabels: { pan: 'Card number' }, copy: 'Copy', copied: 'Copied' }}
onNaturalHeight={setHeight}
style={{ height }}
/>
);
}Trap: a native row cannot be sized from JavaScript. It reports its content height through
onNaturalHeight; apply that height to the row'sstyle. A box shorter than the content clips the value.
Session State
session.snapshot and onSnapshot report the session and each row:
| Property | Values |
|---|---|
snapshot.state | 'idle', 'loading', 'ready', 'error' |
snapshot.fields[i].outcome | 'idle', 'loading', 'loaded', 'failed' |
snapshot.loaded / snapshot.failed | Arrays of fields in that outcome |
snapshot.error, snapshot.fields[i].error | { 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 rejects for a request failure — failures land in the snapshot |
session.refresh() | Reloads |
session.clear() | Wipes the values |
session.destroy() | Releases the native session |
WirexReveal.destroyAll() | Tears down every session |
Masking
pan, cvv and pin start masked.
| Prop | Description |
|---|---|
masked | Controls masking. Pass false to unmask immediately when the user has just confirmed the step-up |
onMaskedChange | Reports { masked, revealRequested, screenshotProtectionActive } |
masksWhenDetached | Default true — a row re-masks when detached |
onCopyResult | (copied: boolean) — fires when the user copies a copyable field. There is no JavaScript-triggered copy |
A row masked because the app left the foreground draws again on return while the session still holds the
value. The session holds values for 120 seconds; past that, 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:
import type { WirexActionScope, WirexCredentialProvider } from '@wirexapp/mobile-sdk';
declare const auth: {
getAccessToken(): Promise<string>;
signAction(scope: WirexActionScope): Promise<string>;
userWallet(): string | undefined;
};
let offered: { scope: WirexActionScope; token: string } | undefined;
export const offerStepUpToken = (scope: WirexActionScope, token: string): void => { offered = { scope, token }; };
export const revokeOfferedStepUpToken = (): void => { offered = undefined; };
export const credentials: WirexCredentialProvider = {
getAccessToken: () => auth.getAccessToken(),
async getActionToken(scope) {
if (offered?.scope === scope) { const { token } = offered; offered = undefined; return token; }
return auth.signAction(scope);
},
get userEmail() { return undefined; },
get userWallet() { return auth.userWallet(); },
};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 height smaller than onNaturalHeight | Apply the reported height |
unauthorized in the snapshot | Stale bearer — the SDK never retries a 401 | Refresh inside getAccessToken |
| Operation reported as declined | getActionToken threw WirexDeclined | None — the user backed out |
pin field failed | Virtual card, or card not Active | Omit 'pin' for virtual cards |
Full error handling: Errors, Logging and Troubleshooting.
Updated about 3 hours ago

