Skip to main content

VORA Mirror — Elements reference

VORA Mirror ships individual UI elements that you mount into your checkout page. Each element handles one slice of the buyer's input (card number, billing address, email, etc.) and exposes a uniform mount / event / collect API across providers.

This page covers what's available today, the options each element accepts, and the value shapes they emit on collect.


Status overview

Element typeStatusNotes
card✅ LiveThe PAN / expiry / CVC iframe. Generally available.
cardholder✅ LiveVORA-rendered cardholder-name input.
email✅ LiveVORA-rendered email input with validation.
address✅ LiveVORA-rendered billing-address form (line1/line2/city/state/postalCode/country).
save-for-future-use✅ LiveCompliance-locked opt-in checkbox.
saved-methods🟡 Coming soonPicker for previously vaulted payment methods. Type stub today; runtime returns frame_not_implemented.
express-checkout🟡 Coming soonApple Pay + Google Pay wallet button row. Type stub today; runtime returns frame_not_implemented. Pending wallet domain-verification rollout.

🟡 "Coming soon" elements have a stable type surface today. You can write merchant code against elements.create('saved-methods', …) now; the call throws frame_not_implemented at runtime until the implementation ships. Track activation on the VORA release notes.


Single-element vs collection patterns

Two ways to create elements:

Single-field shorthand (legacy, still supported)

const card = vora.fields.create("card", { style: {} });
card.mount("#card-element");

Continues to work for existing integrations. Returns a VORAField (the original card-only API).

const elements = vora.elements.create({
theme: { font: { size: "16px" }, color: { primary: "#1a1a1a" } },
});

const card = elements.create("card");
const address = elements.create("address", {
fields: { line2: { hidden: false } },
});
const email = elements.create("email");

card.mount("#card-element");
address.mount("#address-element");
email.mount("#email-element");

// Submit collects from every element + drives binder tokenization,
// returning a unified result shape.
const result = await elements.submit();
if (result.error !== undefined) {
// Validation or tokenization failure — surface result.error.message
} else {
// result.token === "vp_pmt_*"
// result.email, result.cardholder, result.billingAddress, result.setupForFutureUse
// are all present iff their corresponding elements were mounted.
}

The elements collection shares theme defaults across child elements. vora.fields.create (singular, card-only) is the legacy path and deprecates at v2.0; new integrations should prefer vora.elements.create().

SubmitResult shape

interface SubmitResult {
token: string; // vp_pmt_*
last4: string;
brand: string; // "visa" | "mastercard" | …
email?: string;
cardholder?: { name: string };
billingAddress?: BillingAddressValue;
setupForFutureUse?: boolean;
error?: FrameError; // present iff submit failed pre-tokenize
}

The submit result is one object regardless of how many elements you mount. Optional fields are present iff the corresponding element was mounted in the collection.


card — PAN / expiry / CVC iframe

The classic card field. The iframe is served directly from your active card processor's CDN; your DOM never sees the PAN.

Options

interface CardElementOptions {
style?: VORAFieldStyle;
classes?: { focus?: string; invalid?: string; complete?: string };
placeholder?: { number?: string; expiry?: string; cvc?: string };
challengeTimeout?: number; // ms — default 600_000 (10 minutes)
locale?: string; // BCP-47, e.g. "en-US"; phase 2A is en-only
disable3dsModal?: boolean; // opt out of the harmonized VORA 3DS modal; default false (modal on)
}

Events

card.on("ready", () => { /* iframe loaded and accepting input */ });
card.on("change", (event) => {
// event.complete: boolean — all of number/expiry/cvc are valid
// event.empty: boolean
// event.error?: { type: "validation_error", message }
});
card.on("focus", () => {});
card.on("blur", () => {});

Collect

card.tokenize() (singular API) returns the legacy single-card shape:

interface TokenizeResult {
token: string; // vp_pmt_*
paymentMethod: {
type: "card";
card: { brand: string; last4: string; expMonth: number; expYear: number };
};
}

For multi-element collections, use elements.submit() instead — see the collection example above for the unified SubmitResult shape.

The vp_pmt_* is bound to the session that minted it. Pass it to your server for POST /v1/payment_intents.


cardholder — Cardholder name input

VORA-rendered input (no iframe — cardholder name isn't PCI-restricted, and a VORA-controlled input gives merchants better styling + native browser autofill).

Options

interface CardholderElementOptions {
style?: VORAFieldStyle;
placeholder?: string; // default "Cardholder name"
required?: boolean; // default true
maxLength?: number; // default 200
}

Collect

Returns { cardholder: { name: string } } from the collection's collect step. Forwarded to the binder's tokenize call as billing_details.name.


email — Email input

VORA-rendered <input type="email"> with HTML5-aligned validation.

Options

interface EmailElementOptions {
style?: VORAFieldStyle;
placeholder?: string; // default "you@example.com"
required?: boolean; // default true
}

Collect

Returns { email: string }. Used both as billing_details.email for the binder and as the buyer email on the session for receipt delivery.


address — Billing address form

VORA-rendered multi-input form. Today mode: 'billing' is the only supported value; 'shipping' mode is on the roadmap and the type union is already in place.

Field set: line1, line2, city, state, postalCode, country. Renders as native HTML inputs (no iframes — billing addresses aren't PCI scope, and VORA-controlled DOM gives merchants better styling and autocomplete="billing street-address" browser-autofill compatibility.

Options

interface AddressElementOptions {
style?: VORAFieldStyle;
mode?: "billing" | "shipping"; // billing only today; shipping on roadmap
fields?: {
line1?: { required?: boolean }; // default required
line2?: { hidden?: boolean }; // default visible
city?: { required?: boolean }; // default required
state?: { required?: boolean }; // default required
postalCode?: { required?: boolean }; // default required
country?: { default?: string }; // ISO-3166-1 alpha-2; default "US"
};
countries?: readonly string[]; // restrict country dropdown
}

Collect

interface BillingAddressValue {
line1: string;
line2?: string;
city: string;
state: string;
postalCode: string;
country: string; // ISO-3166-1 alpha-2
}
// emitted as { billingAddress: BillingAddressValue }

save-for-future-use — Opt-in checkbox

VORA-rendered checkbox + label that controls whether the resulting vp_pmt_* is registered for future merchant-initiated transactions (MIT) on the buyer's account.

Compliance lock

The checkbox starts unchecked. There is no defaultChecked option. EU GDPR + several US state privacy laws require active opt-in for storing payment-method tokens for future MIT use. Pre-checked default would void the consent record downstream. This is a hard design lock — do not work around it.

Options

interface SaveForFutureUseElementOptions {
style?: VORAFieldStyle;
label?: string; // default "Save this card for future purchases"
}

Collect

Returns { setupForFutureUse: boolean }. Flows through to /v1/public/tokens registration; the resulting vp_pmt_* is marked reusable for MIT only when the box is checked.


saved-methods (coming soon) — Vaulted-method picker

Lists the buyer's previously vaulted payment methods (via vp_tk_* tokens) and lets them re-use one for an MIT-eligible flow without re-entering card data.

Cross-binder neutral — VORA Mirror queries /v1/public/tokens (server-issued vault list) for all binders and renders its own picker UI.

Options (type-stable today)

interface SavedMethodsElementOptions {
style?: VORAFieldStyle;
buyerId: string; // required — identifies whose vault to list
}

Status

Mounting throws frame_not_implemented today. Type surface is stable; merchant code written against this signature will work without changes once the implementation ships.


express-checkout (coming soon) — Apple Pay + Google Pay buttons

Wallet-button row. Apple Pay + Google Pay at first; future wallets (Link, Cash App Pay) add non-breakingly via the wallets array.

Options (type-stable today)

interface ExpressCheckoutElementOptions {
wallets?: readonly ("apple_pay" | "google_pay")[]; // default both
buttonLayout?: "horizontal" | "vertical"; // default horizontal
}

Status

Mounting throws frame_not_implemented today. Pending the wallet domain-verification endpoint rollout. The capability map already returns per-binder support flags via POST /v1/public/binder-load — track activation on the VORA release notes.


Theming — VORAFieldStyle

All elements accept a style option whose shape is governed by an allowlist (any key not in the allowlist throws frame_style_invalid at validation time, before the iframe mounts).

interface VORAFieldStyle {
font?: {
family?: string; // CSS font-family
size?: string; // "16px"
weight?: string | number;
lineHeight?: string;
};
color?: {
primary?: string; // hex / rgb / oklab
placeholder?: string;
error?: string;
icon?: string;
};
spacing?: {
padding?: string; // CSS shorthand
};
border?: {
color?: string;
width?: string;
radius?: string;
};
background?: {
color?: string;
};
}

Theme on the collection is inherited by all child elements. Element-level style overrides per-property (shallow merge at the top-level keys).


What's next

  • React wrapper<VORAProvider>, <CardField>, useVORA() for the React-shaped version of every element
  • Error handlingFrameError shape, codes, retry semantics
  • Quickstart — end-to-end integration walkthrough