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 type | Status | Notes |
|---|---|---|
card | ✅ Live | The PAN / expiry / CVC iframe. Generally available. |
cardholder | ✅ Live | VORA-rendered cardholder-name input. |
email | ✅ Live | VORA-rendered email input with validation. |
address | ✅ Live | VORA-rendered billing-address form (line1/line2/city/state/postalCode/country). |
save-for-future-use | ✅ Live | Compliance-locked opt-in checkbox. |
saved-methods | 🟡 Coming soon | Picker for previously vaulted payment methods. Type stub today; runtime returns frame_not_implemented. |
express-checkout | 🟡 Coming soon | Apple 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 throwsframe_not_implementedat 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).
Elements collection (recommended for multi-element checkouts)
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 handling —
FrameErrorshape, codes, retry semantics - Quickstart — end-to-end integration walkthrough