Documentation Index
Fetch the complete documentation index at: https://docs.ozura.com/llms.txt
Use this file to discover all available pages before exploring further.
@ozura/elements/server is the server-side counterpart to the Elements browser SDK. It wraps the Ozura Pay API and Vault API with typed methods, automatic field mapping, retry logic, and error handling.
Next.js / Fetch API users: usecreateSessionHandlerinstead ofcreateSessionMiddleware. Both are exported from the same package. See createSessionHandler below.
ESM vs CJS: The package ships both ESM (Both builds are identical in behaviour — only the module format differs.
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:Quick Start
merchantId and apiKey are not needed.
Configuration
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
merchantId | string | Charge flows only | — | Your Ozura merchant ID. Required for cardSale(). Omit for tokenize-only integrations. |
apiKey | string | Charge flows only | — | Merchant Pay API key (sent as x-api-key on cardSale requests). Required for cardSale(). Omit for tokenize-only integrations. |
vaultKey | string | ✅ | — | Vault API secret key — used to create sessions. Never expose in browser code. |
apiUrl | string | — | Ozura’s hosted API URL for the installed package | Pay API base URL override |
vaultUrl | string | — | Ozura’s hosted Vault URL for the installed package | Vault API base URL override (for createSession and revokeSession) |
timeoutMs | number | — | 30000 | Request timeout in milliseconds |
retries | number | — | 2 | Max retry attempts for 5xx / network errors on listTransactions and createSession. cardSale never retries. |
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().Methods
cardSale
Charge a tokenized card. Maps theCardSaleInput shape to the Pay 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 For stricter de-duplication, pass your own
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: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.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.createSession
Creates a short-lived payment session key from the vault. This is the server-side companion tosessionUrl / getSessionKey on the frontend SDK.
| Option | Type | Required | Description |
|---|---|---|---|
sessionId | string | — | The UUID the SDK generates and forwards through your session endpoint. Pass it here for vault-side correlation and audit. |
sessionLimit | number | null | — | Maximum card submissions this session accepts before the vault marks it consumed. Default: 3. Pass null to remove the cap (unlimited). Must match VaultOptions.sessionLimit on the client. |
maxProxyCalls | number | null | — | Maximum vault proxy calls. Omit for most integrations. Pass null to remove the cap. |
orderId | string | — | Your order ID — stored on the session for support lookups and transaction correlation. |
customerId | string | — | Your customer ID — stored on the session for support lookups. |
cartId | string | — | Your cart/basket ID — stored on the session for support lookups. |
metadata | Record<string, string> | — | Arbitrary key/value pairs stored on the session. Max 16 keys, 64-char keys, 256-char values. |
ttlSeconds | number | — | Session lifetime in seconds (60–1800). Default: 1800 (30 min). Shorten for flows where a tighter TTL is desirable. |
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 andsessionLimit are defence-in-depth layers — explicit revocation is the primary closure mechanism.
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
ThesessionUrl 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 (Request → Response). Use with Next.js App Router, Remix, Cloudflare Workers, etc.
sessionId from the JSON body, calls ozura.createSession(), and returns { sessionKey }.
createSessionMiddleware (Express / Connect)
Creates an Express-style middleware ((req, res) => void). Requires express.json() (or equivalent body-parser) to be registered before it.
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)
| Option | Type | Required | Description |
|---|---|---|---|
getAmount | (body) => string | Promise<string> | ✅ | Return the authoritative transaction amount as a decimal string (e.g. "49.00"). Source this from your own database — never trust the value the client sends. |
getCurrency | (body) => string | Promise<string> | — | Return the ISO 4217 currency code (e.g. "USD", "CAD"). Defaults to "USD". |
- Read
token,cvcSession, andbillingfrom 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 Pay API error: return
{ error: string }with the normalized error message and the appropriate HTTP status (4xx/5xx); 429 includes aRetry-Afterheader
Utilities
getClientIp
Extract the client IP address from a server request object. Works across frameworks:| Framework | How it reads IP |
|---|---|
| Express / Fastify | req.ip (requires app.set('trust proxy', true) behind a load balancer) |
| Next.js App Router | cf-connecting-ip → x-forwarded-for → x-real-ip |
| Raw Node.js | cf-connecting-ip → x-forwarded-for → x-real-ip → socket.remoteAddress |
What happens if
clientIpAddress is wrong or missing? The Pay 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 throwOzuraError 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.
| Status | Retried (listTransactions)? | createSession | cardSale |
|---|---|---|---|
| 5xx | ✅ Up to retries times | ✅ Up to retries times, exponential backoff | ❌ Never retried |
| Network error | ✅ Up to retries times | ✅ Up to retries times, exponential backoff | ❌ Never retried |
| 4xx | ❌ | ❌ | ❌ |
| 429 | ❌ Throw with retryAfter | ❌ | ❌ |
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.