Skip to main content

Choose your integration

Von Payments has two front-end integration pathsHosted Checkout and Embedded Fields (which itself renders in two modes: embed and elements). Both paths vault the same vp_pmt_* token and settle through the same server-side Payment Intents engine; they differ only in how much of the checkout UI you build. Pick by how much control you need over the UI and how much PCI handling you want to avoid.

Hosted CheckoutEmbedded Fields
Browser SDKvora-hosted.js (js.vonpay.com/v1/vora-hosted.js) — redirectsvora.js (js.vonpay.com/v1/vora.js) — no redirect
Server SDK@vonpay/checkout-node@vonpay/checkout-node + browser vora.js
You buildA redirect + return handlerA page that hosts our iframe-vault fields — one combined card box (embed), or each element placed individually (elements)
Buyer seescheckout.vonpay.comYour page, our card field(s)
PCI scopeNone (SAQ-A)None (SAQ-A; iframe vault)
Control over UILowHigh → Highest (in elements mode)
Time to integrate~30 min~half a day (embed) to ~1–2 days (elements)
3DSAutomaticSDK-managed modal
Submit behaviorBuyer pays on hosted pageTokenize-only or charge-and-save (charges on submit; also vaults a token when a buyer is on the session)
Saved cards (buyer present)Yes (via buyerId)Token reuse; charge-and-save vaults the token on submit
MIT (no buyer)Not applicableToken reuse in Payment Intents
When to pick itSimplest path; brand on checkout page acceptableBrand control matters; your-brand one-page (embed) or fully custom field layout (elements)

elements is not a third path — it's a render mode of Embedded Fields. The composable, place-each-field-yourself option is the elements mode (integrationMode: "elements"), covered under Embedded Fields below. See Render modes: embed vs elements for the full model.

Payment Intents is the engine, not a third path. Both front-end paths produce a vp_pmt_* and charge through POST /v1/payment_intents. You also call it directly — with no buyer-facing redirect — for delayed capture, voids, refunds, MIT, and recurring charges. See The Payment Intents engine below.

vora-hosted.js and vora.js are different scripts

The names are similar enough to mix up. vora-hosted.js is the redirect browser snippet used by the Checkout path. vora.js (a.k.a. @vonpay/vora-js) is the Embedded Fields SDK used by the Embedded Fields path. Both load from the js.vonpay.com/v1/ CDN family and take a publishable key, but they produce different buyer experiences. If a tutorial or AI agent suggests npm install @vonpay/vora-js, note that the npm package is internal-only today — use the CDN at https://js.vonpay.com/v1/vora.js.

Vonpay handles processor selection server-side via VORA. Your integration code never imports any processor SDK — the card iframe is mounted by vora.js, which picks the underlying processor adapter (its binder) at session-load time. See the Embedded Fields quickstart for the supported flow.

Sandbox vs. live behavior at a glance

The Embedded Fields path works in sandbox, but runs against a Vonpay-owned mock binder instead of a real gateway. Knowing this up front saves a round of "is this broken?" debugging. In sandbox, elements mode behaves exactly like embed — because discrete rendering isn't enabled there, so an integrationMode: "elements" session falls back to embed automatically (same SDK, same mock binder, the combined card field — never an error). It's the same fallback you'll hit on any live account that doesn't yet support discrete rendering, so everything in the table below applies to Embedded Fields in either mode.

Checkout (vora-hosted.js)Embedded Fields (vora.js)
Sandbox (test keys)✅ Works end-to-end. Mock gateway routes the session and emits webhooks.✅ Works against the sandbox mock binder. A sandbox merchant (no gateway configured) resolves to the Vonpay-owned "sandbox" iframe-vault binder, so GET /v1/public/sessions/:id and POST /v1/public/tokens return binder details and mount the mock fields. The mock binder is on by default (SANDBOX_EMBED_ENABLED is default-on).
Live (live keys)✅ Works end-to-end on every gateway.✅ Works on gateways with a supported iframe-vault binder. The merchant's gateway config determines availability — GET /v1/public/sessions/:id returns the binder details when supported, or HTTP 422 merchant_not_configured when the gateway has no supported iframe-vault binder yet.
Sandbox testing strategyUse as-is.Use the sandbox mock binder for the embedded dev loop. If you've set SANDBOX_EMBED_ENABLED=false (which restores the 422 gate), fall back to the hosted checkoutUrl returned by POST /v1/sessions.
Production strategyUse as-is.Use as-is; surface the live merchant_not_configured 422 as a "fall back to hosted" code path so an integrator can ship without waiting on every merchant's gateway readiness.

When the embedded path does return merchant_not_configured, that 422 response body links directly to docs/embedded-fields/quickstart and docs/guides/sandbox so the next click in a developer's investigation lands on the supported flow.

When you'd actually see the 422

merchant_not_configured (HTTP 422) on the embedded path is not the default sandbox experience. It fires only when the sandbox mock binder is kill-switched off (SANDBOX_EMBED_ENABLED=false), or for a live merchant whose gateway has no supported iframe-vault binder yet.

Hosted Checkout

The fastest path. Your server creates a session, the buyer pays on checkout.vonpay.com, and we redirect them back with a signed status. You write a redirect + a return handler — that's it.

Pick this if:

  • You want the simplest integration that exists
  • A Von-Payments-hosted checkout page is acceptable
  • You don't need delayed capture or auth-only flows

Go to Checkout →

Embedded Fields

Drop our iframe-vault fields into your own checkout page. The card form stays inside the iframe (PCI-isolated), but you control the page around it — branding, layout, copy.

Submit behavior. Embedded Fields has two submit behaviors (a property of the session you create, not an account setting):

  • Tokenize-only. Submit returns a vp_pmt_* token and moves no money. You charge it later via Payment Intents, or use it in a Session.
  • Charge-and-save. Submit charges the card immediately and, when the session carries a buyer, also vaults a reusable vp_pmt_* in the same step — no separate charge call. A guest/no-buyer session charges once and saves nothing. Because submit already charges, do not also charge the resulting token via Payment Intents — that double-charges the buyer.

The submit result is a discriminated union: { token } (tokenize-only, or charge-and-save with a buyer), { charged: true } (charge-and-save guest, no token), or { error } (a FrameError). Branch on result.error first, then result.token, then result.charged. The client result is a UX signal — confirm settlement via the webhook before fulfilling. See Tokenization for both flows.

Two render modes: embed and elements

Embedded Fields renders in one of two modes, set per session via integrationMode (default embed, no account-level lock) — a mode of the same path, not a separate integration:

  • embed (default) — a drop-in monolith: one combined card iframe renders the whole payment surface (card + cardholder + billing address + method picker + saved cards + wallet buttons). You place only email and save-for-future-use.
  • elementscomposable: you place card (the combined box or the three discrete card-number / card-expiry / card-cvc fields), email, cardholder, address, payment-method-picker, and standalone wallet buttons individually. Unsupported elements sessions fall back to embed silently — never an error; the echoed integrationMode tells you which surface you got.

See Render modes: embed vs elements for the full model + per-binder capability matrix, and Custom checkout with Elements for the elements-mode walkthrough.

Pick Embedded Fields if:

  • The checkout page brand has to be yours
  • You want a standard your-brand one-page checkout (embed), or place each field individually in your own layout (elements)

Go to Embedded Fields → · Custom layout with elements mode →

The Payment Intents engine

Both front-end paths above settle through Payment Intents — and you also call it directly, with no buyer-facing redirect, when your server is the source of truth. Drive auth, capture, void, and refund as discrete steps against a vp_pmt_* token.

Call it directly when:

  • You need delayed capture (auth on order, capture on ship)
  • You need a fraud check before capture
  • You're building a platform, subscriptions, or recurring (MIT) flows
  • You need to drive auth, capture, void, and refund as discrete steps

Go to Payment Intents →

Still not sure?

Need delayed capture, MIT, or recurring? ──────► Payment Intents (engine)
Need to place each field in your own layout? ──► Embedded Fields — elements mode
Need your-brand one-page checkout? ─────────────► Embedded Fields — embed mode
Just want the simplest thing that works? ──────► Hosted Checkout

Two paths often compose: with the tokenize-only Embedded Fields behavior, Embedded Fields tokenizes the card and Payment Intents charges it. That's the standard recipe when you need both full UI control AND server-driven lifecycle (e.g. a SaaS that takes a card today and charges later). This pairing applies only to the tokenize-only behavior — under the charge-and-save behavior the embed already charges on submit, so you must not also charge that token via Payment Intents.

3-D Secure & SCA by path

3-D Secure is issuer-driven — the bank decides whether to challenge, not your code. What differs is where the challenge renders and how much you handle. Embedded Fields behaves identically in both embed and elements mode.

PathWhere the challenge rendersWhat you doControl
Hosted CheckoutOn the hosted checkout.vonpay.com pageNothing — it's automatic.
Embedded Fields (both modes)The Embedded Fields SDK's harmonized 3DS modal, inside submit()Nothing for the synchronous flow — 3DS happens transparently inside submit(). For a server-confirmed charge, forward the intent's client_confirm block back to the SDK to render the challenge.disable3dsModal to use the binder's native sheet; challengeTimeout (default 5 min). Same controls in embed and elements. See Embedded Fields → 3D Secure.
Payment IntentsYour page (via the vora.js SDK, or your own handler)On confirm, the intent returns status: "requires_action" with a client_confirm block. Forward it to the browser to complete the challenge; the intent then settles.You drive the next_action / client_confirm round-trip. See the server-driven flow.

The 3DS test cards are the same across both paths and both modes — see Test cards.

Common to both paths

  • PCI scope. None on your side for either front-end path (Hosted Checkout, Embedded Fields — in embed or elements mode) — card data stays in our iframes (SAQ-A). Calling Payment Intents directly stays PCI-out as long as you don't pass raw card data — use the vp_pmt_* tokens those paths produce.
  • Webhooks. Same signed event surface — session.* for hosted checkout, payment_intent.* for intents. See Webhooks.
  • AI agents. Both paths are agent-friendly via the same SDKs, CLI, and MCP server. See AI Agents.
  • Test cards. The same sandbox cards work across both. See Test cards.