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

@izak0s/mplusqapi-node

v1.6.0

Published

TypeScript client for the MplusKASSA SOAP API

Readme

mplusqapi-node

CI npm license

A fully-typed TypeScript client for the MplusKASSA SOAP API (urn:mplusqapi), auto-generated from the official WSDL.

Community package — This is not an official MplusKASSA package. It is independently developed and maintained by the community. Use at your own risk. For the official PHP client, see MplusKASSA/mplusqapi-php.


Features

  • 372 typed async methods covering the full MplusKASSA API surface
  • Auto-generated from the official WSDL URL — regenerate anytime the WSDL changes
  • Fully typed — 286 enum types and 1341 interfaces, all derived from the WSDL
  • List flattening*List wrapper types (e.g. OrderList) are transparently unwrapped to plain arrays (Order[])
  • Decimal-safexsd:decimal fields typed as string to avoid floating-point precision loss
  • Date handlingSoapMplusDateTime structs and ISO date fields both map to Date
  • Rich error types — errors carry the raw xmlRequest / xmlResponse (where available) for easy debugging
  • Dual module — ships both ESM and CommonJS builds with type declarations for each

Installation

npm install @izak0s/mplusqapi-node

Runtime dependencies:

HTTP uses the built-in node:https module — no HTTP client dependency.


Quick Start

import {
  MplusKassaClient,
  MplusApiClientError,
  MplusApiServerError,
  MplusApiCommunicationError,
} from '@izak0s/mplusqapi-node';

const client = new MplusKassaClient({
  host: process.env.MPLUS_HOST!,
  port: Number(process.env.MPLUS_PORT!),
  ident: process.env.MPLUS_IDENT!,
  secret: process.env.MPLUS_SECRET!,
});

async function main() {
  // Fetch API version
  const version = await client.getApiVersion();
  console.log(`API: ${version.majorNumber}.${version.minorNumber}.${version.revisionNumber}`);

  // Fetch orders (returns Order[] directly — list wrappers are unwrapped)
  const orders = await client.getOrders({ syncMarker: 0, syncMarkerLimit: 10 });
  for (const order of orders) {
    console.log(order.orderId, order.financialDate);
  }

  // Fetch a single relation — `relation` is undefined when result is NOT-FOUND
  const { result, relation } = await client.getRelation(42);
  if (result === 'GET-RELATION-RESULT-OK') {
    console.log(relation?.name, relation?.email);
  }
}

main().catch(console.error);

Authentication

Credentials are passed as URL query parameters (?ident=X&secret=Y), not in SOAP headers. Pass them to the constructor:

const client = new MplusKassaClient({
  host: process.env.MPLUS_HOST!, // hostname only, no protocol
  port: Number(process.env.MPLUS_PORT!),
  ident: process.env.MPLUS_IDENT!,
  secret: process.env.MPLUS_SECRET!,
});

Transport options

| Option | Default | Description | |---|---|---| | timeout | 30 | Request timeout in seconds | | maxRetries | 3 | Retry attempts on retryable transport errors (see below; SOAP faults are never retried) | | retryDelay | 500 | Base retry delay in ms, doubled per attempt (exponential backoff with jitter) | | timezone | 'Europe/Amsterdam' | IANA zone used to interpret/emit the API's wall-clock date structs (see Dates) | | signal | — | AbortSignal to cancel all in-flight requests from this client (e.g. on shutdown) |

Retries and idempotency

Retrying a mutation after an ambiguous network failure (e.g. a connection reset after the request was sent) could execute it twice — duplicate orders, double payments. The client guards against this:

  • Idempotent operations (requests carrying an idempotencyKey, e.g. createOrderV3, payOrderV2): the key is auto-generated (UUID) when you don't provide one, and any transport error is retried freely — the server deduplicates by key. Provide your own key to make retries safe across process restarts.
  • All other operations: only errors where the request provably never reached the server are retried (ECONNREFUSED, ENOTFOUND, DNS failures, unreachable host/network). Timeouts, connection resets, and HTTP 5xx responses are not retried — inspect MplusApiCommunicationError.code / .httpStatus and decide yourself.

All attempts of one call share the same X-Request-Id header.

Response guarantees

  • List fields are always present on responses ([] when the server omits them).
  • Complex response fields (e.g. GetRelationResponse.relation) are undefined when the server omits them — check the accompanying result field.
  • A required scalar field missing from a response throws MplusApiDeserializationError instead of silently producing ''/NaN/false.

Error Handling

All errors extend MplusApiError and carry the raw XML for debugging:

import {
  MplusApiClientError,      // SOAP fault with Client.* faultcode
  MplusApiServerError,      // SOAP fault with Server.* faultcode
  MplusApiCommunicationError, // network / HTTP error
  MplusApiSerializationError, // failed to build request XML
  MplusApiDeserializationError, // failed to parse response XML
} from '@izak0s/mplusqapi-node';

try {
  await client.getOrder('invalid-id');
} catch (err) {
  if (err instanceof MplusApiClientError) {
    console.error(`Client fault [${err.faultCode}]: ${err.message}`);
    console.error('Request XML:', err.xmlRequest);
    console.error('Response XML:', err.xmlResponse);
  } else if (err instanceof MplusApiServerError) {
    console.error(`Server fault [${err.faultCode}]: ${err.message}`);
  } else if (err instanceof MplusApiCommunicationError) {
    console.error(`Network error: ${err.message}`);
  }
}

Type System

Enums

Enum fields are typed as TypeScript string unions:

import type { OrderType, ArticleType } from '@izak0s/mplusqapi-node';

const type: OrderType = 'ORDER-TYPE-SALES-ORDER';

Money and decimal fields

The WSDL uses two conventions for money/quantity values, and the generated types mirror them faithfully:

  • xsd:decimalstring — fractional values like "12.50". Kept as strings end-to-end to preserve precision (the XML parser is configured to never coerce them to floats).
  • xsd:longnumber — scaled integers, typically cents (look for sibling fields like minimumAmountDecimalPlaces). Safe as JS numbers.
// xsd:decimal — string, e.g. SalesLineContractLine
const price: string = contractLine.priceIncl; // "12.50", not 12.5

// xsd:long — integer cents, e.g. Payment
const payment = { method: 'CASH', amount: 1000 }; // €10.00

The same field name (e.g. priceIncl) can be a string on one type and a number on another — trust the TypeScript type, it reflects what the API sends.

Dates

The API has two date structs, handled differently:

SoapMplusDateTime (full timestamp) — carries its own UTC offset (timezone, in minutes). Deserialized to an exact Date using that offset, independent of host or config:

// struct { year:2026, mon:6, day:12, hour:13, min:45, sec:30, timezone:120 }
// -> 2026-06-12T11:45:30.000Z   (exact instant; +120 min == +02:00)

If a SoapMplusDateTime ever omits its offset, the client's configured timezone (default Europe/Amsterdam) is used as a fallback to interpret the wall-clock. The configured zone is also used when serializing outbound timestamps (to fill timezone/isdst).

SoapMplusDate (calendar date, e.g. financialDate) — has only day/mon/year, no time and no offset. Deserialized to UTC midnight, so it reads as the intended calendar date with no off-by-one:

// struct { year:2018, mon:6, day:18 }  ->  2018-06-18T00:00:00.000Z
order.financialDate?.toISOString().slice(0, 10);   // '2018-06-18'
order.financialDate?.getUTCDate();                 // 18

Read date-only fields with the UTC accessors (getUTCFullYear/Month/Date) or .toISOString().slice(0, 10) — not getDate()/toLocaleDateString(), which shift by your host's zone. When sending a date-only field, build a UTC-midnight Date: new Date('2018-06-18') or new Date(Date.UTC(y, m - 1, d)).

Note: the configured timezone is process-wide — constructing multiple clients with different timezone values in one process is not supported. setTimeZone(tz) / getTimeZone() are also exported for direct control.

List fields

*List wrapper types are flattened to plain arrays in both requests and responses:

// Response: getOrders returns Order[] directly, not { orderList: ... }
const orders = await client.getOrders({});
const first: Order = orders[0];

// Request: pass an array directly
await client.payTableOrder({
  terminal: { branchNumber: 1, terminalNumber: 40 },
  paymentList: [{ method: 'CASH', amount: 1000 }], // amount in cents
});

Request input types

Request parameters use Input<T> — a deep-partial variant of the response types. All fields are optional when building requests; fields the WSDL marks required describe what responses are guaranteed to contain (e.g. Order.orderId is assigned by the server on create). Omitted fields are simply not serialized. The server validates true requirements at runtime and responds with a SOAP fault.

// Order without orderId — server assigns it
await client.createOrderV3({
  order: { lineList: [{ articleNumber: 123, data: { quantity: 1 } }] },
});

Request tracing

Pass an optional requestId as the last argument to any method for correlation logging:

const result = await client.getRelations({ syncMarker: 0 }, 'req-abc-123');

The ID is sent as the X-Request-Id header.


Contributing & security

Building from source, regenerating from the WSDL, the source layout, and the PR workflow are all in CONTRIBUTING.md.

Vulnerabilities: report privately via SECURITY.md, not public issues.


License

MIT