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

@thru-payment/checkout-core

v0.2.0

Published

Headless data layer for thru checkout: framework-agnostic client and stores, plus React hooks and provider. No pre-built UI, no styling dependencies - bring your own components.

Readme

@thru-payment/checkout-core

The headless data layer behind @thru-payment/pay-sdk: a provider, a typed read-only client, and a hook that polls thru's public payment endpoints. No components, no CSS, no qrcode/clsx dependency - if you're building your own checkout UI from scratch and just want thru's data and state, install this instead of pay-sdk.

If you want pre-built, themeable React components, use @thru-payment/pay-sdk instead - it depends on this package and re-exports everything here too, so you don't need both.

Install

npm install @thru-payment/checkout-core

Usage

import { ThruProvider, usePayment } from '@thru-payment/checkout-core';

function MyCheckout({ paymentId }: { paymentId: string }) {
  const { data: payment, loading, error } = usePayment(paymentId);
  if (loading) return <MySpinner />;
  if (error || !payment) return <MyError />;
  // Render however you like - payment.status, payment.expectedAmount,
  // payment.paymentAddress, etc.
  return <MyOwnCheckoutMarkup payment={payment} />;
}

function App() {
  return (
    <ThruProvider apiBaseUrl="https://api.thru.la/v1">
      <MyCheckout paymentId={paymentId} />
    </ThruProvider>
  );
}

It is safe to run in the browser: it only reads public, secret-free endpoints. Your secret API key never leaves your server.

You can also skip the provider entirely and hand a hook its own client:

const client = createThruClient(); // defaults to https://api.thru.la/v1
const { data } = usePayment(paymentId, { client });

Without React

react is an optional peer dependency. Everything except ThruProvider and the use* hooks is framework-agnostic and published at the ./core subpath, which contains no React import at runtime or in its types - install this package in a Vue, Svelte, Solid or vanilla app and nothing will ask for React.

Each resource is a small store: subscribe(listener) returns an unsubscribe, getSnapshot() returns the current { data, error, loading }. The first subscriber starts the poll and the last one to leave tears it down, so lifecycle is just subscribe/unsubscribe. This is the same logic the React hooks run - they are a useSyncExternalStore wrapper over exactly these stores.

import { createThruClient, createPaymentStore } from '@thru-payment/checkout-core/core';

const client = createThruClient();
const store = createPaymentStore(client, paymentId); // polls 5s, stops when settled

const unsubscribe = store.subscribe(({ data, error, loading }) => {
  render(data);
});
// later
unsubscribe();

A Vue composable is then about six lines:

import { ref, onScopeDispose } from 'vue';
import { createPaymentStore } from '@thru-payment/checkout-core/core';

export function usePayment(client, id) {
  const store = createPaymentStore(client, id);
  const state = ref(store.getSnapshot());
  onScopeDispose(store.subscribe((next) => (state.value = next)));
  return state;
}

subscribe already matches Svelte's store contract, so $store works directly on a Svelte store wrapper too.

Exports

Root (@thru-payment/checkout-core) - React:

  • Provider: ThruProvider, useThru, useOptionalThru
  • Hooks: usePayment, useThruStore, AsyncState
  • Theming: themeToVars (React CSSProperties)
  • ...plus everything in ./core below, re-exported.

./core (@thru-payment/checkout-core/core) - no React:

  • Client: createThruClient, DEFAULT_API_BASE_URL, ThruClient
  • Stores: createPaymentStore, createResourceStore, createIdleStore, ThruStore, AsyncState, ResourceStoreOptions, Unsubscribe
  • Status: isTerminalPaymentStatus, TERMINAL_PAYMENT_STATUSES, DEFAULT_PAYMENT_POLL_MS
  • Theming: themeToCssVars, mergeTheme, THRU_CSS_VARS, ThruTheme
  • Utilities: shorten, statusTone, statusLabel, formatDuration, StatusTone
  • Types: PublicPayment, PublicPaymentTransaction

Polling behaviour

| Resource | Interval | Stops when | | --- | --- | --- | | Payment | 5s | status is confirmed, settled, expired, underpaid, overpaid, failed, refunded |

expired is terminal for the POLL, not for the money: thru still credits a late transfer to an expired payment's address. If your surface has to see that, keep a store alive with your own isTerminal:

createPaymentStore(client, id, { isTerminal: (p) => p.status === 'confirmed' });
createPaymentStore(client, id, { retryOnError: false }); // stop hammering an id that 404s

License

MIT