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

sumup-client

v1.0.0

Published

A production-grade, fully-typed TypeScript client for the SumUp API (Checkouts, Readers, Customers, Transactions, Payouts, Receipts, Members, Memberships, Roles, Merchants) with retries, pagination, webhook handling, and RFC 9457 aware error handling.

Readme

sumup-client

npm CI License TypeScript

A production-grade, fully-typed TypeScript client for the SumUp API — Checkouts, Readers, Customers, Payment Instruments, Transactions, Payouts, Receipts, Members, Memberships, Roles, and Merchants.

Why this exists

SumUp publishes official SDKs for Node, Go, Python, Java, .NET, PHP and Rust — but the Node one is a thin, loosely-typed wrapper. This package takes a stricter approach, modeling the API's real shape rather than papering over it:

  • Zero runtime dependencies. Built entirely on native fetch, Headers, URL, and AbortController — no axios, no request library, nothing to audit in your supply chain beyond this package itself.
  • Two error formats, normalized into one. Legacy resources (Checkouts, Customers, Transactions, Payouts) return {"error_code", "message", "param"} (sometimes as a list); newer resources (Readers, Members, Merchants) and every 401 return RFC 9457 Problem Details. SumUpError decodes both into one class, thrown so you get idiomatic try/catch or .catch() handling.
  • Two money formats, kept distinct. Legacy endpoints represent money as a float major-unit amount (FloatMoney); Readers use an integer minor-unit MinorUnitMoney. They're deliberately not unified into one type — doing so would either lose precision or require guessing a currency's decimal exponent.
  • Per-resource API versioning. Each resource method hits whatever version SumUp actually ships for it (v0.1 for Checkouts, v2.1 for Transaction reads but v1.0 for refunds, v1 for Merchants, etc.) rather than assuming one version for the whole client.
  • Reader checkouts modeled as genuinely async. Pushing a payment to a physical Solo reader resolves as soon as it's been sent to the device; this library returns that intermediate state honestly instead of faking synchronicity with an internal poll loop.

Installation

npm install sumup-client

Requires Node.js >= 18.17 (for global fetch), or any runtime that provides one (Bun, Deno, Cloudflare Workers, modern browsers).

Quick start

import { SumUpClient } from "sumup-client";

const client = new SumUpClient({ apiKey: process.env.SUMUP_API_KEY });

const checkout = await client.checkouts.create({
  checkoutReference: "order-1234",
  amount: 10.5,
  currency: "EUR",
  merchantCode: "MC12345",
});

const paid = await client.checkouts.process(checkout.id, {
  paymentType: "card",
  card: {
    name: "Jane Doe",
    number: "4111111111111111",
    expiryMonth: "12",
    expiryYear: "2030",
    cvv: "123",
  },
});

Configuration

const client = new SumUpClient({
  apiKey: process.env.SUMUP_API_KEY, // or accessToken for OAuth2
  maxRetries: 5,
  receiveTimeoutMs: 15_000,
});

See SumUpClientOptions in the type definitions for the full option list (base URL override for testing, retry/backoff tuning, a custom fetch implementation, extra headers, etc).

Resources

| Property | Covers | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | client.checkouts | create, get, find by reference, process (card/token/APM), deactivate, Apple Pay sessions, list payment methods | | client.readers | pair, list, get (with If-Modified-Since support), get live status, rename/update, delete, push a checkout, terminate | | client.customers | create, get, update | | client.paymentInstruments | list, deactivate | | client.transactions | get by id/code/foreign id/client id, list (with bracketed array filters), listAll() async-generator stream, refund | | client.payouts | date-ranged report, JSON (list) or CSV (listCsv) | | client.receipts | detailed receipt lookup | | client.members | create, get, list, listAll(), update (full replace via PUT), delete | | client.memberships | list the current authenticated user's own memberships (no merchant/user id in the path) | | client.roles | create, list, get, update, delete custom roles | | client.merchants | get profile (version + change-status pattern), list/get persons — read-only, SumUp's public API has no merchant-update endpoint |

client.webhooks isn't a REST resource — see Webhooks below.

Error handling

Every method throws a SumUpError (API/transport failures) or a SumUpValidationError (bad arguments, caught before any request is sent). SumUpError exposes a consistent set of fields (status, code, message, param, type, title, instance, errors, raw) no matter which underlying shape the API returned:

try {
  await client.checkouts.process(checkoutId, { paymentType: "card", card });
} catch (error) {
  if (error instanceof SumUpError && error.code === "CARD_DECLINED") {
    return { declined: true, reason: error.message };
  }
  throw error;
}

Retries and telemetry

429 and 5xx responses (and transport failures) are retried with exponential backoff and jitter, honoring a numeric retry-after header when SumUp sends one. Every request emits typed telemetry events you can subscribe to without modifying this library:

client.on("request:success", ({ resource, operation, status, durationMs }) => {
  logger.info(`sumup ${resource}.${operation} -> ${status} (${durationMs}ms)`);
});

client.on("request:retry", ({ resource, operation, attempt, delayMs }) => {
  logger.warn(`sumup ${resource}.${operation} retrying (attempt ${attempt}, waiting ${delayMs}ms)`);
});

Streaming large result sets

client.transactions.listAll() and client.members.listAll() return async generators built on a generic paginate() helper, so you can use a plain for await loop and stop whenever you like without fetching more pages than you need:

for await (const tx of client.transactions.listAll("M1", { statuses: ["SUCCESSFUL"] })) {
  if (tx.amount && tx.amount.amount > 100) {
    console.log(tx.id, tx.amount.amount);
  }
}

Or collect everything into an array with the collect() helper:

import { collect } from "sumup-client";

const allMembers = await collect(client.members.listAll("M1"));

Webhooks

SumUp's Checkout webhooks are not signed — there's no HMAC or signature header to check for this API (that's a different SumUp product entirely; see the Webhooks class doc comment for the full explanation). SumUp's own recommendation is to treat the webhook body as an unauthenticated pointer and re-fetch the real state from the API:

app.post("/webhooks/sumup", express.text({ type: "*/*" }), async (req, res) => {
  try {
    const checkout = await client.webhooks.handle(req.body);
    await handleCheckoutUpdate(checkout);
  } catch {
    // still ack below, even on a parse/lookup failure
  }

  // Always ack quickly with 2xx, however processing went — SumUp retries
  // non-2xx deliveries at 1 min, 5 min, 20 min, and 2 hours.
  res.sendStatus(200);
});

client.webhooks.parse() decodes the payload without trusting it; client.webhooks.verify() does the recommended re-fetch; client.webhooks.handle() does both in one call, as above.

Testing your own integration

This library's own test suite uses a small hand-rolled fetch stub (see test/support/mock-fetch.ts) rather than a mocking library — inject your own via the fetch client option:

const client = new SumUpClient({
  apiKey: "sk_test_123",
  fetch: async (url, init) => {
    // return a Response however you like
    return new Response(JSON.stringify({ id: "chk_1", status: "PAID" }), {
      headers: { "content-type": "application/json" },
    });
  },
});

Development

npm install
npm run ci   # format check + lint + typecheck + test with coverage + build

Individual scripts: npm run format, npm run lint, npm run typecheck, npm test, npm run test:coverage, npm run build, npm run docs (generates API docs into docs/ via TypeDoc).

A note on endpoint coverage

Every path, parameter, and response field in this library was cross-checked against SumUp's published OpenAPI spec — including some easy-to-miss details it corrects for: client.memberships has no merchant/user id in its path (it's always the calling user's own memberships), client.transactions.refund() is scoped under /merchants/{merchantCode}/payments/{id}/refunds and returns no body, client.merchants has no update method in the public API, reader checkouts use tipRates/tipTimeout rather than a flat tipAmount, and transaction history's array filters (statuses[], paymentTypes[], entryModes[], types[]) are sent as repeated bracketed query keys, not comma-joined values. SumUp does still evolve its API surface over time, so if you hit a mismatch, please open an issue or PR — every resource module follows the same pattern, so adding or correcting an endpoint is usually a small, self-contained change.

License

MIT. See LICENSE.