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/next-przelewy24

v0.1.0

Published

Next.js 15+ App Router helpers for Przelewy24 (P24) — webhook Route Handler factory, Server Action checkout helper, server-only enforced.

Readme

@zagi_14/next-przelewy24

Next.js 15+ App Router helpers for Przelewy24 (P24) — webhook Route Handler factory and Server Action checkout helper, both server-only-enforced.

npm version CI license

Why this package

@zagi_14/przelewy24-ts-sdk is a runtime-agnostic SDK. @zagi_14/next-przelewy24 is a thin layer over it that fits the App Router shape:

  • A POST factory for app/api/p24/webhook/route.ts that handles body parsing, signature verification, and the 200/400 split.
  • A factory that wraps client.registerTransaction so you can expose it as a Server Action with one line.
  • Every entry imports 'server-only', so accidentally importing the module from a client component fails the build instead of leaking secrets.

If you need anything beyond the webhook + checkout-action pair (refunds, card-charge, payment-methods, verify, etc.), call the underlying @zagi_14/przelewy24-ts-sdk directly from a server component or server action.

Install

pnpm add @zagi_14/next-przelewy24
# or
npm install @zagi_14/next-przelewy24

Requires Node.js >=20.11 and next ^15.0.0 as a peer.

Subpath imports

The package ships three entry points so you can import only what you need:

| Specifier | Exports | | -------------------------- | ---------------------------------------------------------------------- | | @zagi_14/next-przelewy24 | createP24Server, createCheckoutAction, createWebhookHandler, plus re-exported types from @zagi_14/przelewy24-ts-sdk | | @zagi_14/next-przelewy24/webhook | createWebhookHandler | | @zagi_14/next-przelewy24/actions | createCheckoutAction |

createP24Server is just createClient from the core SDK, re-exported behind a server-only boundary.

Quick start

1. Webhook Route Handler

app/api/p24/webhook/route.ts:

import { createWebhookHandler } from '@zagi_14/next-przelewy24/webhook';

export const runtime = 'nodejs';

export const POST = createWebhookHandler({
  merchantId: Number(process.env.P24_MERCHANT_ID),
  crcKey: process.env.P24_CRC_KEY!,
  onNotification: async (payload) => {
    // payload is already signature-verified
    await saveOrderStatus(payload);
  },
});

The handler returns 200 { received: true } on success, 400 on malformed JSON or an invalid signature, and 500 on unexpected errors thrown from onNotification.

2. Checkout Server Action

app/checkout/actions.ts:

'use server';
import { createCheckoutAction, createP24Server } from '@zagi_14/next-przelewy24';

export const checkoutAction = createCheckoutAction({
  client: () =>
    createP24Server({
      merchantId: Number(process.env.P24_MERCHANT_ID),
      apiKey: process.env.P24_API_KEY!,
      crcKey: process.env.P24_CRC_KEY!,
    }),
});

Passing a factory (() => createP24Server(...)) defers env-var reads until the action runs, which keeps next build from blowing up when secrets aren't set at build time. You can also pass a long-lived P24Client instance instead.

app/checkout/page.tsx:

import { redirect } from 'next/navigation';
import { checkoutAction } from './actions';

export default function CheckoutPage() {
  async function action(formData: FormData) {
    'use server';
    const { redirectUrl } = await checkoutAction({
      sessionId: `order-${Date.now()}`,
      amount: 1099,
      currency: 'PLN',
      description: 'Demo order',
      email: String(formData.get('email') ?? ''),
      urlReturn: 'https://shop.example.com/thanks',
      urlStatus: 'https://shop.example.com/api/p24/webhook',
    });
    redirect(redirectUrl);
  }

  return (
    <form action={action}>
      <input name="email" type="email" required />
      <button type="submit">Pay 10.99 PLN</button>
    </form>
  );
}

A complete runnable example lives in examples/next-app.

How 'use server' and 'server-only' interact

The two directives solve different problems and you typically want both:

  • 'use server' (in a file or function) marks an export as a Server Action — Next compiles it into an RPC endpoint that the client can invoke. The module body still runs only on the server.
  • 'server-only' (this package) is an import-time guard: pulling it into a client component fails the bundle. It protects modules that hold secrets (your CRC/API keys, the P24Client) from ever ending up in the browser.

All three exports from @zagi_14/next-przelewy24 import 'server-only', so even if you forget 'use server', importing them from a client component will break next build.

Environment variables

The example app reads three values:

| Variable | Description | | -------------------- | ------------------------------------------ | | P24_MERCHANT_ID | Your P24 merchant ID (numeric). | | P24_API_KEY | The "Report key" from the P24 admin panel. | | P24_CRC_KEY | The "CRC key" from the P24 admin panel. |

The package itself doesn't read process.env — you wire it up at the call site.

Full P24 API

For everything outside webhooks + checkout (refunds, payment methods, card-charge, verify, …) call the underlying @zagi_14/przelewy24-ts-sdk directly:

import { createP24Server } from '@zagi_14/next-przelewy24';

const p24 = createP24Server({ /* … */ });
await p24.verifyTransaction({ /* … */ });
await p24.refund({
  requestId: crypto.randomUUID(),
  refundsUuid: crypto.randomUUID(),
  refunds: [{
    orderId: 12345,
    sessionId: 'order-1',
    amount: 1099,
    description: 'Customer requested refund',
  }],
});

Contributing

Issues and PRs welcome at github.com/zagi/next-przelewy24. Run pnpm install, then pnpm test / pnpm build / pnpm check.

License

MIT © 2026 Michał Zagalski