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

@orkestrapay/cse

v1.0.8

Published

Orkestra client-side encryption (CSE) browser SDK — encrypts card data into a JWE Compact (RSA-OAEP-256 + A256GCM) using only the Web Crypto API.

Readme

@orkestrapay/cse

Encrypt card data in the browser, before it ever leaves your page.

@orkestrapay/cse turns the card number, expiry and CVV into a single encrypted string (a JWE Compact token) using only the browser's built-in Web Crypto API. Your server — and anyone between it and the browser — only ever sees ciphertext. Only Orkestra can decrypt it.

  • Zero dependencies, ~4.5KB gzipped, no polyfills.
  • Works with any framework, or with a plain <script> tag.
  • RSA-OAEP-256 + A256GCM, Web Crypto only.

CSE runs on top of HTTPS, not instead of it. Every request is still TLS.


1. Install

With a bundler (npm / yarn / pnpm):

npm install @orkestrapay/cse
import { initialize, encryptCard } from "@orkestrapay/cse"

Without a bundler (script tag):

<script src="https://<cdn-host>/cse-runtime/1.0.7/runtime.min.js"
        integrity="sha384-…"
        crossorigin="anonymous"></script>

This exposes one global: window.OrkestraCSE, with the same functions.

Two rules for production:

  1. Pin the exact version — no ^, no ~, and never a /latest CDN URL. This is a security component; upgrades should be deliberate.
  2. Always use the integrity (SRI) hash for the script tag. Orkestra provides it with each release. Details in docs/sri-pci.md.

2. Use it

Three steps: initialize once, encrypt at submit, send only the ciphertext.

<script src="https://<cdn-host>/cse-runtime/1.0.7/runtime.min.js"
        integrity="sha384-…" crossorigin="anonymous"></script>
<script>
  // 1. Once, when the checkout page loads. Name the environment — nothing else.
  await OrkestraCSE.initialize({
    environment: "production",   // or "qa" / "sandbox"
    audience: "core",
  })

  // 2. When the customer submits, read the inputs and encrypt.
  const jwe = await OrkestraCSE.encryptCard({
    pan: "4111111111111111",
    expMonth: "03",
    expYear: "2030",
    cvv: "123",
    holderName: "Ada Lovelace",   // optional
  })

  // 3. Send ONLY the ciphertext to your backend, then clear the inputs.
  await fetch("/api/pay", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ crypto: { blob: jwe } }),
  })
</script>

Your backend forwards { crypto: { blob: jwe } } to Orkestra unchanged. It never needs to read, parse or store the card data.

Complete, copy-pasteable examples: examples/vanilla (plain JS) and examples/react (React / Next.js).

The rules that keep this safe

  1. Render card fields in the DOM as usual.
  2. At submit, read the values directly from the inputs — never put them in app state, Redux, localStorage, logs or analytics.
  3. await encryptCard(...).
  4. Clear the inputs (form.reset()), even when encryption fails.
  5. POST only the JWE. Nothing else about the card.

3. API

initialize(options): Promise<void>      // call once, before encrypting
encryptCard(card, ctx?): Promise<string> // returns the JWE Compact string
refreshKey(signal?): Promise<void>       // force a new public key (see key rotation)
getVersion(): string                     // SDK version
createCseClient(): CseClient             // independent instance (multi-tenant / tests)

initialize is idempotent for the same configuration: repeated and concurrent calls are coalesced into one key fetch. Re-initializing with a different environment throws INVALID_CONFIG rather than silently keeping the first one — if you genuinely need two environments at once, use createCseClient(). If initialization fails, just call it again to retry.

initialize(options)

| Option | Required | Default | What it does | |---|---|---|---| | environment | yes | — | "production", "qa" or "sandbox". The public-key URL for that environment ships inside the SDK — you never configure a URL. | | audience | no | — | Written into every payload as aud. Usually "core". | | timeoutMs | no | 8000 | Timeout for fetching the public key. | | cacheTtlMs | no | 3600000 (1h) | How long the key is cached in memory. Capped by the endpoint's Cache-Control. | | keyFetchRetries | no | 1 | Retries for the key fetch only. encryptCard is never retried silently. | | signal | no | — | AbortSignal to cancel initialization. | | keyEndpoint | no | — | Restricted override, for local development and Orkestra-internal staging only. Accepted solely for localhost or an orkestrapay.io host; anything else throws INVALID_CONFIG. Integrations should not use it. |

Why there is no URL to configure. The public key is the trust anchor of the whole scheme. If the endpoint could be set to an arbitrary host, one wrong value in a .env would make the SDK encrypt cards to someone else's public key — and nothing downstream would notice, because the JWE would still be perfectly valid. So the URLs ship inside the SDK and you only name the environment.

encryptCard(card, ctx?)

| Field | Required | Format | |---|---|---| | pan | yes | 12–19 digits. Spaces and dashes are stripped for you. | | expMonth | yes | 1–12; "3" and "03" both work. | | expYear | yes | 4 digits, e.g. "2030". Two-digit years are rejected. | | cvv | yes | 3–4 digits. | | holderName | no | Cardholder name. |

Optional second argument ctx: { audience?, operationId?, signal? } — overrides the audience for a single call, attaches your own correlation id, or aborts a pending key fetch.

Returns the JWE Compact string. That string is the only thing you send.

Using multiple configurations

The module-level functions share one client. If you need more than one (multi-tenant apps, tests, two environments side by side), create independent instances:

import { createCseClient } from "@orkestrapay/cse"

const cse = createCseClient()
await cse.initialize({ environment: "production", audience: "core" })
const jwe = await cse.encryptCard({ pan, expMonth, expYear, cvv })

4. Handling errors

Every failure throws a CseError with a stable .code. Branch on code, never on message (messages are human-readable and may change). Errors never contain the PAN, CVV, payload or key.

import { CseError } from "@orkestrapay/cse"

try {
  const jwe = await encryptCard(card)
} catch (err) {
  if (CseError.is(err)) {
    switch (err.code) {
      case "INVALID_PAYLOAD":   return showFieldError("Check the card details.")
      case "KEY_FETCH_FAILED":
      case "TIMEOUT":           return showRetry("Try again in a moment.")
      default:                  return showGeneric()
    }
  }
  throw err
}

| Code | Meaning / what to do | |---|---| | SDK_NOT_INITIALIZED | encryptCard was called before initialize. | | INVALID_CONFIG | Bad initialize options — missing/unknown environment, or a keyEndpoint override pointing at a host that is not Orkestra's. | | INSECURE_CONTEXT | The page is not served over HTTPS. Fix the page, not the SDK. | | WEBCRYPTO_UNAVAILABLE | The browser has no crypto.subtle. Unsupported browser. | | KEY_FETCH_FAILED | Could not reach the key endpoint. Retryable. | | KEY_INVALID / KEY_EXPIRED | The served key is unusable. Contact Orkestra support. | | UNSUPPORTED_ALGORITHM | The browser cannot perform RSA-OAEP-256 / A256GCM, or the served key's algorithm is outside the strict allowlist (downgrade attempt). Usually an old browser, not an attack. | | INVALID_PAYLOAD | A card field failed format validation. Show a form error. | | ENCRYPTION_FAILED | The crypto operation failed. Retryable once. | | TIMEOUT | The key fetch timed out or was aborted. Retryable. |

Key rotation mid-checkout

Orkestra rotates the encryption key periodically. If your backend reports that the kid has expired, refresh and retry once — the SDK never re-encrypts silently:

await refreshKey()
const jwe = await encryptCard(card)

5. Browser support

Any browser with the Web Crypto API and crypto.subtle over HTTPS — the last two major versions of Chrome, Edge, Firefox, Safari and iOS Safari. There is no polyfill fallback: on an unsupported browser initialize fails fast with WEBCRYPTO_UNAVAILABLE.

6. Security

The private key never leaves Orkestra's Vault; plaintext exists only transiently inside the platform. What this protects against — and, just as importantly, what it does not (JS memory, malware in the page before encryption, TLS, PCI/SAQ scope) — is documented honestly in:

Further reading

| Document | What's in it | |---|---| | docs/integration.md | Longer integration guide | | docs/wire-contract.md | The exact JWE format (frozen) | | docs/versioning.md | SemVer policy | | CHANGELOG.md | Release history | | docs/maintaining.md | Building and releasing the SDK (maintainers) |

Need help? Contact Orkestra support.