JavaScript request helpers
Preview, copy, and understand every helper function.
Downloading is optional: preview and copy the complete code below. Download the file.
Use these functions on a Node.js server, in Express, or in a Next.js Node.js Route Handler. No npm dependencies are required. Copy the complete module below into vantra-client.mjs (or src/lib/vantra-client.mjs in Next.js).
Function reference
signRequest
signRequest({ secret, timestamp, body = "" })
Returns lowercase HMAC-SHA256 of timestamp + dot + body using the API secret. Timestamp is Unix seconds; body is the exact serialized JSON to transmit, or empty for a bodyless GET. Method, URL and idempotency key are not signed. This function does not send a request.
gatewayRequest
await gatewayRequest({ origin, apiKey, secret, method = "POST", path, data, idempotencyKey = "", fetchImpl = fetch })
Signs and sends one request. origin is the deployment origin without /api/v1; path must start with /api/v1/. data is a JSON-serializable object, or omit it for an empty body. Payment creation does not require idempotencyKey; omit it to send no header. Supply orderId in data for duplicate-order checks. fetchImpl is an optional fetch-compatible function for tests. Returns the HTTP envelope’s data, not the whole envelope. Non-2xx responses throw an Error with status and response; retain these for reconciliation. Network/JSON failures also propagate. Redirects are rejected and no automatic retries occur.
Complete source code
import { createHmac } from 'node:crypto';
export function signRequest({ secret, timestamp, body = '' }) {
return createHmac('sha256', secret)
.update(`${timestamp}.${body}`)
.digest('hex');
}
// Server-side only. Supply the deployed API origin (without /api/v1).
export async function gatewayRequest({ origin, apiKey, secret, method = 'POST', path,
data, idempotencyKey = '', fetchImpl = fetch }) {
if (!origin || !apiKey || !secret) throw new Error('Set the API origin and server-side credentials');
if (!path.startsWith('/api/v1/')) throw new Error('Use an /api/v1/ path');
const url = new URL(path, origin);
if (url.origin !== new URL(origin).origin) throw new Error('Unexpected API origin');
const body = data === undefined ? '' : JSON.stringify(data);
const timestamp = Math.floor(Date.now() / 1000).toString();
const response = await fetchImpl(url, {
method,
redirect: 'error',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'x-timestamp': timestamp,
'x-signature': signRequest({ secret, timestamp, body }),
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
},
...(body ? { body } : {}),
});
const result = await response.json();
if (!response.ok) {
const error = new Error(`Vantra HTTP ${response.status}`);
error.status = response.status;
error.response = result; // retain transactionId for reconciliation after an unresolved result
throw error;
}
return result.data;
}