Reveal Card Details

Display an issued card's PAN, expiry, CVV and PIN inside Wirex-served iframes without the values reaching your page.

Before You Start

Read the following guides before proceeding:

GuideWhy
PCI Compliant SDKIsolation model and package map
SDK SetupClient id, environments, installation, CSP
Card DetailsConfirmation methods and action-token rules
Card IssuanceHow the card id is produced

Overview

@wirexapp/card-reveal-web displays an issued card's sensitive details — full PAN, expiry, CVV and PIN —
inside Wirex-served iframes. The frame fetches the values directly from the Wirex API and renders them in
place. Copy-to-clipboard runs inside the frame. Your page mounts the element and receives status events; it
never receives the values.

The element calls the same endpoints documented in Card Details:

Requested rowEndpoint the frame calls
number, expiryPOST /api/v1/cards/{cardId}/details
cvvPOST /api/v1/cards/{cardId}/cvv
pinPOST /api/v1/cards/{cardId}/pin
cardholderNone — rendered from the cardholderName you pass

Prerequisite: Mint an Action Token

Reveal requires a one-shot action_token on top of the user token. Mint it through the confirmation flow —
authenticated with the same user token — immediately before mounting the element.

const nonce = Math.floor(Date.now() / 1000);
const message = `By signing this I confirm that I am executing action GetCardDetails at ${nonce}`;
const messageSignature = await wallet.signMessage(message);

const { action_token: actionToken } = await fetch(
  `${baseUrl}/api/v1/confirmation/signature/verify`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'X-User-Wallet': userEoaAddress,
      'X-Chain-Id': chainId,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ action_type: 'GetCardDetails', message_signature: messageSignature, nonce })
  }
).then(response => response.json());
PropertyValue
Validity5 minutes
ScopeGetCardDetails — one token covers the PAN, CVV and PIN endpoints of one reveal
ReuseNone. Re-opening the details view requires a fresh token

SMS OTP confirmation produces the same token under the field name token. Both methods are documented in
Card Details.


Mount the Element

import { createCardReveal } from '@wirexapp/card-reveal-web';

const reveal = createCardReveal(
  { environment: 'uat', clientId: 'a4f6c1e2-9b03-4d77-8a51-2f0c6b8e4d19' },
  {
    cardId: '64120850-73a1-4df5-a074-d463258c9deb',
    actionToken,
    fetchDetails: true,
    fetchCvv: true,
    fields: ['cardholder', 'number', 'expiry', 'cvv'],
    cardholderName: 'ALEX GREY',
    auth: {
      getBearerToken: () => session.getAccessToken(),
      userEmail: user.email,
      userWallet: user.walletAddress,
    },
    onStateChange: ({ status, errorMessage }) => {
      if (status === 'error') showToast(errorMessage);
    },
    onCopied: ({ field }) => analytics.track('card_detail_copied', { field }),
  },
);

await reveal.mount(document.querySelector('#card-details'));

// When the user closes the details view:
reveal.destroy();

Configuration

FieldTypeRequiredDescription
cardIdstringYesIssued card to reveal
actionTokenstringYesSingle-use step-up token, consumed on fetch
fetchDetailsbooleanYesRequests the number and expiry rows. Ignored when fields is set
fetchCvvbooleanYesRequests the cvv row. Ignored when fields is set
fieldsarrayNoExact rows to render, in order: cardholder, number, expiry, cvv, pin. Drives which endpoints the frame calls
cardholderNamestringNoRendered as a plain, non-sensitive row
auth.getBearerToken() => Promise<string>YesReturns a fresh user token
auth.userEmailstringNoSent as X-User-Email. A cross-origin frame cannot read your session
auth.userWalletstringNoSent as X-User-Wallet
appearanceobjectNoWhitelisted styling tokens — see Appearance and Localization
stringsobjectNoTranslated labels — see Strings Keys
onStateChange(state) => voidNo{ status, errorMessage? }
onCopied(event) => voidNo{ field } — which row the user copied. The value never leaves the frame

fetchDetails and fetchCvv are required by the TypeScript config type even when fields is set. When
fields is present it decides the rows; the two flags are not read. Omitting fields renders
cardholder, then number and expiry when fetchDetails is true, then cvv when fetchCvv is
true — the pin row is reachable only through fields.

The chain identifier and the API base URL are the frame service's own configuration. Neither is a config
field, so your page cannot point a reveal request at another host.

Identity Headers

auth.userEmail and auth.userWallet are sent as X-User-Email and X-User-Wallet. A
user token carries the user id, wallet address and
email in its own claims, so the API resolves the cardholder from the token and does not read these headers.

Setting both is safe with a user token and rejected with an S2S token, which reads exactly one identity
header and returns ErrorInvalidField (field: user_identifier) when given more. This is one more
reason the bearer must be a user token. See
User Identity Headers.


Element State

onStateChange: ({ status, errorMessage }) => {
  // status: 'loading' | 'ready' | 'error'
}
StatusMeaning
loadingThe frame verified its init and started fetching. Nothing renders yet
readyAt least one requested row loaded
errorEvery requested fetch failed. errorMessage is safe to display

A partial failure reports ready, not error. Rows whose fetch failed render an em dash while the
rows that succeeded render their values. The error banner and the error status appear only when nothing
loaded at all.

Causes of error: a consumed or expired actionToken, an unknown card id, a card status that does not
permit the operation, or a backend failure. The action_token error reasons are listed in
Card Details.


Copy Behavior

Each row carries a Copy button that writes to the clipboard from inside the frame. Your page receives no
clipboard content and no event payload containing values — onCopied carries the field name and nothing
else. The reveal iframe is the only frame in the SDK granted clipboard-write; the collect frames are
granted no permissions at all.


Strings Keys

KeyDefault
cardholderLabelCardholder
cardNumberLabelCard Number
expiryLabelExpiry date
cvvLabelCVV/CVC
pinLabelPIN
copyCopy
copiedCopied
loadErrorFailed to load card details

Lifecycle

  • Mount the element when the user asks to see the details; call destroy() when the view closes.
  • Elements are single-use. Re-opening the view requires a new element and a fresh actionToken.
  • mount() rejects when the frame document cannot load or refuses to initialize. See
    Events and Error Handling.

Your Responsibilities

  • Keep the reveal element short-lived. It exists for the duration of a user-initiated details view.
  • Mint the action_token immediately before mounting. A token minted earlier in a flow expires 5 minutes
    after issue.
  • Do not proxy POST /api/v1/cards/{cardId}/details, /cvv or /pin through your own backend. Proxying
    cardholder data is permitted only when that backend is itself PCI DSS compliant, which the SDK exists to
    avoid.

Did this page help you?