Skip to main content
Bank elements let you collect account numbers and routing numbers in your own form without those values ever appearing in your JavaScript or reaching your server. Each field is an isolated iframe; Ozura handles tokenization securely.
OzuraPay does not currently support bank account payments. The bank token is for use with your own ACH-capable payment processor via the Proxy.

Field Types

typeDescription
accountNumber4–17 digit bank account number. Rendered as a password-type input to prevent shoulder surfing.
routingNumber9-digit US ABA routing number. Validated against the standard ABA checksum algorithm.

Creating and Mounting

import { OzVault, OzError, createFetchWaxKey } from '@ozura/elements';

const vault = await OzVault.create({
  pubKey:      'YOUR_PUB_KEY',
  fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
});

const accountEl = vault.createBankElement('accountNumber');
const routingEl = vault.createBankElement('routingNumber');

accountEl.mount('#account-number');
routingEl.mount('#routing-number');
With options:
const style = {
  base:        { fontSize: '15px', color: '#1a1a2e', padding: '12px 14px' },
  focus:       { caretColor: '#6366f1' },
  placeholder: { color: '#94a3b8' },
};

const accountEl = vault.createBankElement('accountNumber', { style });
const routingEl = vault.createBankElement('routingNumber', { style });

createBankElement Options

vault.createBankElement(type: BankElementType, options?: ElementOptions): OzElement
OptionTypeDescription
styleElementStyleConfigPer-element style overrides. See Styling.
placeholderstringPlaceholder text shown inside the field
disabledbooleanMount the field in a disabled state
loadTimeoutMsnumberTimeout (ms) before loaderror fires for this element. Default: 10000.

Tokenizing

Call createBankToken() after the user has filled both fields. firstName and lastName are required — they are included in the vault record alongside the account data.
document.getElementById('submit-btn').addEventListener('click', async (e) => {
  e.preventDefault();
  const firstName = document.getElementById('first-name').value.trim();
  const lastName  = document.getElementById('last-name').value.trim();

  try {
    const { token, bank } = await vault.createBankToken({ firstName, lastName });

    // token                  — vault token, pass to your backend
    // bank.last4             — last 4 digits of account number (safe to display)
    // bank.routingNumberLast4 — last 4 digits of routing number (safe to display)

    await fetch('/api/ach', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token }),
    });
  } catch (err) {
    if (err instanceof OzError) {
      console.error(err.message, err.errorCode);
    }
  }
});

createBankToken options

vault.createBankToken(options: BankTokenizeOptions): Promise<BankTokenResponse>
OptionTypeRequiredDescription
firstNamestringAccount holder first name
lastNamestringAccount holder last name

BankTokenResponse

{
  token: string;     // vault token — pass to your backend
  bank?: {
    last4:              string; // last 4 digits of account number
    routingNumberLast4: string; // last 4 digits of routing number
  };
}
bank.routingNumberLast4 is the last 4 digits of the routing number — sufficient for display and reconciliation. The full routing number is never exposed to your JavaScript.

Events

Bank elements emit the same events as card elements:
accountEl.on('change', ({ complete, valid, error }) => {
  const wrap   = document.getElementById('account-wrap');
  const errMsg = document.getElementById('account-error');

  wrap.classList.toggle('complete', complete && valid);
  wrap.classList.toggle('invalid',  !valid && error != null);
  errMsg.textContent = error ?? '';
});

accountEl.on('focus', () => document.getElementById('account-wrap').classList.add('focused'));
accountEl.on('blur',  () => document.getElementById('account-wrap').classList.remove('focused'));

Error messages

FieldErrorCondition
accountNumber"Invalid account number"Not 4–17 digits
routingNumber"Invalid routing number"Not 9 digits or fails ABA checksum

Gating the submit button

Gate on vault readiness, field iframe readiness, and field completeness:
let vaultReady = false;
const fieldReady  = { accountNumber: false, routingNumber: false };
const fieldFilled = { accountNumber: false, routingNumber: false };

function updateButton() {
  document.getElementById('submit-btn').disabled =
    !(vaultReady &&
      fieldReady.accountNumber && fieldReady.routingNumber &&
      fieldFilled.accountNumber && fieldFilled.routingNumber);
}

const vault = await OzVault.create({
  pubKey:      'YOUR_PUB_KEY',
  fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
  onReady: () => { vaultReady = true; updateButton(); },
});

accountEl.on('ready', () => { fieldReady.accountNumber = true; updateButton(); });
routingEl.on('ready', () => { fieldReady.routingNumber = true; updateButton(); });

accountEl.on('change', ({ complete, valid }) => {
  fieldFilled.accountNumber = complete && valid;
  updateButton();
});

routingEl.on('change', ({ complete, valid }) => {
  fieldFilled.routingNumber = complete && valid;
  updateButton();
});

Displaying Account Info After Tokenization

The bank.last4 and bank.routingNumberLast4 values returned by createBankToken() are safe to display to the user as confirmation:
const { token, bank } = await vault.createBankToken({ firstName, lastName });

if (bank) {
  document.getElementById('confirm-text').textContent =
    `Account ending in ${bank.last4} • Routing ending in ${bank.routingNumberLast4}`;
}

Validation Details

Account number

  • Accepts 4–17 digits
  • All non-digit characters are stripped as the user types
  • Rendered as a password field (dots, not plain text) to prevent shoulder surfing

Routing number

  • Must be exactly 9 digits
  • Validated against the standard US ABA routing number checksum
  • Invalid routing numbers are flagged immediately when the 9th digit is entered

Accessing an Existing Bank Element

const existing = vault.getBankElement('accountNumber');
// returns the OzElement or null if not yet created

Teardown

// Destroy the vault and all its iframes when the component unmounts
vault.destroy();

Next Steps

Proxy

Forward the bank token to your ACH processor.

Styling

Customize colors, fonts, and states.

React Components

Use OzBankCard, OzBankAccountNumber, and OzBankRoutingNumber in React.

Error Handling

Handle errors from createBankToken().