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

@verapay/verapay-js

v1.3.0

Published

Simple TypeScript SDK for Stripe payment processing

Readme

verapay-js

A simple, PCI-compliant TypeScript SDK for Stripe payment processing.

Features

  • PCI Compliant: Uses Stripe.js and Elements for secure tokenization
  • TypeScript First: Full TypeScript support with comprehensive type definitions
  • Customizable: Flexible styling and layout options
  • Validation: Built-in card validation and error handling
  • 3D Secure: Automatic 3D Secure (SCA) support
  • Extensible: Plugin architecture for pre/post payment hooks
  • Tree-shakable: ES Module format for optimal bundle sizes

Installation

npm install @verapay/verapay-js

Quick Start

Partners should integrate once with a single publishable key and pass merchantId dynamically per transaction:

import { VerapayClient } from '@verapay/verapay-js';

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',              // partner publishable key
  merchantId: currentMerchant.id,         // dynamic per order
  paymentData: { amount: order.total, currency: 'gbp' },
});

await verapay.mountPaymentForm('#payment-form');

For single-merchant integrations, pass a fixed merchant ID in the same config.

Then process the payment when your form is submitted:

const result = await verapay.processPayment({
  customerEmail: '[email protected]',
});

Backend-Initialized Flow (Recommended)

For more control, create the payment intent on your server and pass the clientSecret to the SDK. This is the recommended approach for custom checkout flows.

1. Initialize with Client Secret

const verapay = new VerapayClient({
  clientSecret: 'vpy_cs_test_...', // Obtained from your backend
});

await verapay.initialize();
await verapay.mountPaymentForm('#payment-form');

2. Confirm Payment

// All-in-one method
const result = await verapay.processPayment({
  customerEmail: '[email protected]',
});

// OR manual confirmation
const result = await verapay.confirmPayment();

Processing Payments

import { VerapayClient, ValidationError, PaymentError } from '@verapay/verapay-js';

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
});

If your Merchant account is associated with a Partner, include partnerFee — the fee you are taking from the payment, which you are responsible for calculating. It must not exceed the payment amount:

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp', partnerFee: 250 },
});

await verapay.mountPaymentForm('#payment-form');

try {
  const result = await verapay.processPayment({
    customerEmail: '[email protected]',
  });

  console.log('Payment succeeded:', result);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation error:', error.details.formattedMessage);
  } else if (error instanceof PaymentError) {
    console.error('Payment failed:', error.details.formattedMessage);
  }
}

Test Mode

The SDK automatically detects the environment from your API key prefix — no flags needed:

| Key prefix | Backend | Stripe key | |---|---|---| | vpy_pk_test_... | test-api.verapay.app | Stripe test publishable key | | vpy_pk_live_... | api.verapay.app | Stripe live publishable key |

Legacy vpy_test_... keys remain supported for backwards compatibility but are deprecated and will be removed in the next major release.

// Test mode — inferred from the key
const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
});

When using a test key:

  • The SDK connects to the Verapay Test environment
  • Stripe is initialised with the Test publishable key — no real charges are made
  • You must use Merchant IDs created in the Test environment; attempting to access a live merchant with a test key returns a 403 Forbidden error

Environment Mismatch

The backend strictly enforces environment isolation. If the key environment does not match the merchant or payment intent environment, the request is rejected:

try {
  await verapay.initialize();
} catch (error) {
  if (error instanceof PaymentError && error.details.code === 'FORBIDDEN') {
    // Key environment does not match merchant environment
    // Ensure your API key and merchantId are from the same environment
  }
}

Common cause: Using a vpy_pk_test_... key with a merchantId that was created in the live environment (or vice versa).

Going Live

To switch from test to live mode:

  1. Obtain a live publishable key from your Verapay dashboard (vpy_pk_live_...)
  2. Replace the apiKey in your config — no other code changes are needed:
// Before (test mode)
const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'test-merchant-uuid',
  paymentData: { amount: 5000, currency: 'gbp' },
});

// After (live mode)
const verapay = new VerapayClient({
  apiKey: 'vpy_pk_live_...',
  merchantId: 'live-merchant-uuid',   // must be a live-environment merchant
  paymentData: { amount: 5000, currency: 'gbp' },
});

Merchant IDs are environment-specific. You must obtain the corresponding live merchantId from your Verapay dashboard. A test merchantId will not work with a live key.

  1. Remove any apiUrlOverride from your config — the correct endpoint is automatically derived from the key prefix
  2. Ensure your backend has STRIPE_PUBLISHABLE_KEY (live) and SANDBOX_STRIPE_PUBLISHABLE_KEY (test) both configured

Partner Integration

Partners integrate with a single publishable key and pass merchantId dynamically per transaction:

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',           // partner's publishable key
  merchantId: currentMerchant.id,      // set dynamically from your order logic
  paymentData: { amount: order.total, currency: 'gbp' },
});

Local Development

Use apiUrlOverride to point at a locally running backend:

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
  apiUrlOverride: 'http://localhost:8083',
});

Advanced Usage

Custom Elements Layout

Mount individual card elements for a custom layout:

<div id="card-number"></div>
<div id="card-expiry"></div>
<div id="card-cvc"></div>
const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
});

await verapay.mountCardNumber('#card-number');
await verapay.mountCardExpiry('#card-expiry');
await verapay.mountCardCvc('#card-cvc');

Address Collection

Mount an address element for billing or shipping:

await verapay.mountAddress('#billing-address', 'billing');
await verapay.mountAddress('#shipping-address', 'shipping');

Styling the Payment Form and Address Element

When using mountPaymentForm or mountAddress, pass an appearance object to customise their look. See Stripe's Appearance API for the full set of options.

Note: Styling is split across two APIs due to a Stripe limitation. mountPaymentForm and mountAddress use newer Stripe elements that support the appearance API. mountCardNumber, mountCardExpiry, and mountCardCvc use older Stripe element types that predate appearance and only support per-element style configuration via elementsConfig.

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
  appearance: {
    theme: 'stripe',
    variables: {
      colorPrimary: '#6200ee',
      fontFamily: 'Inter, sans-serif',
    },
  },
});

Styling Individual Form Elements

This section applies only when mounting individual form elements (mountCardNumber, mountCardExpiry, mountCardCvc). It does not apply to mountPaymentForm.

The SDK ships with a default style for card fields that works out of the box — no configuration required. If you want to match your own design, pass elementsConfig to override specific properties.

Default appearance

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
  // no elementsConfig needed — sensible defaults are applied automatically
});

Overriding styles

Pass only the properties you want to change. Your values are merged with the defaults, so unspecified properties are preserved.

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
  elementsConfig: {
    style: {
      base: {
        color: '#1a1a1a',
        fontSize: '14px',
      },
      complete: {
        color: '#30a46c',
      },
      invalid: {
        color: '#ff4d4f',
      },
    },
    placeholders: {
      cardNumber: '1234 1234 1234 1234',
      cardExpiry: 'MM / YY',
      cardCvc: 'CVC',
    },
    iconStyle: 'default', // 'default' | 'solid' — defaults to 'solid'
    showIcon: true,       // defaults to true
  },
});

Container styling

The SDK mounts Stripe Elements into whichever div selectors you provide. Style those containers however you like using your own CSS — there are no SDK-specific class names.

Locale

By default Stripe Elements renders in the browser's detected language. Pass locale to override this:

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
  locale: 'fr',
});

Use 'auto' to explicitly opt in to Stripe's automatic detection, or any IETF language tag supported by Stripe Elements (e.g. 'en', 'de', 'ja').

Custom fonts

Stripe Elements run inside sandboxed iframes and cannot access fonts loaded on your page. To use a custom font, pass it via fonts and reference it in elementsConfig.style.base.fontFamily:

const verapay = new VerapayClient({
  apiKey: 'vpy_pk_test_...',
  merchantId: 'your-merchant-id',
  paymentData: { amount: 5000, currency: 'gbp' },
  fonts: [
    { cssSrc: 'https://fonts.googleapis.com/css?family=Inter' },
  ],
  elementsConfig: {
    style: {
      base: { fontFamily: 'Inter, sans-serif' },
    },
  },
});

Error Handling

import {
  VerapayError,
  ValidationError,
  PaymentError,
  ErrorFormatter,
} from '@verapay/verapay-js';

try {
  await verapay.processPayment(request);
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.details.field);            // e.g., 'amount'
    console.log(error.details.formattedMessage); // user-friendly message
  } else if (error instanceof PaymentError) {
    console.log(error.details.code);             // e.g., 'CARD_DECLINED'
    console.log(error.details.stripeCode);       // original Stripe code
  }

  const userMessage = ErrorFormatter.formatForUser(error);
  alert(userMessage);
}

TypeScript Support

import type {
  VerapayConfig,
  ProcessPaymentRequest,
  PaymentResult,
  ElementsConfig,
  CardValidation,
  ErrorCode,
} from '@verapay/verapay-js';

Extension Architecture

Use extensions to hook into the payment lifecycle:

import type { PaymentExtension } from '@verapay/verapay-js';

const analyticsExtension: PaymentExtension = {
  name: 'analytics',
  version: '1.0.0',

  async beforePayment(request) {
    // track payment attempt
    return request;
  },

  async afterPayment(result) {
    // track payment success
  },

  async onPaymentError(error) {
    // track payment failure
  },
};

verapay.addExtension(analyticsExtension);

Testing with Stripe

Use test cards (see below) to simulate payment outcomes. Never enable this in production.

Use Stripe's test cards:

  • Successful payment: 4242 4242 4242 4242
  • 3D Secure required: 4000 0027 6000 3184
  • Declined: 4000 0000 0000 0002

Full list of test cards

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Security & PCI Compliance

This SDK maintains PCI compliance by:

  1. Never touching raw card data: All card input is handled by Stripe Elements (isolated iframes)
  2. Tokenization: Card data is tokenized by Stripe.js before reaching your code
  3. No storage: SDK never stores or transmits raw card details

Your application needs to:

  • Use HTTPS in production
  • Follow Stripe's security best practices

API Reference

VerapayClient

Constructor

new VerapayClient(config: VerapayConfig)

| Property | Type | Required | Description | |---|---|---|---| | apiKey | string | No* | Your Verapay publishable key (vpy_pk_test_... or vpy_pk_live_...). Required for frontend-initialized flows. | | clientSecret | string | No* | The client secret of a Payment Intent created on your backend. Required for backend-initialized flows. | | merchantId | string | No | Merchant ID. Required for frontend-initialized single-merchant integrations. | | paymentData | PaymentData | No* | Amount, currency, and optional Partner fee. Required for frontend-initialized flows. |

| appearance | Appearance | No | Styles mountPaymentForm and mountAddress elements | | locale | StripeElementLocale | No | Locale for Stripe Elements | | fonts | StripeElementsOptions['fonts'] | No | Custom fonts for Stripe Elements | | elementsConfig | ElementsConfig | No | Styles mountCardNumber, mountCardExpiry, and mountCardCvc elements | | apiUrlOverride | string | No | Override the backend URL. For local development only (e.g. http://localhost:8083) — never use in production. |

PaymentData

| Property | Type | Required | Description | |---|---|---|---| | amount | number | Yes | Amount in the smallest currency unit (e.g., pence for GBP) | | currency | string | Yes | ISO 4217 currency code (e.g., 'gbp') | | partnerFee | number | No | Your fee in the smallest currency unit, taken from the payment amount. Only applicable if your Merchant account is associated with a Partner — ignored otherwise. You are responsible for calculating this value. Must not exceed amount. |

Methods

| Method | Returns | Description | |---|---|---| | initialize() | Promise<void> | Initialize the SDK (required when using clientSecret) | | mountPaymentForm(selector) | Promise<void> | Mount the Stripe Payment Element | | mountCardNumber(selector) | Promise<void> | Mount individual card number field | | mountCardExpiry(selector) | Promise<void> | Mount individual expiry date field | | mountCardCvc(selector) | Promise<void> | Mount individual CVC field | | mountAddress(selector, mode) | Promise<void> | Mount address element ('billing' or 'shipping') | | confirmPayment() | Promise<PaymentResult> | Confirm a backend-initialized payment | | retrievePaymentIntent() | Promise<PaymentResult> | Retrieve the current status of the payment intent | | processPayment(request) | Promise<PaymentResult> | Process the payment (works for both flow types) | | addExtension(extension) | void | Register a payment lifecycle extension |

ProcessPaymentRequest

| Property | Type | Required | Description | |---|---|---|---| | customerEmail | string | Yes | Customer email address | | description | string | No | Payment description | | metadata | Record<string, string> | No | Arbitrary key-value metadata | | customerFirstName | string | No | Customer first name | | customerLastName | string | No | Customer last name |

PaymentResult

| Property | Type | Description | |---|---|---| | success | boolean | Whether the payment succeeded | | status | 'succeeded' \| 'processing' \| 'failed' | Payment status | | paymentIntentId | string | Stripe PaymentIntent ID | | error | string | Error message if the payment failed |

License

Copyright © Lopay Ltd. All rights reserved.

This software is proprietary and confidential. Unauthorised copying, distribution, or use of this software, in whole or in part, is strictly prohibited without prior written permission from Lopay Ltd.

Support

For issues and questions, contact the Lopay engineering team.