Skip to content
Documentation menu

Node SDK

@vitix/pay is the official TypeScript SDK. It is a thin, fully typed wrapper over the REST API with no runtime dependencies: it uses the global fetch and Web Crypto, so it runs on Node 18+, Bun, Deno, Cloudflare Workers, Vercel Edge and Next.js route handlers alike.

Install

bash
npm install https://api.vitix.net/sdk/vitix-pay-0.2.0.tgz# orpnpm add https://api.vitix.net/sdk/vitix-pay-0.2.0.tgz# oryarn add https://api.vitix.net/sdk/vitix-pay-0.2.0.tgz
RequirementDetail
RuntimeNode 18 or newer, or any runtime with global fetch, Headers and crypto.subtle.
Module formatsESM and CommonJS, with bundled .d.ts type definitions.
DependenciesNone.
SizeUnder 10 kB minified.

Initialize

Create one client per process and reuse it. The key decides the mode: a vx_test_ key talks to test data, a vx_live_ key to live data.

ts
import { VitixPay } from "@vitix/pay";const vitix = new VitixPay(process.env.VITIX_API_KEY!, {  // baseUrl: "https://api.vitix.net",   // default  // timeout: 15_000,                  // per request  // maxRetries: 2,                      // on network errors / 429 / 5xx (GET and idempotent POST only)});

Resources

Every method returns a Promise of the typed resource and maps 1:1 to an endpoint in the API reference.

ts
// Checkoutsconst checkout = await vitix.checkouts.create({ amount: 2000, currency: "usd", success_url: "https://app.example.com/ok" }, { idempotencyKey: "topup_8f1c" });const same = await vitix.checkouts.retrieve(checkout.id);const open = await vitix.checkouts.list({ status: "open", customer: "user_42", limit: 50 });await vitix.checkouts.expire(checkout.id);// Paymentsconst payment = await vitix.payments.retrieve("py_live_…");const recent = await vitix.payments.list({ customer: "user_42" });const { url } = await vitix.payments.invoice("py_live_…");// Refundsconst refund = await vitix.refunds.create({ payment: "py_live_…", amount_usd: 500, reason: "customer_request" }, { idempotencyKey: "refund_8f1c" });await vitix.refunds.retrieve(refund.id);await vitix.refunds.list({ payment: "py_live_…" });// Eventsconst events = await vitix.events.list({ type: "payment.succeeded" });const event = await vitix.events.retrieve("evt_live_…");const deliveries = await vitix.events.deliveries("evt_live_…");await vitix.events.redeliver("evt_live_…");// Webhook endpointsconst ep = await vitix.webhookEndpoints.create({ url: "https://app.example.com/api/vitix/webhook", events: ["payment.succeeded"] });await vitix.webhookEndpoints.retrieve(ep.id);await vitix.webhookEndpoints.list();await vitix.webhookEndpoints.update(ep.id, { status: "disabled" });await vitix.webhookEndpoints.rotateSecret(ep.id);await vitix.webhookEndpoints.ping(ep.id);await vitix.webhookEndpoints.del(ep.id);// Accountconst account = await vitix.account.retrieve();

Per-request options

Methods that create resources accept a second argument: { idempotencyKey?: string, timeout?: number, signal?: AbortSignal }. Always pass an idempotencyKey when creating checkouts and refunds so network retries are safe.

Pagination

List methods return List<T>: { object: "list", data: T[], has_more: boolean }. Pass the last id as starting_after to continue.

ts
// Walk every payment for a customer, newest firstlet cursor: string | undefined;do {  const page = await vitix.payments.list({ customer: "user_42", limit: 100, starting_after: cursor });  for (const p of page.data) reconcile(p);  cursor = page.has_more ? page.data.at(-1)!.id : undefined;} while (cursor);

Errors

Failed requests reject with a subclass of VitixError carrying status, type, code, param and requestId. See the Errors page for the full code list.

ClassHTTPWhen
AuthenticationError401 / 403Missing, malformed, revoked key, or suspended account.
InvalidRequestError400 / 404Validation failures, unknown ids, business-rule violations.
IdempotencyError400Idempotency-Key reused with a different request.
RateLimitError429More than 100 requests in 10 seconds on one key.
APIError5xxSomething failed on Vitix's side; safe to retry.
VitixError—Base class, also used for network/timeout failures (status 0).
ts
import { VitixPay, InvalidRequestError, AuthenticationError, RateLimitError, VitixError } from "@vitix/pay";try {  await vitix.checkouts.create({ amount: 100, success_url: "https://app.example.com/ok" });} catch (err) {  if (err instanceof InvalidRequestError) {    // err.code === "amount_too_small", err.param === "amount"    showFormError(err.param, err.message);  } else if (err instanceof RateLimitError) {    await sleep(2_000); // then retry  } else if (err instanceof AuthenticationError) {    throw new Error("Check VITIX_API_KEY");  } else if (err instanceof VitixError) {    log.error({ requestId: err.requestId, status: err.status, code: err.code }, err.message);  } else throw err;}

Network errors, 429 and 5xx responses are retried automatically up to maxRetries times with exponential backoff for GET requests and for POST requests that carry an idempotency key.

Webhooks

Webhooks.constructEvent verifies the Standard Webhooks signature (including the 5-minute timestamp tolerance) and returns a typed Event. Pass the raw request body string, the request's headers (a Headers instance or a plain object) and the endpoint secret.

ts
import { Webhooks, WebhookSignatureError } from "@vitix/pay";// Throws WebhookSignatureError on a bad or stale signature, returns the typed Event otherwise.const event = await Webhooks.constructEvent(rawBody, request.headers, process.env.VITIX_WEBHOOK_SECRET!);// Boolean variantconst ok = await Webhooks.verify(rawBody, request.headers, process.env.VITIX_WEBHOOK_SECRET!);if (event.type === "payment.succeeded") {  event.data.object; // typed as Payment}

TypeScript

All resource shapes and event types are exported, and event.data.object narrows by event.type.

ts
import type { Checkout, Payment, Refund, Event, WebhookEndpoint, WebhookDelivery, Account, List } from "@vitix/pay";function onPaid(p: Payment) { /* p.amount_usd, p.customer?.id, p.metadata … */ }const page: List<Checkout> = await vitix.checkouts.list();

Cloudflare Workers & Edge

ts
// Cloudflare Worker / Edge runtime: same API, no Node built-ins requiredexport default {  async fetch(req: Request, env: { VITIX_API_KEY: string }) {    const vitix = new VitixPay(env.VITIX_API_KEY);    const checkout = await vitix.checkouts.create({ amount: 2000, success_url: "https://app.example.com/ok" });    return Response.redirect(checkout.url, 303);  },};

Questions or a missing feature? Email [email protected]