# Von Payments Checkout API
> Von Payments is a hosted checkout page. Merchants create a session via API, redirect the buyer to the checkout URL, the buyer pays, and is redirected back with a signed confirmation.
## Quick Start
1. Call `POST /v1/sessions` with your API key to create a checkout session
2. Redirect the buyer to the returned `checkoutUrl`
3. Buyer pays on the Von Payments hosted page (cards, Apple Pay, Google Pay, Klarna, 130+ methods)
4. Buyer is redirected to your `successUrl` with signed query params
5. Verify the HMAC signature server-side
## Authentication
All merchant API calls require a Bearer token:
```
Authorization: Bearer vp_key_live_xxx
```
## Create a Checkout Session
```
POST /v1/sessions
Authorization: Bearer vp_key_live_xxx
Content-Type: application/json
Idempotency-Key: optional-dedup-key
{
"amount": 1499,
"currency": "USD",
"country": "US",
"successUrl": "https://mystore.com/order/123/confirm",
"cancelUrl": "https://mystore.com/cart",
"description": "Order #123",
"locale": "en",
"mode": "payment",
"buyerId": "cust_123",
"buyerName": "Jane Doe",
"buyerEmail": "jane@example.com",
"lineItems": [
{ "name": "Premium Widget", "quantity": 1, "unitAmount": 1499 }
],
"metadata": { "orderId": "order_123" },
"expiresIn": 1800
}
```
Response (201):
```json
{
"id": "vp_cs_live_k7x9m2n4p3abcdef",
"checkoutUrl": "https://checkout.vonpay.com/checkout?session=vp_cs_live_k7x9m2n4p3abcdef",
"expiresAt": "2026-03-31T15:30:00.000Z"
}
```
### Required fields
- `amount` (integer) — amount in minor units (cents). 1499 = $14.99
- `currency` (string) — ISO 4217, 3 chars (USD, EUR, GBP)
### Optional fields
- `country` (string) — ISO 3166-1 alpha-2, 2 chars. Auto-detected if omitted.
- `successUrl` (string) — HTTPS redirect URL after payment success
- `cancelUrl` (string) — HTTPS redirect URL on cancel
- `description` (string) — payment description for bank statements
- `locale` (string) — checkout page language (en, fr, de, etc.)
- `mode` (string) — "payment" (default). Future: "setup" for card-on-file.
- `buyerId` (string) — your external customer ID (enables saved payment methods)
- `buyerName` (string) — pre-fills billing form, encrypted at rest
- `buyerEmail` (string) — encrypted at rest
- `lineItems` (array) — items displayed on checkout page. Each: { name, quantity, unitAmount, imageUrl? }
- `metadata` (object) — key-value string pairs, passed through to webhooks
- `expiresIn` (integer) — session TTL in seconds (300-3600, default 1800)
### Notes
- `merchantId` is derived from your API key — you don't pass it
- `amount` is the source of truth for charging. `lineItems` are display-only.
- Sessions expire after 30 minutes by default
- Idempotency: pass `Idempotency-Key` header to prevent duplicate sessions on retries
## Get Session Status
```
GET /v1/sessions/vp_cs_live_k7x9m2n4p3abcdef
Authorization: Bearer vp_key_live_xxx
```
Response:
```json
{
"id": "vp_cs_live_k7x9m2n4p3abcdef",
"status": "succeeded",
"mode": "payment",
"merchantId": "default",
"amount": 1499,
"currency": "USD",
"country": "US",
"description": "Order #123",
"transactionId": "txn_abc123",
"metadata": { "orderId": "order_123" },
"createdAt": "2026-03-31T15:00:00.000Z",
"updatedAt": "2026-03-31T15:05:00.000Z",
"expiresAt": "2026-03-31T15:30:00.000Z"
}
```
Session statuses: `pending` → `processing` → `succeeded` | `failed` | `expired`
## Verify Return URL Signature
After payment, the buyer is redirected to your `successUrl` with signed params:
```
https://mystore.com/confirm?session=vp_cs_live_xxx&status=succeeded&amount=1499¤cy=USD&transaction_id=txn_abc&sig=hexstring
```
Verify the HMAC-SHA256 signature server-side:
```
sig = HMAC-SHA256(
key: VON_PAY_SESSION_SECRET,
data: "{session}.{status}.{amount}.{currency}.{transaction_id}"
)
```
Example data string: `vp_cs_live_k7x9m2n4p3abcdef.succeeded.1499.USD.txn_abc123`
### Node.js verification
```javascript
import crypto from "crypto";
function verifyReturnSignature(params, secret) {
const data = `${params.session}.${params.status}.${params.amount}.${params.currency}.${params.transaction_id || ""}`;
const expected = crypto.createHmac("sha256", secret).update(data).digest("hex");
return crypto.timingSafeEqual(Buffer.from(params.sig, "hex"), Buffer.from(expected, "hex"));
}
```
### Python verification
```python
import hmac, hashlib
def verify(session, status, amount, currency, transaction_id, sig, secret):
data = f"{session}.{status}.{amount}.{currency}.{transaction_id or ''}"
expected = hmac.new(secret.encode(), data.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(sig, expected)
```
## Health Check
```
GET /api/health
```
No auth required. Returns `200` when healthy, `503` when degraded.
## Rate Limits
| Endpoint | Limit |
|----------|-------|
| POST /v1/sessions | 10/min per IP |
| GET /v1/sessions/:id | 30/min per IP |
Rate-limited responses return `429` with `Retry-After: 60` header.
## Error Format
All errors return:
```json
{ "error": "Human-readable message" }
```
Every response includes `X-Request-Id` header for debugging.
| Code | Meaning |
|------|---------|
| 400 | Invalid request body |
| 401 | Authentication failed |
| 404 | Session not found |
| 409 | Session in wrong state |
| 410 | Session expired |
| 429 | Rate limited |
| 500 | Server error |
## SDKs
### Node.js (@vonpay/node)
```bash
npm install @vonpay/node
```
```typescript
import { VonPay } from "@vonpay/node";
const vonpay = new VonPay("vp_key_live_xxx");
// Create session
const session = await vonpay.sessions.create({
amount: 1499,
currency: "USD",
successUrl: "https://mystore.com/confirm",
lineItems: [{ name: "Widget", quantity: 1, unitAmount: 1499 }],
});
// Redirect buyer to session.checkoutUrl
// Check status
const status = await vonpay.sessions.get("vp_cs_live_xxx");
// Verify return signature
const isValid = VonPay.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);
```
### Browser (vonpay.js)
```html
```
## Security
- PCI SAQ-A: card data never touches your servers or ours (secure iframe)
- PII (buyer name, email) encrypted with AES-256-GCM at rest
- HMAC-SHA256 signed return URLs (includes amount + currency)
- All URLs must be HTTPS (localhost exempt in sandbox)
- Session tokens are 16-char cryptographically random strings
## Links
- Docs: https://docs.vonpay.com
- OpenAPI spec: https://checkout.vonpay.com/openapi.yaml
- Status: https://checkout.vonpay.com/api/health