Skip to main content

Payment Intents quickstart

Server-side payment lifecycle in 5 steps. Use this path when you need explicit control over auth, capture, void, and refund — for example delayed capture (auth on order, capture on ship), subscriptions, or platform-integrator flows.

Comparing paths first? See Choose your integration. For the deep reference (every field, error code, capability gate, MIT details), see the Payment Intents reference.


Step 0: Get your test keys

Same path as the Checkout quickstart Step 0 — start at vonpay.com/developers, click Activate VORA Sandbox, and grab vp_sk_test_* (secret) and vp_pk_test_* (publishable). The server-side calls below use the secret key.


Step 1: Tokenize a card

Payment Intents charges a vp_pmt_* token, never raw card data. The standard production path pairs Payment Intents with Embedded Fields — buyer's card enters our iframe, you get back a token.

For this quickstart, sandbox keys can auto-mint a mock token without an iframe:

curl -X POST https://checkout.vonpay.com/v1/tokens \
-H "Authorization: Bearer vp_sk_test_xxx" \
-H "Content-Type: application/json" \
-d '{}'
{
"id": "vp_pmt_test_QAqnXEJF_TCum1jg",
"status": "active",
"card": { "brand": "visa", "last4": "4242", "exp_month": 12, "exp_year": 2030 }
}

In production you'd POST { "provider_reference": "<iframe-minted handle>" } to bind your iframe-vault token to a vp_pmt_*.


Step 2: Create a Payment Intent

Use the vp_pmt_* from Step 1 as the payment method. capture_method: "automatic" does auth + capture in one call:

Node

import { VonPayCheckout } from "@vonpay/checkout-node";

const vonpay = new VonPayCheckout(process.env.VON_PAY_SECRET_KEY);

const intent = await vonpay.paymentIntents.create(
{
amount: 1499,
currency: "USD",
captureMethod: "automatic",
paymentMethod: { id: "vp_pmt_test_QAqnXEJF_TCum1jg" },
metadata: { orderId: "ord_42" },
},
{ idempotencyKey: "ord_42_create_attempt_1" }
);

// intent.status: "succeeded" | "requires_action" | "failed"

Python

intent = vonpay.payment_intents.create(
amount=1499,
currency="USD",
capture_method="automatic",
payment_method={"id": "vp_pmt_test_QAqnXEJF_TCum1jg"},
metadata={"order_id": "ord_42"},
idempotency_key="ord_42_create_attempt_1",
)

cURL

curl -X POST https://checkout.vonpay.com/v1/payment_intents \
-H "Authorization: Bearer vp_sk_test_xxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ord_42_create_attempt_1" \
-d '{
"amount": 1499,
"currency": "USD",
"capture_method": "automatic",
"payment_method": { "id": "vp_pmt_test_QAqnXEJF_TCum1jg" }
}'

Use capture_method: "manual" if you want to authorize now and capture later — the intent will stop at authorized and wait for an explicit /capture call.


Step 3: Handle requires_action (3DS challenge)

If the buyer's bank requires Strong Customer Authentication, the intent returns status: "requires_action" with a next_action block:

if (intent.status === "requires_action" && intent.nextAction?.type === "redirect_to_url") {
// Top-level navigation — NOT inside an iframe (banks block this).
res.redirect(intent.nextAction.redirect_to_url.url);
}

The buyer completes the challenge on their bank's page. You learn the final status via webhook (payment_intent.succeeded / payment_intent.failed) — don't trust the buyer's browser. See the reference for full 3DS handling.


Step 4: Capture, void, or refund

For auth-only intents, capture later (empty body captures the full authorized amount):

curl -X POST https://checkout.vonpay.com/v1/payment_intents/vpi_test_abc123/capture \
-H "Authorization: Bearer vp_sk_test_xxx" \
-H "Idempotency-Key: ord_42_capture_attempt_1" \
-d '{}'

Refund (omit amount for full refund):

curl -X POST https://checkout.vonpay.com/v1/refunds \
-H "Authorization: Bearer vp_sk_test_xxx" \
-H "Content-Type: application/json" \
-d '{ "payment_intent": "vpi_test_abc123", "reason": "customer_requested" }'

Void (only valid before capture):

curl -X POST https://checkout.vonpay.com/v1/payment_intents/vpi_test_abc123/void \
-H "Authorization: Bearer vp_sk_test_xxx" \
-H "Idempotency-Key: ord_42_void_attempt_1" \
-d '{}'

Some processors don't support void-after-capture; the reference covers the capability gate so you can branch up front rather than catch the error mid-flow.


Step 5: Go live

Same swap as the Checkout path: vp_sk_test_*vp_sk_live_*. Direct-charge with payment_method may need a per-processor activation gate on live keys — if your live calls return endpoint_not_implemented, contact your VORA point of contact to enable it for your merchant.


Next steps