npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cubepay/radiumone-js

v1.0.0-beta.8

Published

RadiumOne Elements — PCI-compliant card input fields (core SDK)

Readme

@cubepay/radiumone-js

PCI-compliant card input fields for your checkout page. RadiumOne Elements renders secure, iframe-isolated card inputs that encrypt card data client-side — your server never sees raw card numbers. Merchants qualify for PCI DSS SAQ A (the simplest compliance level).

How It Works

Your Page                          RadiumOne (js.radiumone.io)
+------------------------+         +---------------------------+
|  Checkout form         |         |  Sandboxed iframe         |
|  +------------------+  |         |  +---------------------+  |
|  | Card Element     |--+- mount -+->| Card input fields   |  |
|  +------------------+  |         |  +---------------------+  |
|                        |         |                           |
|  elements.submit() ----+---------+-> Encrypt -> Bind API     |
|                        |         |   (card data stays here)  |
|  <-- { token, brand } -+---------+-- Return token            |
+------------------------+         +---------------------------+
         |
         v
   Send token to your
   server for payment

Card data is encrypted as JWE (RSA-OAEP-256 + A256GCM) inside the iframe and sent directly to the tokenization API. Your page only receives a one-time token.

Installation

CDN (recommended for vanilla JS)

<script src="https://js.radiumone.io/elements/v1/radiumone.min.js"></script>

npm (recommended for bundlers/frameworks)

npm install @cubepay/radiumone-js
# or
pnpm add @cubepay/radiumone-js
# or
yarn add @cubepay/radiumone-js

For React, see @cubepay/react-radiumone-js instead.

Quick Start

1. Get your publishable key

Sign up at radiumone.io to get your API keys:

  • Publishable key (r1pk_prod_* or r1pk_test_*) — used client-side in the browser
  • Secret key (r1sk_prod_* or r1sk_test_*) — used server-side only, never expose in frontend code

2. Create a session (server-side)

Before collecting card details, create a session from your server:

curl -X POST https://api.radiumone.io/v1/sessions \
  -H "Authorization: Bearer r1sk_prod_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{"amount": 2500, "currency": "usd"}'

Response:

{
  "id": "sess_abc123",
  "secret": "sess_secret_xyz789"
}

Pass id and secret to your frontend.

3. Collect card details (client-side)

CDN usage:

<div id="card-element"></div>
<button id="pay-btn" disabled>Pay $25.00</button>

<script src="https://js.radiumone.io/elements/v1/radiumone.min.js"></script>
<script>
  const radiumone = RadiumOneSDK.RadiumOne.init('r1pk_prod_your_key');
  const elements = radiumone.elements();
  const card = elements.create('card');
  card.mount('#card-element');

  card.on('change', (event) => {
    document.getElementById('pay-btn').disabled = !event.complete;
  });

  document.getElementById('pay-btn').addEventListener('click', async () => {
    const result = await elements.submit(sessionId, sessionSecret);
    // Send result.token to your server to complete the payment
    await fetch('/api/charge', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token: result.token }),
    });
  });
</script>

npm usage:

import { loadRadiumOne } from '@cubepay/radiumone-js';

const radiumone = await loadRadiumOne('r1pk_prod_your_key');
if (!radiumone) throw new Error('Failed to load SDK');

const elements = radiumone.elements();
const card = elements.create('card');
card.mount('#card-element');

card.on('change', (event) => {
  submitButton.disabled = !event.complete;
  if (event.error) showError(event.error.message);
});

// When the user clicks "Pay"
const result = await elements.submit(sessionId, sessionSecret);
console.log(result.token);  // "tok_abc123"
console.log(result.lastFour);  // "1111"
console.log(result.cardBrand); // "visa"

API Reference

RadiumOne.init(publishableKey, options?)

Initialize the SDK. Used with CDN script tag.

| Param | Type | Description | |-------|------|-------------| | publishableKey | string | Your publishable key (r1pk_prod_* or r1pk_test_*) | | options.locale | string | Locale for labels/errors (default: 'en') |

Returns a RadiumOne instance.

loadRadiumOne(publishableKey, options?)

Async loader for npm users. Same params as init(). Returns Promise<RadiumOne | null> (null during SSR).

import { loadRadiumOne } from '@cubepay/radiumone-js';
const radiumone = await loadRadiumOne('r1pk_prod_your_key');

radiumone.elements(options?)

Create an Elements instance for managing card inputs.

| Param | Type | Description | |-------|------|-------------| | options.appearance | AppearanceOptions | Theme and styling (see Appearance) | | options.locale | string | Override locale for this Elements instance |

elements.create(type, options?)

Create a card input element.

| Param | Type | Description | |-------|------|-------------| | type | 'card' \| 'cardNumber' \| 'cardExpiry' \| 'cardCvv' | Element type | | options.showIcon | boolean | Show card brand icon (default: true) | | options.autoAdvance | boolean | Auto-advance to next field on completion (default: true) |

element.mount(target)

Mount the element to a DOM container.

card.mount('#card-element');       // CSS selector
card.mount(document.getElementById('card-element')); // HTMLElement

element.on(event, handler)

Subscribe to element events.

card.on('change', (event) => { /* ... */ });
card.on('ready', (event) => { /* ... */ });
card.on('focus', (event) => { /* ... */ });
card.on('blur', (event) => { /* ... */ });

element.unmount() / element.destroy()

Remove the element from DOM. unmount() allows re-mounting; destroy() is permanent.

elements.submit(sessionId, sessionSecret)

Submit all card fields — encrypts card data and tokenizes it.

const result = await elements.submit(sessionId, sessionSecret);

Returns a BindResult:

| Field | Type | Description | |-------|------|-------------| | token | string | One-time payment token — send this to your server | | bin | string | First 6 digits of card number | | lastFour | string | Last 4 digits of card number | | cardBrand | string | Detected brand (visa, mastercard, amex, etc.) | | sessionId | string | Session ID used | | status | string | Bind status | | cvvProvided | boolean | Whether CVV was provided |

elements.getState()

Get the current state of all fields (async — queries iframes for live state).

const state = await elements.getState();
console.log(state.complete); // true if all fields are filled and valid
console.log(state.errors);   // array of validation errors

elements.destroy()

Destroy all elements and clean up iframes.

Events

change

Fired when the field value, validity, or completion state changes.

card.on('change', (event) => {
  event.elementType; // 'card' | 'cardNumber' | 'cardExpiry' | 'cardCvv'
  event.empty;       // true if field is empty
  event.complete;    // true if field value is complete and valid
  event.valid;       // true if current value passes validation
  event.brand;       // 'visa' | 'mastercard' | 'amex' | ... (cardNumber/card only)
  event.error;       // { code, message, field } or null
});

ready

Fired when the iframe is loaded and the field is interactive.

focus / blur

Fired when the field receives/loses focus.

cardTypeChange (cardNumber only)

Fired when the detected card brand changes as the user types.

cardNumber.on('cardTypeChange', (event) => {
  event.brand;     // 'visa' | 'mastercard' | 'amex' | ...
  event.binLength; // number of digits used for detection
});

Error Handling

All SDK errors are instances of ElementsError:

import { ElementsError } from '@cubepay/radiumone-js';

try {
  const result = await elements.submit(sessionId, sessionSecret);
} catch (err) {
  if (err instanceof ElementsError) {
    console.error(err.type);    // 'validation_error' | 'api_error' | 'network_error' | ...
    console.error(err.code);    // e.g. 'required', 'bind:failed'
    console.error(err.message); // Human-readable message
    console.error(err.field);   // Which field caused the error (if applicable)
  }
}

| Error Type | When | |------------|------| | validation_error | Required fields empty, invalid card number, expired card | | api_error | Bind API returned an error (invalid session, declined) | | network_error | Network failure or request timeout | | encryption_error | Card data encryption failed | | load_error | SDK failed to load or iframe timed out |

Split Fields

Use separate inputs for card number, expiry, and CVV when you need individual control:

const cardNumber = elements.create('cardNumber');
const cardExpiry = elements.create('cardExpiry');
const cardCvv = elements.create('cardCvv');

cardNumber.mount('#card-number');
cardExpiry.mount('#card-expiry');
cardCvv.mount('#card-cvv');

// Detect card brand as user types
cardNumber.on('cardTypeChange', (event) => {
  brandIcon.src = `/icons/${event.brand}.svg`;
});

// Track per-field errors
cardNumber.on('change', (e) => { numberError.textContent = e.error?.message ?? ''; });
cardExpiry.on('change', (e) => { expiryError.textContent = e.error?.message ?? ''; });
cardCvv.on('change', (e) => { cvvError.textContent = e.error?.message ?? ''; });

Tab navigation between split fields is handled automatically.

Appearance

Customize card fields to match your checkout design.

Themes

Three built-in themes:

const elements = radiumone.elements({
  appearance: { theme: 'default' },  // Bordered inputs (default)
});

const elements = radiumone.elements({
  appearance: { theme: 'flat' },     // Underline-only inputs
});

const elements = radiumone.elements({
  appearance: { theme: 'minimal' },  // Subtle background, no border
});

Variables

Override individual design tokens:

const elements = radiumone.elements({
  appearance: {
    theme: 'default',
    variables: {
      colorPrimary: '#0070f0',         // Focus border, icons
      colorBackground: '#ffffff',      // Input background
      colorText: '#1a1a1a',            // Input text
      colorTextPlaceholder: '#9ca3af', // Placeholder text
      colorDanger: '#dc2626',          // Error states
      colorSuccess: '#16a34a',         // Valid states
      fontFamily: 'system-ui, sans-serif',
      fontSize: '16px',
      fontWeight: '400',
      borderRadius: '6px',
      borderWidth: '1px',
      borderColor: '#d1d5db',
      focusBorderColor: '#0070f0',
      focusBoxShadow: '0 0 0 3px rgba(0, 112, 240, 0.15)',
      colorScheme: 'light',           // 'light' | 'dark' | 'auto'
    },
  },
});

CSS Rules

For fine-grained control, target specific element states:

const elements = radiumone.elements({
  appearance: {
    rules: {
      '.Input': { padding: '14px 16px', lineHeight: '1.5' },
      '.Input--focus': { borderColor: '#6366f1' },
      '.Input--invalid': { borderColor: '#ef4444' },
      '.Input--complete': { borderColor: '#22c55e' },
      '.Input--disabled': { opacity: '0.5' },
      '.Label': { fontSize: '14px', fontWeight: '500' },
      '.Error': { color: '#ef4444', fontSize: '13px' },
      '.Icon': { width: '24px' },
    },
  },
});

Dynamic Updates

Update appearance after initialization without destroying iframes:

elements.update({
  appearance: {
    variables: { colorPrimary: '#6366f1' },
  },
});

CSP Configuration

Add to your Content Security Policy headers:

script-src https://js.radiumone.io;
frame-src https://js.radiumone.io;

You do not need connect-src for our API — the tokenization call happens from inside the iframe (our origin), not from your page.

Browser Support

| Browser | Minimum Version | |---------|----------------| | Chrome / Edge | 90+ | | Firefox | 90+ | | Safari | 14+ | | Samsung Internet | 15+ |

Requires HTTPS (secure context) for Web Crypto API.

Test Cards

Use these with publishable keys starting with r1pk_test_:

| Number | Brand | Result | |--------|-------|--------| | 4111 1111 1111 1111 | Visa | Success | | 5500 0000 0000 0004 | Mastercard | Success | | 3782 822463 10005 | Amex | Success | | 6011 0000 0000 0004 | Discover | Success | | 4000 0000 0000 0002 | Visa | Declined |

Security

  • Card data never leaves the iframe as plaintext
  • JWE encryption (RSA-OAEP-256 + A256GCM) via Web Crypto API
  • Iframe isolated from merchant page by cross-origin boundary
  • CSS property allowlist prevents data exfiltration attacks
  • credentials: 'omit' on all API calls from iframe
  • Runtime script attestation for tamper detection
  • Nonce-based postMessage protocol prevents replay attacks

TypeScript

Full type definitions are included. Import types as needed:

import type {
  ChangeEvent,
  BindResult,
  CardBrand,
  ElementsError,
  AppearanceOptions,
} from '@cubepay/radiumone-js';

License

Proprietary. See LICENSE.md in the published package for details.