Choose your integration
Von Payments has two front-end integration paths — Hosted 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 Checkout | Embedded Fields | |
|---|---|---|
| Browser SDK | vora-hosted.js (js.vonpay.com/v1/vora-hosted.js) — redirects | vora.js (js.vonpay.com/v1/vora.js) — no redirect |
| Server SDK | @vonpay/checkout-node | @vonpay/checkout-node + browser vora.js |
| You build | A redirect + return handler | A page that hosts our iframe-vault fields — one combined card box (embed), or each element placed individually (elements) |
| Buyer sees | checkout.vonpay.com | Your page, our card field(s) |
| PCI scope | None (SAQ-A) | None (SAQ-A; iframe vault) |
| Control over UI | Low | High → Highest (in elements mode) |
| Time to integrate | ~30 min | ~half a day (embed) to ~1–2 days (elements) |
| 3DS | Automatic | You send the buyer to the bank's page |
| Submit behavior | Buyer pays on hosted page | Tokenize-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 applicable | Token reuse in Payment Intents |
| When to pick it | Simplest path; brand on checkout page acceptable | Brand control matters; your-brand one-page (embed) or fully custom field layout (elements) |
elementsis not a third path — it's a render mode of Embedded Fields. The composable, place-each-field-yourself option is theelementsmode (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 throughPOST /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 scriptsThe names are similar enough to mix up. vora-hosted.js is the redirect browser snippet used by the Checkout path. 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. vora.js always loads from https://js.vonpay.com/v1/vora.js. In a bundler, the @vonpay/vora package loads that script for you and adds TypeScript types; it does not contain the SDK, and @vonpay/vora-js is not on npm.
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
Both paths work with a sandbox account's test keys, webhooks fire, and no money moves. What a test key reaches is on Test mode.
Checkout (vora-hosted.js) | Embedded Fields (vora.js) | |
|---|---|---|
| Test keys | ✅ Works end-to-end on a sandbox account. A test key with no test environment behind it (a live merchant account, or a sandbox with no processor) is refused with HTTP 422 sandbox_account_required. | ✅ Works on a sandbox account whose processor has a supported card-field binder. Refused with sandbox_account_required in the same cases as Checkout. |
| Live (live keys) | ✅ Works end-to-end on every gateway. | ✅ Works on gateways with a supported card-field binder. GET /v1/public/sessions/:id returns the binder details when supported, or HTTP 422 merchant_not_configured when not. |
| Production strategy | Use as-is. | Surface merchant_not_configured 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 returns merchant_not_configured, the 422 response body links directly to Embedded Fields quickstart and Sandbox & Test Mode.
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
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 one flat object. Branch error → chargeStatus → token → charged: requires_action (3-D Secure, nothing charged — redirect the buyer) and pending (await the webhook) come before token (tokenize-only, or charge-and-save with a buyer) and charged: true (charge-and-save guest, no token). 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 onlyemailandsave-for-future-use.elements— composable: you placecard(the combined box or the three discretecard-number/card-expiry/card-cvcfields),email,cardholder,address,payment-method-picker, and standalone wallet buttons individually. Unsupportedelementssessions fall back toembedsilently — never an error; the echoedintegrationModetells 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
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. Charge at submit runs only in elements mode.
| Path | Where the challenge renders | What you do | Control |
|---|---|---|---|
| Hosted Checkout | On the hosted checkout.vonpay.com page | Nothing — it's automatic. | — |
| Embedded Fields — tokenize-only | Raised by your server's POST /v1/payment_intents (status: "requires_action"), not by submit() | Set return_url on the create call and send the buyer to next_action.redirect_to_url.url; without return_url, a card that needs a challenge is refused. | You drive the redirect. See Embedded Fields → 3D Secure. |
| Embedded Fields — charge at submit ( elements + chargeAtSubmit: true) | The card issuer's hosted challenge page — the buyer leaves your site for it | One branch is required. submit() resolves chargeStatus: "requires_action" + redirectUrl and nothing is charged; send the buyer to redirectUrl. They return to the session's successUrl, the charge settles, and the charge.* webhook is authoritative. | Create the session with successUrl. disable3dsModal / challengeTimeout do not apply. See Charge at submit → 3-D Secure. |
| Payment Intents (called directly) | The issuer's hosted page via next_action.redirect_to_url | Send return_url on every buyer-present charge — on a connection that completes the challenge by redirect, a card that needs one is rejected without it (422 provider_request_rejected), not challenged. The payment_intent.* webhook is the outcome. | You drive the redirect. See Authentication challenges. |
Which cards and amounts produce a 3-D Secure challenge, a decline or a success depends on your account's test path — see Test mode.
Common to both paths
- PCI scope. None on your side for either front-end path (Hosted Checkout, Embedded Fields — in
embedorelementsmode) — 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 thevp_pmt_*tokens those paths produce. - Webhooks. Same signed event surface. ⚠️ Fulfil on
charge.*—charge.succeededfor hosted checkout, pluspayment_intent.*for intents.session.succeeded/session.failedreport a hosted-checkout session's outcome and can be subscribed to, but they carry no saved card orvp_tx_id, so fulfil oncharge.succeeded. See Webhook events. - AI agents. Both paths are agent-friendly via the same SDKs, CLI, and MCP server. See AI Agents.
- Test mode. What a test key reaches, and what decides outcomes, is one page: Test mode.