Events and Error Handling
Change events, mount failures, tokenize errors, timeouts and element lifecycle rules for the PCI Compliant SDK.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| PCI Compliant SDK | Isolation model and package map |
| SDK Setup | Environments, CSP and origin registration |
Overview
Every element reports through callbacks and rejected promises. No callback and no rejection ever carries a
card value: events carry validity flags, the detected brand and tokenize results; errors carry a safe
message and, where a backend call was made, its HTTP status.
The Change Event
Collect elements report non-sensitive state while the user types.
onChange: state => {
state.complete; // boolean — every field of the profile passes validation
state.brand; // 'Visa' | 'MasterCard' | 'unknown'
state.fields; // { number: { valid, touched }, … }
}| Field | Type | Description |
|---|---|---|
complete | boolean | true when every field of the profile is valid |
brand | enum | Visa, MasterCard or unknown. Reported by the card-number field |
fields | object | Per-field { valid, touched }. Keys are the fields of the profile |
Use it to enable your submit button and to drive brand-dependent UI. A field that has never been mounted
reports { valid: false, touched: false }.
Field Validation
The frames validate as the user types and render error messages inline, inside the frame, styled by
colorError and localized by strings. Do not re-implement card validation on your page.
Calling a tokenizer while a field is invalid rejects with a PciFrameRequestError whose message starts
with Validation failed. The inline message is already visible to the user, so the typical handler shows
nothing extra for that case:
import { PciFrameRequestError } from '@wirexapp/card-topup-web';
try {
const result = await topUp.tokenizeTopUp({ billingDetails });
} catch (error) {
const isFieldValidation =
error instanceof PciFrameRequestError && error.message.startsWith('Validation failed');
if (!isFieldValidation) {
showToast('Failed to add card');
}
}The per-field variant reports which field failed: Validation failed — number: <message>,
Validation failed — missing <field> when a sibling field was never mounted, and
Validation failed — invalid <field> when a sibling holds an invalid value.
PciFrameRequestError
PciFrameRequestErrorEvery frame-request failure the frame itself reports — init and tokenize — rejects with this class.
| Property | Type | Description |
|---|---|---|
message | string | Safe, non-sensitive description: a validation failure, the backend's error message, or a transport failure |
status | number | undefined | HTTP status of the underlying backend call, where one was made |
| Message | Cause | Resolution |
|---|---|---|
Validation failed — … | A field is empty or invalid at tokenize time | Show nothing extra; the inline message is already visible |
Backend error text, with status | Tokenization rejected by Wirex | Surface a generic failure and let the user retry |
Network error | The frame could not reach Wirex | Surface a retry action |
Card service is not configured | Frame service misconfiguration | Report it to your Wirex integration contact |
The class is re-exported as a value by every capability package and by @wirexapp/card-react, so
instanceof works from whichever package you import.
Mount Failures
field(name).mount(container) and mount(container) reject when the frame document cannot load or refuses
to initialize. These reject with a plain Error, not a PciFrameRequestError.
try {
await topUp.field('number').mount(el);
} catch {
showToast('Failed to load the secure card form');
}| Message | Cause | Resolution |
|---|---|---|
PCI frame did not become ready in time | The parent origin is not registered, the environment is wrong, the network failed, or the loader and frame protocol versions differ | Verify the registered origins and the environment value; align every @wirexapp/card-* package on one version |
PCI frame is not mounted | A tokenize call was made before mounting or after destroy() | Create a new element |
PCI frame request timed out | No response to init or tokenize within 30 seconds | Surface a retry action |
PCI frame destroyed | destroy() ran while a request was in flight | Expected during teardown. Ignore it |
PCI SDK: unknown environment "<value>" | environment is not dev, uat or prod | Correct the config |
An unregistered parent origin is the most common mount failure. The browser blocks the frame through
frame-ancestors, the iframe renders empty, and the ready handshake never completes — so the symptom is a
timeout, not a CSP error in your own console.
| Phase | Limit |
|---|---|
| Frame ready handshake | 10 seconds |
| Frame request — init or tokenize | 30 seconds |
Reveal State
The reveal element reports its lifecycle through onStateChange.
onStateChange: ({ status, errorMessage }) => {
// status: 'loading' | 'ready' | 'error'
}| Status | Meaning |
|---|---|
loading | The frame verified its init and started fetching |
ready | At least one requested row loaded |
error | Every requested fetch failed. errorMessage is safe to display |
error covers a consumed or expired actionToken, an unknown card id, a card status that does not permit
the operation, and backend failures. A partial failure reports ready with an em dash in the failed rows —
see Reveal Card Details.
Lifecycle Rules
- Call
destroy()on every element when its view unmounts. It removes the iframes and the message
listeners. - Elements are single-use. After
destroy(), create a new element rather than remounting the old one. - Field iframes report their own height and the SDK sizes them. Width and placement come from your
containers. - Appearance and strings are read at mount. Re-theming requires a new element.
- In React these rules are handled by the components — see React Bindings.
What Never Reaches Your Page
- The PAN, CVV and expiry as typed by the user, and the values fetched by a reveal element
- The frames' API endpoints, which the frame service injects server-side
- Clipboard content copied inside a reveal frame.
onCopiedcarries the field name and nothing else - Any event payload beyond validity flags, the detected brand, and tokenize results —
cardIdor
externalCardId, brand, BIN, last 4 and expiry month and year
Updated 15 days ago

