Documentation
Quickstart
Create a sandbox account, get a test key and take your first payment — in about ten minutes.
1. Create a sandbox account
Sign up in the dashboard at https://veyra-dashboard.web.app/signup. No card is required and nothing you do in the sandbox moves real money. Smart Setup will offer to configure a first product for you — you can accept or skip it.
2. Get your test key
In the dashboard, open Developers → API keys. Copy the secret key that starts with sk_test_. It is shown once and stored only as a hash, so keep it in your secret manager or a local .env file — never in client-side code or a repository.
export VEYRA_SECRET_KEY="sk_test_..."3. Create a Payment Intent
Amounts are integers in minor units: 12000 means €120.00. Always send an Idempotency-Key so a retry can never create a second payment.
curl http://localhost:4610/v1/payment_intents \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: order-1042-attempt-1" \
-H "Veyra-Version: 2026-10-01" \
-H "Content-Type: application/json" \
-d '{
"amount": 12000,
"currency": "EUR",
"description": "Order #1042",
"metadata": { "order_id": "1042" }
}'4. Send the customer to checkout
The fastest integration is a Checkout Session: create it on your server, redirect the customer to its url, and VEYRA handles card entry in provider-hosted fields, 3-D Secure and localisation.
curl http://localhost:4610/v1/checkout/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: checkout-1042" \
-H "Content-Type: application/json" \
-d '{
"currency": "EUR",
"line_items": [
{ "name": "Linen shirt", "amount": 8400, "quantity": 1 },
{ "name": "Shipping", "amount": 500, "quantity": 1 }
],
"success_url": "https://shop.example/thanks?session={CHECKOUT_SESSION_ID}",
"cancel_url": "https://shop.example/cart"
}'
# → { "id": "cs_...", "url": "http://localhost:4603/c/cs_...", "expires_at": ... }
# Redirect the customer to "url".Pay with a test card
4242 4242 4242 4242, any future expiry date and any three-digit CVC. See Testing for declines and 3-D Secure.5. Fulfil on the webhook
Register an endpoint under Developers → Webhooks and copy its signing secret (whsec_…). Ship the order when you receive checkout.session.completed — not when the browser comes back, because the customer may close the tab.
import { createHmac, timingSafeEqual } from "node:crypto";
const TOLERANCE_SECONDS = 5 * 60;
export function verifyVeyraSignature(rawBody: string, header: string, secret: string): boolean {
// header: "t=1760000000,v1=5f2b...e9"
const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=") as [string, string]));
const t = Number(parts.t);
if (!parts.v1 || !Number.isInteger(t)) return false;
if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SECONDS) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
// Express: use the RAW body — not the parsed JSON.
app.post("/webhooks/veyra", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifyVeyraSignature(req.body.toString("utf8"), req.get("Veyra-Signature") ?? "", process.env.VEYRA_WEBHOOK_SECRET!);
if (!ok) return res.sendStatus(400);
const event = JSON.parse(req.body.toString("utf8"));
// Handle event.type idempotently (events can be delivered more than once).
res.sendStatus(200);
});Done

