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

@suluk/payments

v0.2.0

Published

Provider-agnostic payments — a Workers-native TypeScript reimplementation of the Hyperswitch Prism connector interface. ONE unified request schema (authorize/capture/void/refund/sync + optional customer/tokenize/recurring/webhook); switch processor by con

Readme


CANDIDATE tooling — not official OpenAPI. Suluk is a single-contributor candidate for OpenAPI Specification v4.0 ("Moonwalk"), unaffiliated with the OpenAPI Initiative and unable to ratify anything on the SIG's behalf.

Install

bun add @suluk/payments

What it does

One unified payment schema for every processor (C048). Hyperswitch Prism ships as a native FFI addon (a Rust core) that can't run in a Cloudflare Worker, so @suluk/payments adopts its interface — a single request schema for authorize / capture / void / refund / sync, plus optional customer / tokenize / recurring / webhook surfaces — and implements it over fetch: zero native deps, edge-safe, swappable. You pass one connectorConfig naming the processor and its credentials; switching Stripe → Adyen is a config change, not a code change.

The status enums mirror Prism's integer values exactly, so a real Prism backend stays a drop-in later and connector semantics match. A soft decline is returned in-band as status: PaymentStatus.* (e.g. ROUTER_DECLINED), never thrown — thrown errors are reserved for integration/network faults. Sensitive values are wrapped in Secret<T> so they're explicit at every call site and never accidentally logged.

This barrel ships the interface + a mockConnector, the first real backend (stripeConnector), the processor-agnostic pricing primitives, and the SDK-free Stripe webhook surface — the seam that supersedes @suluk/stripe.

Usage

import {
  paymentClient, stripeConnector,
  CaptureMethod, AuthenticationType, Currency, PaymentStatus,
  type ConnectorConfig, type AuthorizeRequest,
} from "@suluk/payments";

// Config selects the connector — name exactly one processor + its credentials.
const config: ConnectorConfig = { connectorConfig: { stripe: { apiKey: { value: env.STRIPE_SECRET_KEY } } } };

// `registry` maps a processor name → its connector factory.
const client = paymentClient(config, { stripe: stripeConnector });

const req: AuthorizeRequest = {
  merchantTransactionId: "txn_1",
  amount: { minorAmount: 2000, currency: Currency.USD }, // integer minor units
  captureMethod: CaptureMethod.AUTOMATIC,
  authType: AuthenticationType.NO_THREE_DS,
  paymentMethod: { card: {
    cardNumber: { value: "4242424242424242" }, cardExpMonth: { value: "12" },
    cardExpYear: { value: "2030" }, cardCvc: { value: "123" },
  } },
};

const res = await client.authorize(req);
res.status;                 // PaymentStatus.CHARGED — a soft decline is a status, never a throw
res.connectorTransactionId; // "pi_..." — carry it into capture/void/refund/sync

// Later: refund part of the charge.
await client.refund({
  merchantRefundId: "r1",
  connectorTransactionId: res.connectorTransactionId,
  refundAmount: { minorAmount: 500, currency: Currency.USD },
  paymentAmount: 2000,
});

The transport seam is mockable — pass { fetch } as the third arg to paymentClient (or use mockConnector with MOCK_DECLINE_CARD / MOCK_3DS_CARD) to test the whole flow with no live processor.

What's inside

| Export | What it does | | --- | --- | | paymentClient(config, registry, http?) | Resolve the config-named processor to a bound PaymentConnector. | | PaymentConnector (interface) | The unified surface every processor implements (core flows required; advanced optional). | | stripeConnector | The first real backend — fetch → Stripe REST, Workers-native. | | mockConnector, MOCK_DECLINE_CARD, MOCK_3DS_CARD | An in-memory connector + fixtures for tests. | | IntegrationError / ConnectorError / NetworkError / PaymentLibError | The thrown error taxonomy (a soft decline is a status, not an error). | | subtotal, orderTotal, composeTotal, computeDiscountAmount, validateDiscount, prorateDiscount, verifyAmount, cartFingerprint, idempotencyKey, … | Processor-agnostic pricing math (anti-tampering, proration, checkout totals). | | verifyStripeSignature, timingSafeHexEqual, webhookRouter, STRIPE_EVENTS | SDK-free Stripe webhook verification + a typed event router. | | stripePost / stripeGet / toForm (stripe-transport) | The low-level one-client Stripe transport for platform ops the agnostic seam doesn't model. |

The type vocabulary (AuthorizeRequest, PaymentResponse, RefundRequest, MinorAmount, Secret, Currency, CaptureMethod, AuthenticationType, PaymentStatus, RefundStatus, WebhookEvent, …) is re-exported from ./types.

Boundary

@suluk/payments is the provider-agnostic seam that supersedes @suluk/stripe — interface-first (C048), with real connectors (Adyen, …) and the @suluk/billing rewire as follow-on builds. It is dependency-free (zero native deps, only fetch), stores nothing itself, and returns money status in-band. It's the transport @suluk/billing builds its Stripe plumbing on.

License

Apache-2.0