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

@zagi_14/przelewy24-ts-sdk

v0.1.0

Published

Modern, fully-typed Przelewy24 (P24) payments SDK for Node.js, Bun, and Deno — zero dependencies, native fetch, ESM + CJS.

Readme

@zagi_14/przelewy24-ts-sdk

Modern, fully-typed Przelewy24 (P24) payments SDK for Node.js — zero dependencies, native fetch, ESM + CJS.

npm version bundle size CI license

Why this SDK

Przelewy24 only ships official SDKs for iOS and Android — there is no first-party Node.js or TypeScript library. The most popular community alternative, node-przelewy24, has been effectively unmaintained for years: it still pins axios 0.21, declares Node 13 types, and is missing the card-charge and payment-methods endpoints that real production integrations need today. This SDK is a clean rewrite for modern JavaScript runtimes. It has zero runtime dependencies, uses the platform's native fetch, covers the full P24 REST v1 surface (including card-charge), ships both ESM and CJS builds with .d.ts types, and exposes rich TypeScript types for every request and response.

Install

pnpm add @zagi_14/przelewy24-ts-sdk
# or
npm install @zagi_14/przelewy24-ts-sdk

Requires Node.js >=20.11 (or any runtime with a global fetch and node:crypto).

Quick start

import { createClient, isP24Error } from '@zagi_14/przelewy24-ts-sdk';
import { verifyWebhook } from '@zagi_14/przelewy24-ts-sdk/webhooks';

const p24 = createClient({
  merchantId: 12345,
  posId: 12345,
  apiKey: process.env.P24_API_KEY!,
  crcKey: process.env.P24_CRC_KEY!,
  environment: 'sandbox',
});

// 1. Register a transaction and redirect the customer to P24.
const { redirectUrl } = await p24.registerTransaction({
  sessionId: 'order-2026-0001',
  amount: 1099, // 10.99 PLN expressed in groszy
  currency: 'PLN',
  description: 'Order #1',
  email: '[email protected]',
  urlReturn: 'https://shop.example.com/return',
  urlStatus: 'https://shop.example.com/api/p24/webhook',
});

// Send the customer to `redirectUrl`.

// 2. Handle the asynchronous webhook from P24.
async function onWebhook(rawBody: string): Promise<void> {
  const payload = verifyWebhook({
    merchantId: 12345,
    crcKey: process.env.P24_CRC_KEY!,
    payload: rawBody,
  });

  // 3. Confirm the transaction once the webhook is verified.
  try {
    await p24.verifyTransaction({
      sessionId: payload.sessionId,
      orderId: payload.orderId,
      amount: payload.amount,
      currency: payload.currency,
    });
  } catch (error) {
    if (isP24Error(error)) {
      console.error('P24 verify failed', error.name, error.message);
    }
    throw error;
  }
}

// 4. Issue a refund later if needed.
await p24.refund({
  requestId: 'refund-2026-0001',
  refundsUuid: '8c8b1f70-0000-4000-8000-000000000001',
  refunds: [
    {
      orderId: 987654,
      sessionId: 'order-2026-0001',
      amount: 1099,
      description: 'Customer requested refund',
    },
  ],
});

API reference

createClient(options) / new P24Client(options)

Both forms create a fully-configured client. createClient is a convenience factory; P24Client is the underlying class if you prefer new.

| Parameter | Type | Required | Description | | -------------- | ----------------------------- | -------- | ---------------------------------------------------------------------------- | | merchantId | number | yes | P24 merchant ID assigned to your account. | | posId | number | no | Point-of-sale ID. Defaults to merchantId (the common single-POS setup). | | apiKey | string | yes | The "Report key" from the P24 admin panel. | | crcKey | string | yes | The "CRC key" used to sign requests and verify webhooks. | | environment | 'sandbox' \| 'production' | no | Selects the base URL. Defaults to 'sandbox'. | | baseUrl | string | no | Overrides the base URL entirely (useful for tests / proxies). | | fetch | typeof fetch | no | Inject a custom fetch implementation (e.g. undici, msw). |

Returns: P24Client.

import { createClient } from '@zagi_14/przelewy24-ts-sdk';

const p24 = createClient({
  merchantId: 12345,
  apiKey: process.env.P24_API_KEY!,
  crcKey: process.env.P24_CRC_KEY!,
  environment: 'production',
});

client.testAccess()

Pings GET /api/v1/testAccess to verify that credentials are valid.

Returns: Promise<boolean>true when credentials are accepted.

const ok = await p24.testAccess();

client.registerTransaction(input)

Creates a transaction and returns the redirect URL the customer should be sent to.

| Parameter | Type | Required | Description | | -------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------- | | sessionId | string | yes | Merchant-provided unique ID for this transaction. | | amount | number | yes | Total amount in the smallest currency unit (groszy for PLN). | | currency | P24Currency | yes | One of 'PLN' \| 'EUR' \| 'USD' \| 'GBP' \| 'CZK'. | | description | string | yes | Short order description shown to the customer. | | email | string | yes | Customer email. | | urlReturn | string | yes | URL P24 redirects the customer to after payment. | | urlStatus | string | no | Webhook URL P24 posts the transaction notification to. | | country | string | no | ISO-3166 alpha-2 country code (e.g. 'PL'). | | language | string | no | UI language code (e.g. 'pl', 'en'). | | timeLimit | number | no | Time limit in minutes (0 = no limit). | | encoding | string | no | Character encoding for legacy integrations. | | client | string | no | Optional customer name. | | channel | number | no | Channels bitmask restricting available payment methods. | | cart | ReadonlyArray<CartItem> | no | Optional line items (name, quantity, price in groszy). |

Returns: Promise<RegisterTransactionResult>{ token, redirectUrl }.

const { token, redirectUrl } = await p24.registerTransaction({
  sessionId: 'order-1',
  amount: 1099,
  currency: 'PLN',
  description: 'Order #1',
  email: '[email protected]',
  urlReturn: 'https://shop.example.com/return',
});

client.verifyTransaction(input)

Confirms a transaction after the webhook arrives. P24 requires this call to mark the payment as settled.

| Parameter | Type | Required | Description | | ----------- | ------------- | -------- | -------------------------------------------------------- | | sessionId | string | yes | The same sessionId used in registerTransaction. | | orderId | number | yes | The P24-assigned order ID from the webhook payload. | | amount | number | yes | The verified amount in the smallest currency unit. | | currency | P24Currency | yes | Must match the registered currency. |

Returns: Promise<{ status: 'success' }>.

await p24.verifyTransaction({
  sessionId: 'order-1',
  orderId: 987654,
  amount: 1099,
  currency: 'PLN',
});

client.getTransactionBySessionId(sessionId)

Looks up the current status of a transaction by merchant session ID.

| Parameter | Type | Required | Description | | ----------- | -------- | -------- | ------------------------------------------ | | sessionId | string | yes | The merchant-provided session identifier. |

Returns: Promise<TransactionStatus>.

const status = await p24.getTransactionBySessionId('order-1');
console.log(status.orderId, status.status);

client.refund(input)

Refunds one or more transactions in a single batch request.

| Parameter | Type | Required | Description | | ------------- | ---------------------------- | -------- | -------------------------------------------------------------------- | | requestId | string | yes | Merchant-provided idempotency key for this refund batch. | | refundsUuid | string | yes | UUID identifying the refund operation. | | refunds | ReadonlyArray<RefundEntry> | yes | Individual refunds: { orderId, sessionId, amount, description }. | | urlStatus | string | no | Webhook URL that P24 posts refund status updates to. |

Returns: Promise<RefundResult>{ responseCode, data: [{ orderId, sessionId, status, message? }] }.

await p24.refund({
  requestId: 'refund-1',
  refundsUuid: '8c8b1f70-0000-4000-8000-000000000001',
  refunds: [
    { orderId: 987654, sessionId: 'order-1', amount: 500, description: 'Partial refund' },
  ],
});

client.listPaymentMethods(language?)

Lists payment methods available for the configured merchant.

| Parameter | Type | Required | Description | | ---------- | -------- | -------- | ---------------------------------------------------------- | | language | string | no | UI language code for human-readable names. Default 'pl'. |

Returns: Promise<readonly PaymentMethod[]>.

const methods = await p24.listPaymentMethods('en');
for (const method of methods) {
  console.log(method.id, method.name, method.status);
}

client.chargeCard(input)

Charges a previously-tokenised card for a registered transaction (recurring / one-click payments).

| Parameter | Type | Required | Description | | ----------- | ------------- | -------- | ------------------------------------------------------ | | sessionId | string | yes | The session ID of the transaction to charge. | | orderId | number | yes | The P24 order ID of the parent transaction. | | amount | number | yes | Amount to charge, in the smallest currency unit. | | currency | P24Currency | yes | Currency of the charge. |

Returns: Promise<{ orderId: number }>.

const { orderId } = await p24.chargeCard({
  sessionId: 'subscription-1',
  orderId: 987654,
  amount: 1999,
  currency: 'PLN',
});

Webhooks

P24 sends an asynchronous notification (the "transaction status" webhook) to the urlStatus you registered. You must verify the SHA-384 signature on every webhook before trusting any field. The standalone helper at @zagi_14/przelewy24-ts-sdk/webhooks does not require an SDK client instance, so it is cheap to call from edge runtimes and serverless handlers.

import type { Request, Response } from 'express';
import { verifyWebhook } from '@zagi_14/przelewy24-ts-sdk/webhooks';
import { isP24Error } from '@zagi_14/przelewy24-ts-sdk';

export function p24WebhookHandler(req: Request, res: Response): void {
  try {
    const payload = verifyWebhook({
      merchantId: Number(process.env.P24_MERCHANT_ID),
      crcKey: process.env.P24_CRC_KEY ?? '',
      payload: req.body as string, // raw body, registered with express.text()
    });
    // payload is a fully-typed WebhookPayload — enqueue verifyTransaction here.
    void payload;
    res.status(200).end();
  } catch (error) {
    if (isP24Error(error)) {
      res.status(400).end();
      return;
    }
    throw error;
  }
}

If you are on Nuxt or Next.js, the framework adapters below wrap this helper into a typed event handler so you do not have to deal with raw bodies yourself.

Environments

| Environment | Base URL | When to use | | ------------- | --------------------------------- | -------------------------------------------------------------------------- | | sandbox | https://sandbox.przelewy24.pl | Default. Test transactions, free, isolated P24 sandbox account. | | production | https://secure.przelewy24.pl | Real money. Set environment: 'production' in the client options. |

To obtain credentials, log into the P24 admin panel, then go to My Account → My Data → API Data and Configuration. You need three values:

  • posId — usually the same as your merchantId for a single-POS setup.
  • apiKey — listed as the Report key in the admin panel.
  • crcKey — listed as the CRC key in the admin panel.

Amounts and currencies

All amount fields across the SDK are integers expressed in the smallest unit of the currency: groszy for PLN, cents for EUR / USD / GBP, halerze for CZK. So 1099 means 10.99 PLN, not 1099 PLN. Always store amounts as integers in your database too — never multiply floats by 100 at the API boundary.

Supported currencies: PLN, EUR, USD, GBP, CZK.

Errors

Every error thrown by the SDK is a subclass of P24Error. Use the isP24Error type guard to narrow unknown from a catch block.

| Class | When it throws | Properties | | -------------------- | -------------------------------------------------------------------- | -------------------------------- | | P24ApiError | P24 returned a non-2xx HTTP response. | status, code?, body? | | P24NetworkError | The underlying fetch rejected (DNS, socket, timeout, etc.). | cause | | P24SignatureError | Webhook payload is malformed, has a wrong merchantId, or bad sign. | — | | isP24Error(e) | Type guard that matches any of the above. | — |

import { isP24Error, P24ApiError } from '@zagi_14/przelewy24-ts-sdk';

try {
  await p24.registerTransaction({
    sessionId: 'order-1',
    amount: 1099,
    currency: 'PLN',
    description: 'Order #1',
    email: '[email protected]',
    urlReturn: 'https://shop.example.com/return',
  });
} catch (error) {
  if (error instanceof P24ApiError) {
    console.error(error.status, error.code, error.body);
  } else if (isP24Error(error)) {
    console.error(error.name, error.message);
  } else {
    throw error;
  }
}

Signing rules

P24 signs every request body and webhook with SHA-384 over a JSON-encoded subset of the payload, in a documented field order. The SDK does this for you, but the table below is a useful reference for anyone verifying signatures by hand (or in another language).

| Endpoint / event | Hashed fields, in order | | ---------------------- | ------------------------------------------------------------------------------------------------ | | transaction/register | sessionId, merchantId, amount, currency, crc | | transaction/verify | sessionId, orderId, amount, currency, crc | | transaction/refund | requestId, refundsUuid, amount, currency, crc | | webhook notification | merchantId, posId, sessionId, amount, originAmount, currency, orderId, methodId, statement, crc| | card/charge | sessionId, orderId, amount, currency, crc |

The signature is sha384(JSON.stringify({ ...fieldsInOrder })).digest('hex'). The SDK exposes signFields from the public surface if you need to reproduce a signature in test code:

import { signFields } from '@zagi_14/przelewy24-ts-sdk';

const sign = signFields({
  sessionId: 'order-1',
  merchantId: 12345,
  amount: 1099,
  currency: 'PLN',
  crc: 'your-crc-key',
});

Framework adapters

  • nuxt-przelewy24 — Nuxt 4 module that wires this SDK into useP24() server composables and a verified webhook event handler.
  • next-przelewy24 — Next.js 15 App Router helpers and a typed route handler for the webhook endpoint.

Contributing

pnpm install
pnpm test:coverage
pnpm check
pnpm build

This repo uses Conventional Commits (feat:, fix:, docs:, chore:, test:, refactor:). Please do not add Co-Authored-By trailers — the commit log is the source of truth for CHANGELOG.md generation and keeping it clean matters.

License

MIT © 2026 Michał Zagalski