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 signing secret (verifies return URL signatures)

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: Verify the return redirect

After payment, the buyer is redirected to your successUrl with signed query parameters:

https://mystore.com/order/123/confirm
?session=vp_cs_test_k7x9m2n4p3
&status=succeeded
&amount=1499
&currency=USD
&transaction_id=abc123
&sig=v2.eyJzaWQiOi...
Signature format — v1 vs v2

The example above shows a v2 signature (sig=v2.…), the replay-safe format. Today the server's default is v1 — a bare hex HMAC (sig=<hex>, no v2. prefix). You don't choose: the SDK auto-detects the format and verifies either one, and the expectedSuccessUrl / expectedKeyMode options below bind only on v2 (they're ignored on v1). See Handle the return for the v1 replay caveat.

Always verify the signature server-side. The secret for return signatures is your session secret (VON_PAY_SESSION_SECRET, prefixed ss_test_* or ss_live_*), not your API key.

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

const url = new URL(req.url, `https://${req.headers.host}`);
const isValid = VonPayCheckout.verifyReturnSignature(
{
session: url.searchParams.get("session"),
status: url.searchParams.get("status"),
amount: url.searchParams.get("amount"),
currency: url.searchParams.get("currency"),
transaction_id: url.searchParams.get("transaction_id"),
sig: url.searchParams.get("sig"),
},
process.env.VON_PAY_SESSION_SECRET, // ss_test_* or ss_live_*
{
expectedSuccessUrl: "https://mystore.com/order/123/confirm",
expectedKeyMode: "test", // "live" in production
},
);

if (!isValid) {
throw new Error("Invalid return signature");
}

// Safe to show order confirmation

The SDK auto-detects v1 (the current default) vs v2 signature format. expectedSuccessUrl and expectedKeyMode are required for v2 — see Handle the return for details.


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. Update your session secret from ss_test_* to ss_live_*
  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