Skip to main content

npm / yarn

npm install @ozura/elements
Pre-release builds (@next): The next dist-tag contains unreleased changes before they reach latest. It is intended for Ozura-coordinated testing only — production credentials will not work with it. If you have been given access to pre-release testing, contact ammar@ozura.com for the matching staging credentials and setup instructions.
npm install @ozura/elements@next
npm install @ozura/elements (no tag) always resolves latest@next has no impact on production installs.
Then import in your JavaScript or TypeScript:
import { OzVault, OzError } from '@ozura/elements';
For React components:
import {
  OzElements,
  useOzElements,
  OzCard,
  OzCardNumber,
  OzExpiry,
  OzCvv,
  OzBankCard,
  OzBankAccountNumber,
  OzBankRoutingNumber,
} from '@ozura/elements/react';
@ozura/elements/react requires React 17 or later as a peer dependency. If React is not already in your project, install it first:
npm install react react-dom
For server-side usage (Node.js, Deno, Bun):
import { Ozura, OzuraError, getClientIp, createCardSaleHandler, createSessionHandler } from '@ozura/elements/server';

CDN (Script Tag)

The Ozura CDN blocks localhost origins. If you load the CDN script on http://localhost, the browser will reject it with a CORS error (No 'Access-Control-Allow-Origin' header). The CDN is for deployed origins only. See Local Development below.
If you’re not using a bundler, load the UMD build directly from the Ozura CDN:
<script src="https://elements.ozura.com/oz-elements.umd.js"></script>
<script>
  const { OzVault } = window.OzElements;

  // Production: pubKey required (pk_live_… / pk_prod_…).
  // Test vault keys (from a Test project at ozuravault.com): omit pubKey.
  OzVault.create({
    pubKey:     'YOUR_PUB_KEY',
    sessionUrl: '/api/oz-session',
  }).then(vault => {
    // vault is ready
  });
</script>
Or use the ESM build with a native module script:
<script type="module">
  import { OzVault } from 'https://elements.ozura.com/oz-elements.esm.js';

  const vault = await OzVault.create({
    pubKey:     'YOUR_PUB_KEY',         // omit for test vault keys
    sessionUrl: '/api/oz-session',
  });
</script>

Local Development

The CDN blocks localhost. For local development you have two options depending on how you load the SDK: If your project uses a bundler (webpack, Vite, Next.js, etc.), @ozura/elements is imported directly from node_modules — no CDN involved, no workaround needed. The iframes load from elements.ozura.com at runtime, but that uses <iframe src> which is not subject to the same CORS restriction.
npm install @ozura/elements

CDN script tag on localhost

If you want to use the CDN <script> tag during local development, you need to serve the SDK file from the same origin as your page. Install the npm package and add a single route to your local server:
app.get('/oz-elements.esm.js', (_, res) =>
  res.sendFile('node_modules/@ozura/elements/dist/oz-elements.esm.js', { root: '.' })
);
Then change the <script src> in your HTML from:
<script src="https://elements.ozura.com/oz-elements.esm.js"></script>
to:
<script src="/oz-elements.esm.js"></script>
The iframes (/frame/tokenizer-frame.js, /frame/element-frame.js) still load from elements.ozura.com — they are fetched via <iframe src>, not fetch, so CORS does not apply.

TypeScript

The package ships with full TypeScript definitions — no @types package needed. Import types directly:
import type {
  OzVault,
  OzElement,
  OzError,
  VaultOptions,
  ElementOptions,
  ElementType,
  BankElementType,
  ElementStyleConfig,
  ElementChangeEvent,
  TokenizeOptions,
  TokenResponse,
  CardMetadata,
  BillingDetails,
  BillingAddress,
  BankTokenizeOptions,
  BankTokenResponse,
  BankAccountMetadata,
  FontSource,
  Appearance,
  AppearanceVariables,
  OzTheme,
  CardSaleRequest,
  CardSaleResponseData,
} from '@ozura/elements';
For server-side code:
import type {
  OzuraConfig,
  CreateSessionOptions,
  CreateSessionResult,
  CardSaleInput,
  CardSaleHandlerOptions,
  ListTransactionsInput,
  ListTransactionsResult,
} from '@ozura/elements/server';

// MintWaxKeyOptions and MintWaxKeyResult are deprecated aliases for
// CreateSessionOptions and CreateSessionResult — both still work.

Vault Project Types and the Pub Key

When you sign up at ozuravault.com, your first project is a test project by default. To create a production project, open the project dropdown in the top-left, select Create new project, and enable the Production switch. Test vault API key (test project): No pub key is required. You can omit pubKey entirely from OzVault.create(). This is the easiest way to get started — create an application in your test project and use that key for local development and testing. Production vault API key: A pub key is required and is tied to your registered domain. Production pub keys have domain restrictions and will not work on localhost. Contact ammar@ozura.com to obtain a production pub key for your domain.
If you’re testing with a production vault API key on localhost, OzVault.create() will fail due to domain restrictions — and without a try/catch, the failure is silent. Use a test vault API key (test project) for local development instead.
Frame updates are automatic and backward compatible. The SDK always loads iframe files from https://elements.ozura.com. Updates are delivered transparently — no npm update is required. frameBaseUrl is a local development option only — do not set it in production code.

Credentials

Which credentials do you need?
GoalRequired credentials
Tokenize card / bank data only (no charging)Vault API key (+ pub key for production vault keys)
Charge cards via OzuraPayAll four credentials
You need credentials from the Ozura Dashboard.
CredentialFormatWhere it livesRequired for
Vault pub keypk_live_… or pk_prod_…Frontend env var (safe to expose)Production vault keys only — omit for test/sandbox keys
Vault API keykey_…Server env var only — never in browserCreating sessions (all integrations)
OzuraPay API keyak_…Server env var onlyOzuraPay merchants (card charging)
Merchant IDozu_…Server env var onlyOzuraPay merchants (card charging)
If you are not routing payments through OzuraPay you only need the vault API key (backend), plus a pub key if you are using a production vault key.
Testing? Use a test vault key (no pub key needed) and fill fields with a test card number. See Test Credentials for card numbers and expected outcomes.
The vault API secret must never be sent to the browser. The SDK’s session mechanism exists specifically so your server can create a short-lived credential without exposing the secret. See the Server SDK for setup.

Quick Initialization

OzVault.create() is the public factory method — it sets up the vault and initializes the payment session. It resolves once the session is ready; you can create and mount elements immediately after. The vault is ready to tokenize once onReady fires (or vault.isReady === true).
onReady can fire while OzVault.create() is still pending. Vault initialization has two parallel steps — session fetch and iframe load. Either can finish first. If the iframe loads before the session resolves, onReady fires while await OzVault.create(...) is still in progress and vault is still undefined. Do not reference vault inside onReady. Declare any readiness flags before calling create() so they are in scope when the callback runs.
Always wrap OzVault.create() in a try/catch. If initialization fails (e.g. pub key blocked, session endpoint unreachable, backend error) the promise rejects. Without a catch, the failure is a silent unhandled rejection and the fields appear to hang.
import { OzVault, OzError } from '@ozura/elements';

let vault;
try {
  vault = await OzVault.create({
    pubKey:     'YOUR_PUB_KEY',         // omit for test vault keys
    sessionUrl: '/api/oz-session',
  });
} catch (err) {
  if (err instanceof OzError) {
    // err.errorCode: 'auth' | 'network' | 'timeout' | 'config' | ...
    console.error('Vault init failed:', err.message, err.errorCode);
  }
  // Render a fallback UI — payment fields are not available
  document.getElementById('payment-section').textContent =
    'Payment fields could not load. Please refresh and try again.';
}
sessionUrl is the simplest integration option — just pass your session endpoint path and the SDK handles everything. See Server SDK — Session route for the matching backend route. To provide custom headers or auth tokens, use getSessionKey instead:
let vault;
try {
  vault = await OzVault.create({
    pubKey: 'YOUR_PUB_KEY',             // omit for test vault keys
    getSessionKey: async (sessionId) => {
      const res = await fetch('/api/oz-session', {
        method:  'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
        body:    JSON.stringify({ sessionId }),
      });
      if (!res.ok) throw new Error(`Session endpoint returned ${res.status}`);
      const { sessionKey } = await res.json();
      return sessionKey;
    },
  });
} catch (err) {
  console.error('Vault init failed:', err);
}

OzVault.create() Options

OzVault.create(options: VaultOptions, signal?: AbortSignal): Promise<OzVault>
OptionTypeRequiredDefaultDescription
pubKeystring²Your Vault pub key
sessionUrlstring✅ ¹URL of your session endpoint — the simplest option
getSessionKey(sessionId: string) => Promise<string>✅ ¹Custom callback for session key retrieval (custom headers, auth, etc.)
fetchWaxKey(sessionId: string) => Promise<string>✅ ¹Deprecated — use sessionUrl or getSessionKey instead
frameBaseUrlstringhttps://elements.ozura.comOverride where iframe assets are served from. Local development only — do not set in production.
fontsFontSource[][]Custom web fonts to load inside iframes
appearanceAppearanceGlobal theme and CSS variable overrides
onLoadError() => voidCalled if the vault fails to load within loadTimeoutMs
loadTimeoutMsnumber10000Requires onLoadError to be set. Setting this without onLoadError is silently ignored.
onSessionRefresh() => voidCalled when the SDK silently refreshes the session during a tokenization attempt
onReady() => voidCalled once when the vault is ready (vanilla JS only; in React use useOzElements().ready)
sessionLimitnumber | null3Maximum card submissions per session before auto-refresh. Pass null to remove the cap. Must match sessionLimit in your server-side createSession call — see Server SDK → createSession.
debugbooleanfalseEnables [OzVault]-prefixed console.log output at every lifecycle event. Safe in production — no sensitive data is logged.
¹ Exactly one of sessionUrl, getSessionKey, or fetchWaxKey is required. ² pubKey is required when using a production vault API key. If your vault API key was created for the test/sandbox environment, you can omit pubKey entirely — see Pub Key for Local Development.
Keep sessionLimit in sync between client and server. If VaultOptions.sessionLimit on the client differs from the value passed to ozura.createSession() on your server, the proactive refresh timing will be off. The vault may reject a tokenization attempt before the client expects a refresh, causing a user-visible delay. Both default to 3 — only change one if you change the other.
The optional signal parameter is an AbortSignal for advanced teardown scenarios. The React OzElements provider handles this automatically — vanilla JS integrations rarely need it.

Content Security Policy (CSP)

If your site sets a Content-Security-Policy header, you need to allow the Ozura iframe and tokenizer origins. Without this the iframes are blocked before they load and OzVault.create() will reject. Add these directives to your CSP:
frame-src   https://elements.ozura.com;
connect-src https://elements.ozura.com;
If you use frameBaseUrl to point at a custom or staging deployment, substitute that origin for https://elements.ozura.com. If you load custom fonts via the fonts option (e.g. Google Fonts), add the font CDN origins too:
style-src  'self' https://fonts.googleapis.com;
font-src   'self' https://fonts.gstatic.com;
Full header example (Express):
app.use((_, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    [
      "default-src 'self'",
      "script-src  'self' 'nonce-YOURNONCE'",
      "frame-src   'self' https://elements.ozura.com",
      "connect-src 'self' https://elements.ozura.com",
      // Add the two lines below only if using fonts: [{ cssSrc: 'https://fonts.googleapis.com/...' }]
      "style-src   'self' https://fonts.googleapis.com",
      "font-src    'self' https://fonts.gstatic.com",
    ].join('; ')
  );
  next();
});
The connect-src entry covers requests made by the Ozura iframe for tokenization.

Next.js App Router

When using Next.js with the App Router (app/ directory), the vault and card fields require a client component boundary because they use browser APIs.
The examples below show a production setup with a pubKey. If you are integrating against a test vault key (from a Test project at ozuravault.com), omit pubKey from OzVault.create() and the <OzElements> provider — no pub key is required.

Client component wrapper

// app/checkout/CardForm.tsx
'use client';

import { useEffect, useRef } from 'react';
import { OzVault, OzError } from '@ozura/elements';

export function CardForm() {
  const cardNumberRef = useRef<HTMLDivElement>(null);
  const expiryRef     = useRef<HTMLDivElement>(null);
  const cvvRef        = useRef<HTMLDivElement>(null);

  useEffect(() => {
    let vault: OzVault | null = null;

    OzVault.create({
      pubKey:     process.env.NEXT_PUBLIC_VAULT_PUB_KEY!,
      sessionUrl: '/api/oz-session',
    })
      .then(v => {
        vault = v;
        v.createElement('cardNumber').mount(cardNumberRef.current!);
        v.createElement('expirationDate').mount(expiryRef.current!);
        v.createElement('cvv').mount(cvvRef.current!);
      })
      .catch(err => console.error('Vault init failed:', err));

    return () => { vault?.destroy(); };
  }, []);

  return (
    <>
      <div ref={cardNumberRef} />
      <div ref={expiryRef} />
      <div ref={cvvRef} />
    </>
  );
}
Or use the React provider — this is the recommended approach and handles lifecycle automatically:
// app/checkout/CardForm.tsx
'use client';

import { OzElements, OzCard, useOzElements } from '@ozura/elements/react';

function CheckoutForm() {
  const { createToken, ready } = useOzElements();
  return (
    <form onSubmit={async (e) => {
      e.preventDefault();
      const { token, cvcSession } = await createToken();
      // send token + cvcSession to your backend
    }}>
      <OzCard />
      <button type="submit" disabled={!ready}>Pay</button>
    </form>
  );
}

export function CardForm() {
  return (
    <OzElements
      pubKey={process.env.NEXT_PUBLIC_VAULT_PUB_KEY!}
      sessionUrl="/api/oz-session"
    >
      <CheckoutForm />
    </OzElements>
  );
}

Session route (app/api/oz-session/route.ts)

// app/api/oz-session/route.ts
import { Ozura, createSessionHandler } from '@ozura/elements/server';

const ozura = new Ozura({
  vaultKey: process.env.VAULT_API_KEY!,
});

export const POST = createSessionHandler(ozura);

Environment variables (.env.local)

NEXT_PUBLIC_VAULT_PUB_KEY=pk_live_...
VAULT_API_KEY=key_...
MERCHANT_API_KEY=ak_...
MERCHANT_ID=ozu_...
NEXT_PUBLIC_ prefix exposes the pub key to the browser bundle. All other credentials stay server-side only.

Production Checklist

Before you go live, confirm each of the following: Credentials
  • Production pub key registered to your domain (contact ammar@ozura.com)
  • VAULT_API_KEY set in server environment — never in browser code or committed to git
  • OzuraPay merchants: MERCHANT_API_KEY and MERCHANT_ID also set server-side
Session route
  • POST /api/oz-session (or your equivalent path) is deployed and reachable
  • Route uses createSessionHandler / createSessionMiddleware or manually calls ozura.createSession()
  • sessionLimit on the server matches sessionLimit in VaultOptions (both default to 3 — only change one if you change the other)
  • Session route is CSRF-safe (the SDK helpers enforce POST + Content-Type: application/json)
Security
  • CSP includes frame-src and connect-src for https://elements.ozura.com (see CSP above)
  • Charge route reads amount from your database, not from the request body
  • getClientIp used (or equivalent) when calling ozura.cardSale() so the OzuraPay API gets the real client IP
Error handling
  • OzVault.create() is wrapped in try/catch with a fallback UI
  • Payment form disables the submit button until vault.isReady and all field 'ready' events fire
  • vault.reset() is called on tokenization error so the customer can re-enter card data
  • frameBaseUrl is not set in production code (local development option only)
Testing
  • Tested end-to-end on localhost using a test vault API key (test project) — no pub key needed
  • If using a production vault API key: tested with the production pub key on your staging/preview domain before going live

Next Steps

Card Elements

Mount card number, expiry, and CVV fields.

Bank Elements

Mount account number and routing number fields.

React

OzElements provider and pre-built components.

Server SDK

Process payments and query transactions on your backend.