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

@paysmith/sdk

v0.0.2

Published

Paysmith JavaScript SDK — server client for sandbox payment intents, receipts and entitlements, plus browser-safe checkout helpers that never hold a token.

Readme

@paysmith/sdk

The Paysmith JavaScript SDK, split into two entry points with a hard boundary between them: @paysmith/sdk/server holds the sandbox token and talks to the Paysmith control plane; @paysmith/sdk/browser never holds a token and only ever calls your own application's endpoints. There is no default export worth importing — always pick /server or /browser explicitly.

All sandbox activity behind this SDK is fake money: a deterministic $100.00 balance that expires 72 hours after activation. Every server-side write is idempotent by default (see below).

Install

pnpm add @paysmith/sdk
# or
npm i @paysmith/sdk

Quick start

Server (@paysmith/sdk/server)

Server-only. Importing this module in a browser bundle throws immediately at import time — it never gets the chance to leak the sandbox token.

// PAYSMITH_API_BASE and PAYSMITH_SANDBOX_TOKEN come from your sandbox activation
import { createPaysmithServerClient } from "@paysmith/sdk/server";

const client = createPaysmithServerClient(); // reads both env vars automatically

const intent = await client.createPaymentIntent({
  environment: "sandbox",
  amount: { value: "1.00", currency: "USD" },
  product: { reference: "paysmith-demo-lifetime-access", name: "Lifetime access" },
  customer: { reference: "demo-browser-session" },
  metadata: { entitlement: "demo.premium" },
});

const confirmed = await client.confirmTestPayment(intent.payment_intent_id, "succeeds");
const entitlement = await client.getEntitlement("demo-browser-session", "demo.premium");

Environment variables:

| Variable | Purpose | | --- | --- | | PAYSMITH_API_BASE | Base URL of your Paysmith sandbox API. Must be https://, unless it is localhost / 127.0.0.1. | | PAYSMITH_SANDBOX_TOKEN | The short-lived scoped token issued for the sandbox. |

Both can also be passed explicitly as apiBase / clientToken to createPaysmithServerClient(options), which take priority over the environment variables. A non-loopback http:// base throws — over plaintext, an attacker on the network path could substitute the sandbox signing key and forge unlock events.

Every write (createPaymentIntent, confirmTestPayment) automatically sends an Idempotency-Key header. Pass one explicitly via { idempotencyKey } to control retries yourself; otherwise it is derived deterministically from the method, path, and body, so an identical retry is naturally safe.

Pull mode

pullEvents is transport only — it fetches signed event envelopes your webhook route might never receive (e.g. a localhost app the control plane cannot reach) but does not verify them. Run each one through @paysmith/webhook's verifyPaysmithEvent yourself, exactly as you would for a webhook delivery; downstream processing is deduped by event_id, so polling overlap with webhook delivery is safe.

import { createPaysmithServerClient } from "@paysmith/sdk/server";
import { verifyPaysmithEvent } from "@paysmith/webhook";

const client = createPaysmithServerClient();
const publicKey = await client.getSandboxPublicKey();

let after = 0;
const { events, latestId } = await client.pullEvents({ after, limit: 50 });
for (const event of events) {
  const result = verifyPaysmithEvent({
    rawBody: event.rawBody,
    headers: event.headers,
    publicKeyPem: publicKey.public_key_pem,
  });
  if (result.ok) {
    // hand result.event to the same verify -> dedupe -> grant pipeline a webhook route uses
  }
}
after = latestId;

Browser (@paysmith/sdk/browser)

Browser-safe. These helpers call only your own application's routes — never Paysmith directly, and never with a sandbox token.

import { startCheckout, confirmScenario, pollEntitlement } from "@paysmith/sdk/browser";

const { payment_intent_id } = (await startCheckout("/api/paysmith/checkout")) as {
  payment_intent_id: string;
};

await confirmScenario("/api/paysmith/checkout/confirm", payment_intent_id, "succeeds");

const entitlement = await pollEntitlement("/api/paysmith/entitlement");
// entitlement.status === "unlocked" once your webhook route has verified the signed event

confirmScenario's third argument is a SandboxOutcomeScenario: "succeeds" | "declined" | "delayed" | "duplicate_webhook" | "refund_after_success".

Neither startCheckout nor confirmScenario tells you the payment outcome — their responses come from your checkout/confirm routes, which themselves only start or nudge a sandbox payment intent. The only trustworthy signal is what pollEntitlement eventually reports, because that reflects your webhook route's verified state, not a client-side guess.

API

@paysmith/sdk/server

  • createPaysmithServerClient(options?: PaysmithServerClientOptions): PaysmithServerClientoptions: { apiBase?, clientToken?, fetchImpl? }.
  • PaysmithServerClient methods:
    • createPaymentIntent(request: CreatePaymentIntentRequest, options?: WriteOptions): Promise<CreatePaymentIntentResponse>
    • confirmTestPayment(intentId: string, scenario: SandboxOutcomeScenario, options?: WriteOptions): Promise<ConfirmTestPaymentResult>
    • getReceipt(id: string): Promise<ReceiptWithVerification>
    • getEntitlement(subject: string, entitlementKey?: string): Promise<Entitlement>
    • getSandbox(): Promise<SandboxStatus>
    • getSandboxPublicKey(): Promise<SandboxPublicKey>
    • pullEvents(options?: { after?: number, limit?: number }): Promise<{ events: Array<{ id: number, rawBody: string, headers: Record<string, string> }>, latestId: number }>
  • assertSecureApiBase(apiBase: string): void — throws unless apiBase is https:// or loopback.
  • PaysmithApiError — thrown on any non-2xx response; carries status, code, and requestId.

@paysmith/sdk/browser

  • startCheckout(appCheckoutUrl: string): Promise<unknown>
  • confirmScenario(appConfirmUrl: string, paymentIntentId: string, scenario: SandboxOutcomeScenario): Promise<unknown>
  • pollEntitlement(appEntitlementUrl: string, options?: PollEntitlementOptions): Promise<EntitlementStatusResponse>options: { intervalMs? (default 1000), timeoutMs? (default 30000) }; rejects if the timeout elapses before status becomes "unlocked".

How it fits

@paysmith/sdk/server is how your backend turns a product into a payment intent and, once @paysmith/webhook has verified the resulting signed event, looks up the receipt it produced. @paysmith/sdk/browser is how your frontend drives that flow and observes the resulting entitlement — without ever seeing the sandbox token that makes any of it authoritative.

License

MIT