Skip to main content

Session Object

A checkout session represents a single payment attempt from creation to completion.

Session ID​

Format: vp_cs_{env}_{nanoid}

  • vp_cs_test_k7x9m2n4p3q8r1s5: sandbox session
  • vp_cs_live_k7x9m2n4p3q8r1s5: production session

The ID ends in a 16-character random string. It cannot be guessed.

Fields​

Core fields​

FieldTypeAlways PresentDescription
idstringYesSession ID
statusstring: pending | processing | succeeded | failed | expiredYesWhere the session is in its lifecycle. The status table below gives the trigger for each value.
expiredFromstring | null: pending | processing | failedNoHow an expired session expired, and whether a payment may still exist on it. null whenever status is not expired. pending means no payment had been started through Von Payments' servers, so nothing was charged. failed means nothing was charged either: a declined elements + chargeAtSubmit session that was closed with nothing recorded, or a session whose time limit passed while the buyer was at their bank's security check, once the payment provider confirmed that payment was never authorized. processing means a payment HAD been started and its outcome was never reported before the time limit passed: do not read it as "nothing was charged". On an elements + chargeAtSubmit session it can later change to failed when the provider confirms no authorization after the bank check times out; that attempt's payment_intent.failed / charge.failed webhooks announce it. Otherwise do NOT rely on polling the session or on a webhook to settle it, because a payment that lands on an already-expired session is not reported today. Contact support with the session id before charging the buyer again for the same order. null on a session that IS expired is unknown, and should be treated like processing.
paymentStatusstring: unpaid | held | paid | cancelledYesDid the money move? Deliberately separate from status, which says whether the buyer finished. unpaid means nothing is committed. held means authorised, not captured: the card is on hold and you must capture or void it. Do not fulfil on held. An authorisation you never capture lapses at the issuer and the sale is lost, and the window is shorter than most integrations assume: around seven days on Mastercard, American Express and Discover, about five on a Visa merchant-initiated payment, as little as two for card-present. Treat the shortest as your deadline unless you know the brand. paid means captured, and is the one to fulfil on. cancelled means a hold released without ever being captured. Refunds are not reported here: a captured-then-refunded session stays paid, so read the refund for that.
modestring: payment | setupYesPayment mode.
captureMethodstring: automatic | manualYesEchoes the captureMethod the session was created with (automatic or manual), so you can see which semantics it carries without inferring them from paymentStatus.
integrationModestring: embed | elementsYesThe integration mode the session was created with, as applied: embed if you asked for elements and your account does not support it. The same value the create response returned.
channelstring | null: virtual_terminalYesWhich Von Payments product created the session: virtual_terminal for a sale keyed in your dashboard's virtual terminal, null for a session your integration created. Set by Von Payments; you cannot send it.
chargeAtSubmitbooleanYesWhether the embedded fields charge the buyer on submit, as applied at create: false unless the session is an elements session in payment mode that asked for it.
threeDSecureobjectYesHow the session will authenticate the cardholder if the bank asks for it: an object whose mode is in_page, redirect or not_applicable (treat it as an open list). Worked out each time you read it, so it follows your account's current setup. It says what will happen, not that a challenge ran.
storedCredentialUsestring | null: recurring | installment | unscheduledYesThe storedCredentialUse declaration recorded at create: recurring, installment or unscheduled. null means none was declared. Read it back to confirm your declaration landed.
setupForFutureUsestring | null: on_sessionYeson_session when you declared setupForFutureUse at create, otherwise null. This is your declaration. What a saved card actually records is on the charge result, where a buyer who ticked "save my card" (off_session) wins.
merchantIdstringYesMerchant account that owns this session
amountintegerYesPayment amount in minor units
currencystringYesISO 4217 currency code (a required 3-letter uppercase code)
countrystring | nullNoISO 3166-1 alpha-2 country code (optional)
descriptionstring | nullNoHuman-readable description of the payment (max 500 characters)
localestring | nullYesThe checkout page language you sent at create, as stored. null when none was sent.
successUrlstring | nullNoRedirect URL on success. Echoes the value supplied at creation (null if none)
cancelUrlstring | nullNoRedirect URL on cancel. Echoes the value supplied at creation (null if none)
shippingstring: none | auto | requiredYesShipping-address collection mode actually applied: none, auto or required. Not necessarily what you sent: omit the field on create and this is your account setting (none out of the box); send it with a publishable key and a value weaker than your account setting is raised to it. See Asking the buyer for more than a card.
billingAddressstring: none | auto | requiredYesBilling-address collection mode actually applied: none, auto or required. Your account setting when you omitted it. This is the mode, not the buyer's address.
phonestring: none | auto | requiredYesPhone collection mode actually applied: none, auto or required. The number the buyer typed is buyerPhone.
collectEmailbooleanYesWhether the checkout shows an email input even when a buyer email was pre-filled, as stored at create.
nameCollectionstring: none | auto | requiredYesWhether this session requires the buyer's name: none, auto or required, from your account's buyer-field settings when the session was created.
emailCollectionstring: auto | requiredYesWhether this session requires the buyer's email: auto or required, from your account's buyer-field settings when the session was created. Email is always collected, so it is never none.
shippingAddressobject | nullNoBuyer's shipping address (only present when status is succeeded)
buyerIdstring (≤ 200) | nullYesYour own reference for this session's buyer, as sent at create. null when the session has none. The buyer's email and name are personal data and are never returned here.
buyerPhonestring | nullYesThe phone number the buyer typed on the checkout page. null when none was collected. Not the same as phone, the collection mode you set at creation, which decides whether the field is shown.
transactionIdstring | nullNoThe payment provider's reference for this session's payment (set on completion). Not the id refunds take: use vp_tx_id.
vp_tx_idstring | nullYesThe Von transaction id (vp_tx_*): the id POST /v1/refunds takes as transaction, and the same vp_tx_id the charge.* webhooks carry, so a server that confirms a payment by reading the session can refund it without a webhook. null until captured, and while paymentStatus is held. Can trail paid by a few moments: read again rather than treating null as "no payment". Also null when vp_tx_count is 2 or more.
vp_tx_countintegerYesHow many separate payments are recorded for this session: 0 before payment and while held, 1 normally. 2 or more means the buyer was charged more than once: vp_tx_id is then null on purpose. Check the payments by hand before refunding or fulfilling.
amount_refundedinteger | nullYesTotal refunded so far on the session's payment, in minor units: refunds done or still in progress (requested). Failed and canceled refunds are not counted. On GET only; PATCH and expire return null. Also null when vp_tx_id is null or the totals could not be read at that moment. To list the refunds themselves, call GET /v1/refunds?transaction= with vp_tx_id.
remaining_refundableinteger | nullYesWhat a refund with no amount would return now, in minor units: the captured amount minus amount_refunded. A refund still in progress is already subtracted; if it later fails, its amount comes back. 0 whenever refundable is false. null in the same cases as amount_refunded.
refundableboolean | nullYesWhether the payment qualifies for a refund now, by the same rules POST /v1/refunds applies: false when it is not settled yet, disputed, already fully refunded or not refundable through the API. Account conditions, such as refunds being switched on for your payment provider, are still checked by the refund call. null in the same cases as amount_refunded.
paymentIntentIdstring | nullYesThe payment intent this session's payment created. On a captureMethod: "manual" session this is the id you capture or void once paymentStatus reads held. Also set on a paid automatic payment, where the money is already taken: use it to read or refund, never to capture. Decide from paymentStatus (only held owes a capture), not from this field being present. null before payment, for a few moments while a payment is confirmed, and when more than one payment intent is recorded (check by hand rather than capturing either).
retryobject | nullYesWhether a declined elements + chargeAtSubmit session will take another card, on an account with retry on the same session switched on: { allowed, attempts_remaining }. allowed: true means a decline re-opened the session (status is pending) and the buyer can pay with another card. allowed: false with attempts_remaining above 0 means re-opened, with the next card form still being prepared: read again in a second. allowed: false with attempts_remaining: 0 means the session ended with a decline (failed). null when retry on the same session is not switched on for your account, the session is not elements + chargeAtSubmit, nothing has been declined, or a decline handed the session back (pending) without using up an attempt, in which case it can still be paid. Only allowed: false with attempts_remaining: 0 is final; close the session before you cancel an order on it. This is the only place to learn it after a decline on the bank's 3-D Secure page, where no charge response is waiting. The failure webhooks carry the same field.
last_payment_errorobject | nullYesThe most recent declined attempt, when the session failed, was re-opened after a decline, or is expired with expiredFrom: "failed": { code, decline_code }. code is card_declined (the bank refused) or payment_failed (any other reason); decline_code uses the same values as a payment intent's decline_code, or null. Filled in on GET only; PATCH and expire return null. Also null if it could not be read at that moment, which never fails the read.
metadataobject of string | nullNoMerchant-provided key-value pairs (passed through to webhooks)
createdAtstringYesISO 8601 creation timestamp
updatedAtstringYesISO 8601 last update timestamp
expiresAtstringYesISO 8601 expiry timestamp

A buyer on the session is what makes the embedded charge-and-save flow vault a reusable vp_pmt_* alongside the charge; a guest session charges once and vaults nothing (charged: true, no token). A returning buyer also sees the cards saved in prior completed sessions inside the embed — buyer identification.

Payment routing (VORA)​

Processor selection happens server-side. Neither POST /v1/sessions nor GET /v1/sessions/:id tells you which processor runs the payment — VORA.

Status Lifecycle​

pending ──> processing ──> succeeded
└──> failed ──> succeeded (retry converges)

pending ──> expired
processing ──> expired
StatusDescriptionTrigger
pendingSession created, waiting for buyerPOST /v1/sessions
processingBuyer has acted; charge in flightBuyer submits payment
succeededPayment completedPayment processor confirms the capture
failedPayment declined or erroredPayment processor rejects
expiredSession reached its expiry without completingCleanup sweep (see Rules)

Rules​

  • A succeeded session cannot become failed, and cannot be charged again: one session pays for one order once. The reverse is allowed. failed is not terminal; a buyer whose card was declined may pay again on the same session until it expires, and a successful retry converges it to succeeded. Do not mark an order dead on failed. Keep listening for charge.succeeded.
  • Expired sessions cannot be re-activated; create a new session.
  • Session TTL defaults to 30 minutes, but is configurable per session via expiresIn (from 5 minutes up to 7 days). expiresAt equals the creation time plus that TTL.
  • A session that passes its expiresAt without completing is moved to expired by a periodic cleanup sweep (not at the exact instant the TTL elapses). Only sessions that have not moved money are expired this way.