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

@shoppexio/checkout-js

v0.7.0

Published

Official JavaScript SDK for Shoppex checkout: embed the hosted checkout modal, or build a fully custom (headless) checkout via @shoppexio/checkout-js/headless

Readme

@shoppexio/checkout-js

Official JavaScript SDK for embedding Shoppex hosted checkout as a modal on any website.

Install

npm install @shoppexio/checkout-js

Vanilla JS

import { open, on } from '@shoppexio/checkout-js';

on((event) => {
  if (event.type === 'success') console.log('Paid', event.payload?.invoiceId);
});

document.querySelector('#buy')?.addEventListener('click', () => {
  void open({
    shopId: 'your-shop',
    items: [{ productId: 'PROD_123', quantity: 1 }],
    theme: 'auto',
    locale: 'de',
    returnUrl: 'https://your-site.com/thanks',
  });
});

Events

on(listener) receives every checkout event and returns an unsubscribe function.

| event.type | event.payload | When | |---|---|---| | ready | — | A checkout step finished mounting. Fires again for each step. | | invoice-created | { invoiceId } | An invoice exists. Not paid yet. | | redirect / external-redirect | { url } | The buyer is going to a payment provider. | | success | { invoiceId, completionAccessGrant } | Paid. Fire your conversion here. | | error | { error } | Checkout failed — reported by the checkout, or by the modal when the checkout could not be started at all. | | close | { completed, invoiceId } | The modal closed — by the close button, Escape, the backdrop, close(), or the checkout itself. completed: false means the buyer left without paying. | | resize | { height, width } | Handled by the modal; rarely useful. | | rewards-updated | { invoiceId, rewards } | Rewards were recalculated. | | setLocale | { locale } | The buyer switched language inside the checkout. |

rewards-applied, rewards-error, and style-update are typed and forwarded but are not emitted by a live checkout today.

close and error reach this SDK on two channels — the checkout's own messages and the modal script's page events — because the modal script is served unversioned and can be an older cached build than this package. Each occurrence is reported once regardless: close fires exactly once per opened modal, and an error the checkout reports is not counted twice.

on((event) => {
  if (event.type === 'success') analytics.track('purchase', event.payload);
  if (event.type === 'close' && !event.payload?.completed) {
    analytics.track('checkout_abandoned');
  }
});

Full reference, including the CSP directives your site needs: docs.shoppex.io/developers/embeds/reference.

React

import { ShoppexCheckout } from '@shoppexio/checkout-js/react';

export default function BuyButton() {
  return (
    <ShoppexCheckout
      shopId="your-shop"
      items={[{ productId: 'PROD_123', quantity: 1 }]}
      locale="de"
      onSuccess={(invoiceId) => console.log('paid', invoiceId)}
      trigger={<button>Buy now</button>}
    />
  );
}

Headless (build your own checkout UI)

For full control of the checkout step on your own domain, use the headless primitives. You build every pixel; Shoppex runs the invoice, payment session, webhooks, and fulfillment. Add your site's origin under Dashboard → Checkout → Headless and use your publishable key (pk_live_…).

import {
  ShoppexCheckoutProvider,
  useCheckoutSession,
  useStripePaymentSession,
  ShoppexPoweredBy,
} from '@shoppexio/checkout-js/headless/react';

function Checkout() {
  const { session } = useCheckoutSession({ product_id: 'PROD_123', email });
  const stripe = useStripePaymentSession(session?.id, {
    returnUrl: 'https://your-site.com/thanks',
  });

  return (
    <form onSubmit={(e) => { e.preventDefault(); stripe.confirm(); }}>
      {/* your layout, your styles */}
      <div ref={stripe.mountRef} />
      <button disabled={!stripe.ready}>Pay</button>
      <ShoppexPoweredBy />
    </form>
  );
}

export default function App() {
  return (
    <ShoppexCheckoutProvider publishableKey="pk_live_…">
      <Checkout />
    </ShoppexCheckoutProvider>
  );
}

Render against the session kind (embed | redirect | address | balance | manual), not the provider name — that keeps redirect and crypto gateways provider-independent. Stripe has a React hook. Square, NMI, SumUp, and PayPal have framework-agnostic provider adapters exported from /headless:

import {
  HeadlessCheckoutClient,
  mountSquareCard,
  mountNmiCardFields,
  mountSumupCard,
  mountPaypalButtons,
} from '@shoppexio/checkout-js/headless';

Card fields stay inside the provider SDK/iframe. Shoppex never receives the PAN. mountSumupCard and PayPal order capture call on_complete only after Shoppex projects COMPLETED; use on_pending to show provider processing. For PayPal subscriptions, on_approved means the buyer approved the subscription at PayPal. Keep polling the checkout session until its webhook-backed payment_status becomes COMPLETED; subscription approval is not payment completion.

ready is bound to the session id you passed on that render. Hand useStripePaymentSession a different session and ready is false from that render on, until the new payment form is mounted — so disabled={!stripe.ready} is a complete guard. confirm() refuses (throws) if it is called while the mounted form still belongs to a previous session, rather than confirming one session against another's return URL.

<ShoppexPoweredBy /> is required unless the server returns an active White Label entitlement. The SDK refuses to confirm a payment unless the badge is visible whenever session.attribution.required is true. The hidden White Label plan is assigned by Shoppex operators and includes Business; it is not publicly purchasable. A gateway's presentation.hide_provider_attribution preference only affects that payment provider's attribution; it never hides Shoppex branding.

The framework-independent client does not use React: import { HeadlessCheckoutClient } from '@shoppexio/checkout-js/headless'. Without React hooks, you must enforce the badge. Register the visible badge with registerAttributionBadge(el, session.attribution). Before you confirm a payment, call assertAttribution(session.attribution.required). Both functions are exported from /headless. The server directive protects the required label and Shoppex link from DOM or CSS changes.

The session includes authoritative gateway fee previews, payment-method labels and requirements, buyer identity state, Customer Balance availability, product custom-field definitions, and delivery-instruction definitions. Use the client methods instead of writing directly to invoice endpoints:

await client.updateSession(session.id, {
  email: '[email protected]',
  payment_method_terms_accepted: true,
  billing_address: {
    name: 'Buyer Name',
    line1: '1 Main Street',
    city: 'Berlin',
    country: 'DE',
    postal_code: '10115',
  },
});

if (!session.payment_required) await client.completeFreeCheckout(session.id);

EU consumer law lets buyers of digital goods waive their 14-day withdrawal right in exchange for immediate delivery. When the shop has enabled this under Dashboard → Settings → Checkout, the session tells you whether the waiver is outstanding for this buyer. Render withdrawal_consent.text verbatim as an unchecked checkbox and record it before starting the payment — otherwise the payment start is refused with 409 withdrawal_consent_required:

const consent = session.withdrawal_consent;
if (consent.required && !consent.recorded_at) {
  // buyer ticked the checkbox showing consent.text
  session = await client.recordWithdrawalConsent(session.id, consent.text_version);
}
// React: const { recordWithdrawalConsent } = useCheckoutSession(...)

useStripePaymentSession starts the Stripe attempt as soon as the session id arrives. When that start is refused because the consent is outstanding, the hook re-issues it by itself once recordWithdrawalConsent() succeeds; other gateways start on your call, so start them after the consent.

await client.requestBalanceOtp(session.id, '[email protected]');
const verified = await client.verifyBalanceOtp(session.id, '[email protected]', '123456');
await client.payWithBalance(session.id, verified.session_token);

Crypto progress and underpayments are returned from the authoritative payment projection. Do not calculate the remainder from floating-point numbers:

const detail = session.payment_detail;

if (typeof detail?.confirmations_needed === 'number') {
  console.log(`${detail.confirmations ?? 0}/${detail.confirmations_needed}`);
}

if (detail?.remaining && detail.buyer_actionable !== false) {
  console.log(`Send ${detail.remaining} ${detail.crypto_currency ?? 'crypto'}`);
}

if (detail?.buyer_actionable === false) {
  console.log(detail.buyer_action_reason);
}

if (detail?.received && detail.received_fiat_estimate) {
  console.log(
    `Received on-chain: ${detail.received} ${detail.crypto_currency} (~${detail.received_fiat_estimate} ${detail.fiat_currency})`,
  );
  console.log(`Credited by provider: ${detail.credited_fiat ?? '0'} ${detail.fiat_currency}`);
}

Manual gateways expose require_proof and proof_type in their payment session. Submit proof with client.submitManualProof(...); it registers the completion grant first and keeps the proof bound to the buyer's browser.

API

See docs.shoppex.io/developers/embeds/reference for the modal API and docs.shoppex.io/headless/checkout for the headless session contract, CSP, and origin setup.