Skip to content
VEYRA — Payments without borders

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.

.env
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

Use 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

You have taken a sandbox payment end to end. Next, follow it into your balance and a payout from the dashboard.
Quickstart — Documentation · VEYRA