Skip to main content

Quickstart

Get a working checkout integration in 5 minutes.

This is the Checkout quickstart — the simplest path. For other integration paths see Choose your integration, or jump straight to the Embedded Fields quickstart if you want buyers to stay on your domain.

Step 0: Get your test keys

Start at vonpay.com/developers — the developer-first landing page. The Get sandbox keys button deep-links into the developer dashboard, where one click on Activate VORA Sandbox mints all three test credentials in seconds:

  • vp_sk_test_* — secret API key (server-only)
  • vp_pk_test_* — publishable key
  • ss_test_* — session secret, issued alongside them; your integration does not need it

Your webhook signing secret (whsec_*) is separate again — one is issued per webhook endpoint when you create it.

OTP sign-in (any email), no merchant application, no ops approval, no shared demo credentials.

Test mode is fully simulated — no real money

Your test keys (vp_sk_test_* / vp_pk_test_*) run against Von Payments' built-in sandbox, a mock payment provider. No real card processor is contacted and no money moves — outcomes are deterministic, driven by the test card number (and amount). You can build and test the entire flow this way without boarding a processor. See Sandbox for the outcome table. (Advanced: you can later attach a real-processor sandbox to your account for richer decline testing — still no real funds.)

Already signed in? Skip the landing page and go straight to app.vonpay.com/dashboard/developers.

The same path works whether you're a merchant going live with Von Payments or a developer/platform integrator evaluating VORA — the Activate VORA Sandbox flow short-circuits business-details collection so you can build without going through KYC. The merchant record is real, but the UX never asks you to be a business. See Platform Integrator Sandbox if you're building a connector, or Go-Live Checklist for the path to live keys (vp_sk_live_*) after KYC + contract review.

Other prerequisites

  • Node.js 20+ (or Python 3.9+ for the Python SDK)

Step 1: Install the SDK

Node.js

npm install @vonpay/checkout-node

Python

pip install vonpay-checkout

CLI

npm install -g @vonpay/checkout-cli
vonpay checkout login

Pinning to an exact version is recommended during the pre-1.0 window.


Step 2: Create a checkout session

When to create it: mint the session when the buyer clicks Checkout — not on every pageview. It's a server-to-server call with a 30-minute TTL, so creating it earlier just risks it expiring unpaid. See When to create the session.

Node.js

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

const apiKey = process.env.VON_PAY_SECRET_KEY;
if (!apiKey) throw new Error("VON_PAY_SECRET_KEY is required");

const vonpay = new VonPayCheckout(apiKey);

const session = await vonpay.sessions.create({
amount: 1499, // $14.99 in cents
currency: "USD",
successUrl: "https://mystore.com/order/123/confirm",
cancelUrl: "https://mystore.com/cart",
lineItems: [
{ name: "Premium Widget", quantity: 1, unitAmount: 1499 },
],
buyerId: "user_8f3a2b", // your STABLE per-user ID — same value every visit, never randomize
buyerName: "Jane Doe",
buyerEmail: "jane@example.com", // enables cross-session buyer dedup
});

Python

from vonpay.checkout import VonPayCheckout, LineItem

vonpay = VonPayCheckout(os.environ["VON_PAY_SECRET_KEY"])

session = vonpay.sessions.create(
amount=1499,
currency="USD",
success_url="https://mystore.com/order/123/confirm",
cancel_url="https://mystore.com/cart",
line_items=[
LineItem(name="Premium Widget", quantity=1, unit_amount=1499),
],
buyer_name="Jane Doe",
buyer_email="jane@example.com",
)

CLI

vonpay checkout sessions create --amount 1499 --currency USD

cURL

curl -X POST https://checkout.vonpay.com/v1/sessions \
-H "Authorization: Bearer vp_sk_test_xxx" \
-H "Content-Type: application/json" \
-H "Von-Pay-Version: 2026-04-14" \
-H "Idempotency-Key: order_123_attempt_1" \
-d '{
"amount": 1499,
"currency": "USD",
"successUrl": "https://mystore.com/order/123/confirm",
"lineItems": [{"name": "Premium Widget", "quantity": 1, "unitAmount": 1499}]
}'

Response:

{
"id": "vp_cs_test_k7x9m2n4p3",
"checkoutUrl": "https://checkout.vonpay.com/checkout?session=vp_cs_test_k7x9m2n4p3",
"expiresAt": "2026-03-31T15:30:00.000Z"
}

Step 3: Redirect the buyer

Send the buyer to the checkout URL returned in the session response.

Node.js (Express)

res.redirect(session.checkoutUrl);

Python (Flask)

return redirect(session.checkout_url)

The buyer sees the Von Payments hosted checkout page with billing address, payment methods (cards, Apple Pay, Google Pay, Klarna, etc.), and your order summary. You don't need to handle anything on this page.


Step 4: Handle the webhook

When a payment event occurs (charge succeeds, fails, or is refunded), Von Payments sends a signed POST to each registered webhook endpoint that subscribes to that event type. Register an endpoint at /dashboard/developers/webhooksWebhooks; mint a whsec_* signing secret at create time and store it in your handler env.

Node.js (Express)

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

const apiKey = process.env.VON_PAY_SECRET_KEY;
if (!apiKey) throw new Error("VON_PAY_SECRET_KEY is required");

const vonpay = new VonPayCheckout(apiKey);
const endpointSecret = process.env.VON_PAY_WEBHOOK_SECRET; // whsec_*

app.post("/webhooks/vonpay", express.raw({ type: "application/json" }), (req, res) => {
try {
const event = vonpay.webhooks.constructEvent(
req.body, // raw body (Buffer)
req.headers["x-vonpay-signature"] as string, // signature header
endpointSecret, // whsec_* — per-endpoint secret
);

switch (event.type) {
case "charge.succeeded":
console.log(`Payment succeeded: ${event.data.transaction_id}`);
// fulfill the order
break;
case "charge.failed":
console.log(`Payment failed: ${event.data.failure_reason}`);
break;
}

res.status(200).json({ received: true });
} catch (err) {
console.error("Webhook verification failed:", err.message);
res.status(400).json({ error: "Invalid signature" });
}
});

Python (Flask)

from vonpay.checkout import VonPayCheckout

vonpay = VonPayCheckout(os.environ["VON_PAY_SECRET_KEY"])
endpoint_secret = os.environ["VON_PAY_WEBHOOK_SECRET"] # whsec_*

@app.route("/webhooks/vonpay", methods=["POST"])
def webhook():
try:
event = vonpay.webhooks.construct_event(
request.data,
request.headers.get("x-vonpay-signature"),
endpoint_secret,
)

if event.type == "charge.succeeded":
print(f"Payment succeeded: {event.data['transaction_id']}")

return {"received": True}, 200
except Exception as e:
return {"error": str(e)}, 400

Key details:

  • The webhook secret is per-endpoint (whsec_*), minted when you register the endpoint. Each endpoint has its own. Not the same as your API key.
  • The x-vonpay-signature header carries t=<unix_seconds>,v1=<hex_hmac>. During a secret rotation window a second v1= is present; accept on any match.
  • Replay window is asymmetric: reject if more than 5 min in the past or 30 sec in the future.
  • Register express.raw() for this route before any app-wide express.json(). constructEvent verifies the signature against the raw request body; if express.json() parses it first, verification fails. Scope the raw parser to the webhook path only (as shown above), not globally.
  • Testing this handler: vonpay checkout trigger can fire session.*, payment_intent.*, and charge.refunded — but not charge.succeeded. To exercise the charge.succeeded branch specifically, run a real test-mode checkout — it fires charge.succeeded on settlement. (The CLI smoke-tests your payment_intent.* and charge.refunded branches directly; it just can't stand in for charge.succeeded.)
  • See Webhooks for the full surface walkthrough and Webhook Verification for the algorithm + reference verifiers in 5 languages.

Step 5: Confirm the payment when the buyer returns

After payment, the buyer is redirected to your successUrl with the session ID:

https://mystore.com/order/123/confirm?session=vp_cs_test_k7x9m2n4p3

That redirect means the buyer came back. It does not mean they paid. Read the outcome from the API with your secret key:

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

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

const sessionId = new URL(req.url, `https://${req.headers.host}`)
.searchParams.get("session");

const { status } = await client.sessions.get(sessionId);

if (status === "succeeded") {
// Show the confirmation page
} else if (status === "pending" || status === "processing") {
// Still in flight — show "confirming your payment", NOT a failure
} else {
// failed or expired — offer to try again
}
Do not fulfil the order here

A buyer who pays and then closes their laptop never loads this page. Fulfilment belongs on the charge.succeeded webhook, which reaches you regardless of what the browser does. This page is for showing the buyer something immediately.

And treat pending / processing as "still working", never as a failure — on the 3-D Secure path buyers routinely arrive here before the payment settles, and a failure page makes them pay twice.

See Handle the return for the full pattern, including fulfilling exactly once.


Step 6: Go live

Replace your test keys with live keys. That's it.

  1. Swap vp_sk_test_xxx for vp_sk_live_xxx
  2. Point your webhook subscription at your production endpoint and use its whsec_* secret
  3. Ensure successUrl uses HTTPS
  4. Test with a small real payment

Next steps

If you're integrating for your own checkout

If you're building a platform/CRM connector