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

@nlpay/js

v1.0.5

Published

NLPay JavaScript SDK - Secure payment elements for card and ACH capture

Downloads

502

Readme

@nlpay/js

Secure, PCI-compliant payment elements for card and ACH capture. The SDK renders payment inputs inside isolated iframes, ensuring that sensitive cardholder data never touches your servers. Tokenization is handled entirely within the iframe, and your application receives only a token suitable for server-side API calls.


Table of Contents


Installation

npm / yarn

npm install @nlpay/js
yarn add @nlpay/js

Then import the default export:

import NLPay from '@nlpay/js';

CDN (UMD)

Include the script directly in your HTML. The SDK is exposed as window.NLPay.

<script
  src="https://js.nextlevelpayments.io/nlp.umd.js"
  integrity="sha384-6EWRjP7RKaeylMz6qUXJmnHsNx6/ca8i1k3gxzAObytsCBOSNQNuPLs2TZdYYiax"
  crossorigin="anonymous"
></script>

ES Module (CDN)

<script type="module">
  import NLPay from 'https://js.nextlevelpayments.io/nlp.es.js';
</script>

Quick Start

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

<script type="module">
  import NLPay from '@nlpay/js';

  // 1. Initialize the SDK with your public key
  NLPay.init({ publicKey: 'pk_live_xxxxxxxx' });

  // 2. Create a card element
  const card = NLPay.createElement('card');

  // 3. Mount it to the DOM
  card.mount('#card-element');

  // 4. Wait for the element to be ready
  card.on('ready', () => {
    console.log('Card element is ready');
  });

  // 5. Tokenize on form submission
  document.getElementById('pay-btn').addEventListener('click', async () => {
    try {
      const result = await NLPay.createToken(card);
      console.log('Token:', result.token);
      // Send result.token to your server to complete the payment
    } catch (err) {
      console.error('Tokenization failed:', err.error);
    }
  });
</script>

API Reference

NLPay.init(options)

Initialize the SDK. Must be called before creating any elements.

NLPay.init({
  publicKey: 'pk_live_xxxxxxxx',
  mode: 'production',
  fonts: ['Inter', 'Roboto Mono'],
});

Parameters

| Option | Type | Default | Description | |-------------|------------|----------------|--------------------------------------------------------| | publicKey | string | (required) | Your NLPay public API key | | mode | string | 'production' | Environment: 'local', 'staging', 'sandbox', or 'production' | | apiUrl | string | (auto) | Override the API URL for the selected mode | | cdnUrl | string | (auto) | Override the CDN URL where iframe files are hosted | | fonts | string[] | [] | Google Fonts to load inside the iframe |

Returns the NLPay instance (for chaining).

Throws if publicKey is not provided.


NLPay.createElement(type, options)

Create a payment element. Call init() first.

const card = NLPay.createElement('card', {
  styles: { colorPrimary: '#4F46E5' },
  hideZipCode: false,
  hideCardholderName: false,
});

const ach = NLPay.createElement('ach', {
  styles: { colorPrimary: '#4F46E5' },
  hideAccountType: false,
});

Parameters

| Parameter | Type | Description | |------------|----------|-----------------------------| | type | string | 'card' or 'ach' | | options | object | Optional configuration (see below) |

Element Options

| Option | Type | Default | Applies To | Description | |-----------------------|-----------|---------|-------------|------------------------------------------| | styles | object | {} | card, ach | Style overrides (see Styling) | | labels | object | {} | card, ach | Custom labels for form fields | | placeholders | object | {} | card, ach | Custom placeholder text for form fields | | hideZipCode | boolean | false | card, ach | Hide the zip/postal code field | | hideCardholderName | boolean | false | card | Hide the cardholder name field | | hideAccountType | boolean | false | ach | Hide the account type dropdown |

Returns a CardElement or AchElement instance.

Throws if init() has not been called or type is invalid.


element.mount(selector)

Mount the element into a DOM container. The SDK creates a secure iframe inside the target container.

card.mount('#card-element');

// Or pass a DOM element directly
const container = document.getElementById('card-element');
card.mount(container);

Parameters

| Parameter | Type | Description | |------------|-------------------------|--------------------------------------| | selector | string \| HTMLElement | A CSS selector or a DOM element |

Returns the element instance (for chaining).

Throws if the element is already mounted or the container is not found.


element.unmount()

Remove the element from the DOM, tear down the iframe, and clean up all event listeners.

card.unmount();

element.update(options)

Update the element's options after creation. Changes are applied immediately if the element is ready.

card.update({
  styles: { colorPrimary: '#DC2626' },
  labels: { cardNumber: 'Card #' },
  hideZipCode: true,
});

Accepts the same options as createElement (except type).


element.focus(field)

Programmatically focus a specific field inside the iframe.

card.focus('cardNumber');  // 'cardNumber', 'expiry', 'cvv', 'cardholderName', 'zipCode'

element.clear()

Clear all form fields inside the element.

card.clear();

element.on(event, callback)

Subscribe to element events. Returns an unsubscribe function.

const unsubscribe = card.on('change', (data) => {
  console.log('Valid:', data.complete);
});

// Later, remove the listener
unsubscribe();

See the Events section for all available events and their payloads.


NLPay.createToken(element)

Validate the element's form data and tokenize the payment information. The actual tokenization occurs inside the secure iframe -- sensitive data never reaches your page.

try {
  const result = await NLPay.createToken(card);
  // result.token  - the payment token
  // result.last4  - last 4 digits of the card number
  // result.cardType - detected card brand
} catch (err) {
  // err.error - error message string
  console.error(err.error);
}

Parameters

| Parameter | Type | Description | |-----------|-----------|--------------------------------------------| | element | Element | A mounted and ready CardElement or AchElement |

Returns Promise<object> -- resolves with token data or rejects with an error object.

Throws (rejects) if the element is not mounted or not ready.


NLPay.destroy()

Unmount all elements, remove all event listeners, and reset the SDK. Call this when the payment form is no longer needed.

NLPay.destroy();

After calling destroy(), you must call init() again before creating new elements.


Styling

Pass a styles object when creating an element to customize the appearance of the secure iframe inputs.

const card = NLPay.createElement('card', {
  styles: {
    fontFamily: 'Inter, sans-serif',
    fontSize: '16px',
    colorText: '#1E293B',
    colorPrimary: '#4F46E5',
    colorBorder: '#CBD5E1',
    colorError: '#DC2626',
    colorBackground: '#FFFFFF',
    borderRadius: '8px',
    inputHeight: '44px',
    spacing: '12px',
  },
});

Available Style Properties

| Property | Type | Default | Description | |-------------------|----------|-----------|-------------------------------------------------| | fontFamily | string | System default | Font family for all inputs | | fontSize | string | '14px' | Font size for input text | | colorText | string | '#1E293B' | Input text color | | colorPrimary | string | '#4F46E5' | Focus ring and accent color | | colorBorder | string | '#CBD5E1' | Input border color | | colorError | string | '#DC2626' | Error state text and border color | | colorBackground | string | '#FFFFFF' | Input background color | | borderRadius | string | '6px' | Border radius for inputs | | inputHeight | string | '40px' | Height of each input field | | spacing | string | '12px' | Vertical spacing between fields |

To load custom Google Fonts inside the iframe, pass them in init():

NLPay.init({
  publicKey: 'pk_live_xxxxxxxx',
  fonts: ['Inter', 'Roboto Mono'],
});

const card = NLPay.createElement('card', {
  styles: { fontFamily: 'Inter, sans-serif' },
});

Events

Subscribe to events with element.on(event, callback). Each call returns an unsubscribe function.

ready

Fired when the iframe has loaded and the element is ready to accept input.

card.on('ready', () => {
  console.log('Element is interactive');
});

Callback receives: no data.

change

Fired when the form state changes (field input, validation state).

card.on('change', (data) => {
  console.log(data.complete);  // boolean - all fields valid
  console.log(data.error);     // string | null - current validation error
  console.log(data.cardType);  // string | null - detected card brand (card only)
});

focus

Fired when any field inside the element gains focus.

card.on('focus', (data) => {
  console.log(data.field);  // string - the field that received focus
});

blur

Fired when any field inside the element loses focus.

card.on('blur', (data) => {
  console.log(data.field);  // string - the field that lost focus
});

error

Fired when a validation error or iframe-level error occurs.

card.on('error', (data) => {
  console.error(data.message);  // string - error description
  console.error(data.field);    // string | undefined - related field
});

Removing Listeners

// Option 1: Use the returned unsubscribe function
const unsub = card.on('change', handler);
unsub();

// Option 2: Use element.off()
card.off('change', handler);

Token Response

Card Element

On a successful createToken() call with a card element, the resolved object contains:

{
  token: 'tok_xxxxxxxxxxxxxxxx',  // Payment token for server-side use
  last4: '4242',                   // Last 4 digits of the card number
  cardType: 'visa',                // Card brand identifier
  // ...additional fields as returned by the API
}

ACH Element

For ACH elements, the resolved object contains:

{
  token: 'tok_xxxxxxxxxxxxxxxx',  // Payment token for server-side use
  // ...additional fields as returned by the API
}

Fallback

If the API returns a non-token response, the result will contain a paymentData object:

{
  paymentData: { ... }  // Raw payment data from the iframe
}

Error

On failure, the promise rejects with:

{
  error: 'Validation failed: card number is invalid'
}

Always wrap createToken() in a try/catch block.


React Integration

import { useRef, useEffect, useState } from 'react';
import NLPay from '@nlpay/js';

function PaymentForm({ publicKey, onToken }) {
  const cardRef = useRef(null);
  const elementRef = useRef(null);
  const [ready, setReady] = useState(false);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // Initialize the SDK
    NLPay.init({ publicKey });

    // Create and mount the card element
    const card = NLPay.createElement('card', {
      styles: {
        fontFamily: 'Inter, sans-serif',
        colorPrimary: '#4F46E5',
        borderRadius: '8px',
      },
    });

    card.mount(cardRef.current);
    elementRef.current = card;

    card.on('ready', () => setReady(true));
    card.on('change', (data) => setError(data.error || null));

    // Cleanup on unmount
    return () => {
      NLPay.destroy();
    };
  }, [publicKey]);

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!elementRef.current || !ready) return;

    setLoading(true);
    setError(null);

    try {
      const result = await NLPay.createToken(elementRef.current);
      onToken(result);
    } catch (err) {
      setError(err.error || 'Tokenization failed');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div ref={cardRef} />
      {error && <p style={{ color: '#DC2626' }}>{error}</p>}
      <button type="submit" disabled={!ready || loading}>
        {loading ? 'Processing...' : 'Pay'}
      </button>
    </form>
  );
}

export default PaymentForm;

ACH Variant

useEffect(() => {
  NLPay.init({ publicKey });

  const ach = NLPay.createElement('ach', {
    hideAccountType: false,
  });

  ach.mount(achRef.current);
  elementRef.current = ach;

  ach.on('ready', () => setReady(true));

  return () => NLPay.destroy();
}, [publicKey]);

Environments

The SDK supports four environments. The mode is set during init() and determines which API and CDN endpoints are used.

| Mode | API URL | CDN URL | |---------------|-----------------------------------------------|----------------------------------------------| | local | http://localhost:8081 | http://localhost:4600 | | staging | https://api-staging.nextlevelpayments.io | https://js-staging.nextlevelpayments.io | | sandbox | https://api-sandbox.nextlevelpayments.io | https://js-sandbox.nextlevelpayments.io | | production | https://api.nextlevelpayments.io | https://js.nextlevelpayments.io |

// Development
NLPay.init({ publicKey: 'pk_test_xxxxxxxx', mode: 'staging' });

// Production (default)
NLPay.init({ publicKey: 'pk_live_xxxxxxxx' });

You can also override the URLs directly:

NLPay.init({
  publicKey: 'pk_test_xxxxxxxx',
  mode: 'staging',
  apiUrl: 'https://custom-api.example.com',
  cdnUrl: 'https://custom-cdn.example.com',
});

Supported Card Types

The SDK automatically detects and validates the following card brands:

| Brand | Prefix Pattern | Accepted Lengths | CVV Length | |------------------|-----------------------|------------------|------------| | Visa | 4 | 13, 16, 19 | 3 | | Mastercard | 51-55, 22-27 | 16 | 3 | | American Express | 34, 37 | 15 | 4 | | Discover | 6011, 65, 644-649 | 16, 19 | 3 | | Diners Club | 300-305, 36, 38 | 14, 16, 19 | 3 | | JCB | 2131, 1800, 35 | 15, 16, 19 | 3 |

Card numbers are validated with the Luhn algorithm. Expiry dates are checked against the current date.


Publishing Updates

When you make changes to the SDK and need to publish a new version to npm:

1. Bump the version

# Patch release (1.0.0 -> 1.0.1) — bug fixes
npm version patch

# Minor release (1.0.0 -> 1.1.0) — new features, backward compatible
npm version minor

# Major release (1.0.0 -> 2.0.0) — breaking changes
npm version major

npm version automatically updates package.json and creates a git tag.

2. Publish to npm

npm publish --access public

The prepublishOnly script runs npm run build automatically before publishing, so the dist/ output is always fresh.

3. Update consumers

After publishing, update the dependency in each app that uses @nlpay/js:

npm update @nlpay/js

Current consumers:

  • nlp-portal — merchant admin payment modal
  • nlp-bill-pay-app — customer bill pay (BillPayContent, QuickPayModal)
  • nlp-payment-link — hosted payment page

Authentication

Publishing requires an npm account that belongs to the nlpay organization.

Recommended: enable 2FA on your npm account (npmjs.com > Account Settings > Two-Factor Authentication). This protects against unauthorized publishes. Once enabled, publish with:

npm publish --access public --otp YOUR_CODE

Alternatively, create a granular access token at npmjs.com > Access Tokens with "Bypass 2FA" enabled for CI/CD use. Store it as a secret and authenticate via .npmrc:

echo "//registry.npmjs.org/:_authToken=YOUR_TOKEN" > .npmrc
npm publish --access public

Never commit .npmrc with tokens to version control. The .gitignore should include .npmrc.


License

Proprietary. All rights reserved.