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/contracts

v0.0.1

Published

Paysmith contract foundation: zod schemas for the wire contracts, prefixed ids, decimal-string money, RFC 8785-style canonical JSON, Ed25519 event signing, and idempotency primitives.

Readme

@paysmith/contracts

Shared wire contracts for Paysmith, an embedded-finance sandbox that gives any app a believable $1 paywall/unlock demo. This package defines the vocabulary every other Paysmith package and every generated app speaks: zod schemas for sandbox activation, payment intents, the signed paysmith.event/v1 envelope, receipts and entitlements; decimal-string Money (never floats); prefixed identifiers; RFC 8785-style canonical JSON with sha256 digests; Ed25519 event signing; and the idempotency primitives every write is scoped by. Zero runtime dependencies beyond zod and Node's built-in node:crypto.

All amounts this contract describes are sandbox money: fake, deterministic, and scoped to a 72-hour sandbox lifetime. SANDBOX_ENVIRONMENT is the only environment Phase 0 mints resources in.

Install

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

Quick start

Validate a signed event envelope

import { EventEnvelopeSchema } from "@paysmith/contracts";

const event = EventEnvelopeSchema.parse({
  schema: "paysmith.event/v1",
  event_id: "evt_01J9ZAW4QK4M6VXNTS0P6ZC9G7",
  type: "payment.succeeded",
  environment: "sandbox",
  resource: { type: "payment_intent", id: "pi_sbx_01J9ZAW4QK4M6VXNTS0P6ZC9G7" },
  sequence: 1,
  occurred_at: "2026-08-08T12:00:00Z",
  data: {
    amount: { value: "1.00", currency: "USD" },
    product_reference: "paysmith-demo-lifetime-access",
  },
});
// event.type: "payment.succeeded" | "payment.failed" | "payment.refunded"

Canonical JSON + digest

import { canonicalJson, canonicalDigest, sha256Digest } from "@paysmith/contracts";

canonicalJson({ b: 1, a: "x" }); // '{"a":"x","b":1}' — sorted keys, no whitespace
canonicalDigest({ b: 1, a: "x" }); // "sha256:<hex>" over the canonical bytes
sha256Digest("raw bytes or a string"); // "sha256:<hex>"

This is the exact byte-for-byte serialization events are signed over: two independent implementations of canonicalJson must agree, which is what makes signCanonical / verifyCanonical and signEvent / verifyEventSignature trustworthy across languages.

Money helpers (decimal strings, never floats)

import { fromCents, toCents, addMoney, toDisplayBalance } from "@paysmith/contracts";

const price = fromCents(100); // { value: "1.00", currency: "USD" }
toCents(price); // 100
addMoney(price, fromCents(50)); // { value: "1.50", currency: "USD" }
toDisplayBalance({ value: "100.00", currency: "USD" }); // { amount: "100.00", currency: "USD" }

Arithmetic always happens in integer minor units internally, so no IEEE-754 rounding can reach a ledger posting.

Sign and verify a webhook delivery

import { generateSigningKeyPair, signEvent, verifyEventSignature } from "@paysmith/contracts";

const { keyId, privateKeyPem, publicKeyPem } = generateSigningKeyPair();
const rawBody = JSON.stringify(event);
const timestamp = Math.floor(Date.now() / 1000);

const signature = signEvent({
  rawBody,
  environment: "sandbox",
  sandboxId: "sbx_01J9ZAW4QK4M6VXNTS0P6ZC9G7",
  timestamp,
  keyId,
  privateKeyPem,
});

verifyEventSignature({
  rawBody,
  environment: "sandbox",
  sandboxId: "sbx_01J9ZAW4QK4M6VXNTS0P6ZC9G7",
  timestamp,
  keyId,
  signature,
  publicKeyPem,
}); // { valid: true }

@paysmith/webhook wraps this together with header parsing for the receiving side — use that package instead of hand-rolling verification in a webhook handler.

API

  • EnvironmentENVIRONMENTS, SANDBOX_ENVIRONMENT, isEnvironment, isSandbox.
  • IdsnewId, isId, assertId, ID_PREFIXES (sbx_, pi_ / pi_sbx_, evt_, rcp_, ent_, ins_, rb_, act_, trg_), plus per-resource minters: newSandboxId, newPaymentIntentId, newEventId, newReceiptId, newEntitlementId, newInstallationId, newRepositoryBindingId, newActivationId, newTriggerId.
  • MoneyMoney, DisplayBalance, isValidMoneyValue, isMoney, assertMoney, toCents, fromCents, normalizeMoney, addMoney, subtractMoney, negateMoney, compareMoney, moneyEquals, zeroMoney, toDisplayBalance, fromDisplayBalance.
  • CanonicalizationcanonicalJson, canonicalBytes, sha256Digest, canonicalDigest, isSha256Digest.
  • SigninggenerateSigningKeyPair, signCanonical, verifyCanonical, buildEventSigningMaterial, signEvent, verifyEventSignature, toUnixSeconds, DEFAULT_TIMESTAMP_TOLERANCE_SECONDS (300s), and the header name constants (SIGNATURE_HEADER, SIGNATURE_TIMESTAMP_HEADER, SIGNATURE_KEY_ID_HEADER, etc.).
  • IdempotencyIDEMPOTENCY_KEY_HEADER, normalizedRequestDigest, idempotencyScopeKey, classifyIdempotency ("fresh" | "replay" | "conflict"), isIdempotencyKey.
  • SchemasAnonymousSandboxRequestSchema / AnonymousSandboxResponseSchema, CreatePaymentIntentRequestSchema / CreatePaymentIntentResponseSchema, ConfirmSandboxOutcomeSchema, EventEnvelopeSchema, ReceiptSchema, EntitlementSchema, TriggerEnvelopeSchema, AdapterManifestSchema, plus shared primitives (MoneySchema, IsoTimestampSchema, prefixedIdSchema, …). Every schema has an inferred z.infer type of the same name without the Schema suffix.
  • JSON SchemabuildJsonSchemaDocuments() projects the zod schemas to JSON Schema for non-TypeScript consumers (./schemas-json/*.json is also published as static files).
  • ErrorsContractError (base class, .code is one of CONTRACT_ERROR_CODES), isContractError, and typed subclasses: InvalidMoneyError, CurrencyMismatchError, MoneyOverflowError, InvalidIdError, CanonicalizationError, InvalidSigningInputError, IdempotencyConflictError.

How it fits

@paysmith/contracts is the shared vocabulary behind every step of the demo: a product becomes a payment intent (CreatePaymentIntentRequestSchema), which resolves into a signed event (EventEnvelopeSchema, verified by @paysmith/webhook), which an app turns into an entitlement (EntitlementSchema) and can look up as a receipt (ReceiptSchema).

License

MIT