Skip to main content
@ozura/elements/server is the server-side counterpart to the Elements browser SDK. It wraps the OzuraPay API and Vault API with typed methods, automatic field mapping, retry logic, and error handling.
Next.js / Fetch API users: use createSessionHandler instead of createSessionMiddleware. Both are exported from the same package. See createSessionHandler below.
ESM vs CJS: The package ships both ESM (import) and CommonJS (require) builds. Code examples throughout use ESM import syntax. If your project uses CommonJS (no "type": "module" in package.json, or a .cjs file), swap to require:
Both builds are identical in behaviour — only the module format differs.
This module runs on your server (Node.js, Deno, Bun, etc.). Never import it in browser code — it requires your vault key and merchant API key, which must stay secret.
Node.js requirement: @ozura/elements/server requires Node.js ≥ 18. Versions below 18 lack the native fetch API the package depends on.

Quick Start

Tokenize-only setup — vault-only merchants (using their own processor or just storing tokens) don’t need merchantId or apiKey:

Configuration

Tokenize-only integrations (create sessions + tokenize cards, no charging) only need vaultKey. The merchantId and apiKey fields are validated lazily — they are only required when you call cardSale().
Store all credentials in environment variables. Never hardcode them in source code or commit them to version control.

Methods

cardSale

Charge a tokenized card. Maps the CardSaleInput shape to the OzuraPay API’s flat field format.
cardSale never retries, regardless of the retries config. Financial POSTs are not idempotent — retrying a failed cardSale could cause a double charge. If you receive a 5xx response, check transaction records before retrying manually.
5xx de-duplication pattern. If cardSale throws with a 5xx status, the charge may or may not have been processed — the request could have timed out after the processor accepted it. Before retrying or failing the order, query listTransactions to check for a matching charge:
For stricter de-duplication, pass your own orderId in createSession when creating the session. You can filter listTransactions on a wider window and match by correlating your orderId to the transaction metadata.
Rate limit: 100 requests/minute per merchant.
You can pass tokenResponse.billing directly from the frontend — the browser SDK validates and normalizes it for you.

listTransactions

List transactions by date range with pagination. Returns all transaction types.
Rate limit: 200 requests/minute per merchant.

createSession

Creates a short-lived payment session key from the vault. This is the server-side companion to sessionUrl / getSessionKey on the frontend SDK.
Keep sessionLimit in sync with the client. Set the same value in VaultOptions.sessionLimit when calling OzVault.create() — see Installation → VaultOptions. A mismatch means the vault may reject a tokenize call before the client expects a refresh, causing a user-visible delay mid-checkout.
CreateSessionResult:

revokeSession

Revoke a payment session key. Best-effort — never throws. Call this on all three session-end paths to close the exposure window before the 30-minute vault TTL elapses. The SDK’s proactive/reactive refresh and sessionLimit are defence-in-depth layers — explicit revocation is the primary closure mechanism.
Wire all three exit paths:

Persisting session state for cancel/revoke

revokeSession(sessionKey) requires the sessionKey — but the browser SDK never exposes the session key to the page (it is kept inside the vault internals for security). To call revokeSession on cancel, your session route must store the mapping when the session is first created. The sessionId sent by the browser SDK to your session route is a UUID you can use as the lookup key. Store it server-side (e.g. Redis with the session TTL, a DB row, or an in-memory map for simple cases) when you create the session, then look it up when the cancel route fires.
If you do not need explicit revocation (e.g. tokenize-only flows with no cancel path), you can skip persistence entirely. Sessions expire automatically after their TTL (default 30 minutes). The sessionId the SDK sends is opaque from the browser’s perspective — it is safe to store as a key.

Session Route

The sessionUrl option in the browser SDK expects your backend to expose a POST /api/oz-session route. The SDK ships two factory functions to make this trivial.

createSessionHandler (Next.js / Fetch API)

Creates a handler for the Web Fetch API (RequestResponse). Use with Next.js App Router, Remix, Cloudflare Workers, etc.
The handler reads sessionId from the JSON body (sent automatically by the SDK), calls ozura.createSession(), and responds: The Elements SDK reads sessionKey from the 200 response. Any non-200 response causes OzVault.create() to reject with the error field as the message.

createSessionMiddleware (Express / Connect)

Creates an Express-style middleware ((req, res) => void). Requires express.json() (or equivalent body-parser) to be registered before it. The middleware expects req.body.sessionId as a string — any standard JSON body-parser that populates req.body works.
The response contract is the same as createSessionHandler above: { sessionKey } on success, { error } on failure.

Card Sale Handler Factories

For backends where a dedicated route fully owns the card sale flow (amount from server-side DB, billing from token, IP from request), the SDK ships factory functions that build complete handlers.

createCardSaleHandler (Next.js / Fetch API)

createCardSaleMiddleware (Express / Connect)

Both factories accept the same options: Both factories also:
  • Read token, cvcSession, and billing from the request body
  • Reject non-POST methods (405) and non-JSON content types (415)
  • Call getClientIp() to resolve the client IP
  • On success: return { transactionId, amount, cardLastFour, cardBrand }
  • On OzuraPay API error: return { error: string } with the normalized error message and the appropriate HTTP status (4xx/5xx); 429 includes a Retry-After header

Utilities

getClientIp

Extract the client IP address from a server request object. Works across frameworks:
Always fetch the client IP on the server. Never trust a value sent from the browser — ad blockers and browser extensions can interfere with client-side IP detection services.Headers like x-forwarded-for and x-real-ip are only trustworthy when your server sits behind a reverse proxy that strips and rewrites them. If your Node.js process is directly internet-accessible, an attacker can spoof these values.
What happens if clientIpAddress is wrong or missing? The OzuraPay API accepts the field without strict validation — it will not reject a cardSale request because the IP is an internal address, 127.0.0.1, or appears blank. The IP is used for fraud scoring and audit logging; an incorrect value weakens fraud detection but does not block the transaction. If getClientIp cannot resolve a real IP (e.g. headers are absent or your proxy uses a non-standard header), the charge will still proceed — but your fraud risk posture degrades. If you are behind a proxy that uses a custom header, extract the IP manually and pass it as a string rather than relying on getClientIp.

Error Handling

All methods throw OzuraError on failure.

OzuraError

Retry behavior

listTransactions and createSession automatically retry on 5xx and network errors with exponential backoff (1 s, 2 s, 4 s…). 4xx errors are never retried. cardSale is never retried — see note above.

Types

CreateSessionOptions

CreateSessionResult

The deprecated MintWaxKeyOptions and MintWaxKeyResult types are still exported as aliases to the above for backward compatibility.

CardSaleInput

CardSaleResponseData

surchargeAmount and tipAmount are only present in the response when non-zero. Handle missing values defensively:

ListTransactionsInput


Transaction Types

TransactionType

Narrowing TransactionData

listTransactions() returns TransactionData — a discriminated union. Use transactionType to narrow:
Field name difference: cardSale() returns billing fields as billingFirstName, billingLastName, etc. Transaction queries return them as firstName, lastName, etc. The SDK types reflect what each endpoint actually returns.

Full Example

Express backend handling a card payment end-to-end:

Next Steps

Card Elements

Set up the frontend card fields that produce the tokens this SDK consumes.

Error Handling

Normalize vault and payment errors for your users.

API Reference

Full type definitions for all SDK exports.

Installation

Credentials and OzVault.create() setup.