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

epay-nextjs

v1.0.4

Published

ePay EU payment gateway integration for Next.js (App Router & Pages Router)

Readme

epay-nextjs

Type-safe, production-grade ePay EU payment gateway integration for Next.js App Router and Pages Router in a single NPM package.

Features

  • Full ePay Payments API coverage via modular services:
    • Sessions (CIT) / MIT / MOTO
    • Transactions (capture, refund, void, operations)
    • Payouts (async/sync)
    • Subscriptions & Subscription Billing
    • Payment Links
    • Webhooks (store/list/delete)
    • Management (accounts, points of sale, API keys)
    • Partner APIs (accounts & access tokens)
  • Strict TypeScript schemas for all main ePay resources (sessions, transactions, payouts, subs, billing, links, webhooks, management, partner)
  • Secure webhooks with HMAC SHA-256 + timingSafeEqual and raw body verification
  • React provider and hooks for client-side checkout flows
  • Next.js Pages Router helpers (NextApiRequest / NextApiResponse)
  • Next.js App Router helpers (NextRequest / NextResponse, server actions)
  • Structured logging with pluggable loggers for audit-able output
  • Optional admin panel components (transactions, merchants/POS, subscriptions, billing) that you can drop into any Next.js app
  • No API keys in client code – always server-side

Installation

npm install epay-nextjs

Peer dependencies (installed in your Next.js app):

npm install next react react-dom

Environment Variables

In your Next.js app, configure:

  • EPAY_API_KEY – ePay secret API key
  • EPAY_WEBHOOK_SECRET – signing secret for webhooks
  • EPAY_POS_ID – ePay Point of Sale ID (for session initialization, optional but recommended)

Never expose these values in client-side code.


Usage – Pages Router

API route: create session

// pages/api/epay/create-session.ts
import { createSessionApiHandler } from 'epay-nextjs/pages';

export default createSessionApiHandler({
  apiKey: process.env.EPAY_API_KEY!,
});

API route: webhook

// pages/api/epay/webhook.ts
import { webhookApiHandler } from 'epay-nextjs/pages';

export const config = {
  api: {
    bodyParser: false, // required to access rawBody
  },
};

export default webhookApiHandler({
  secret: process.env.EPAY_WEBHOOK_SECRET!,
  onEvent: async (event) => {
    // handle ePay events (update DB, send emails, etc.)
  },
});

Client-side checkout hook

// components/CheckoutButton.tsx
'use client';

import { useCheckout, EpayProvider } from 'epay-nextjs';

export function CheckoutButton() {
  const { createCheckout, redirectToCheckout, loading } = useCheckout('/api/epay/create-session');

  const handleClick = async () => {
    const session = await createCheckout({
      pointOfSaleId: process.env.NEXT_PUBLIC_EPAY_POS_ID as string,
      amount: 1000, // minor units, e.g. 100 = 1.00 DKK
      currency: 'DKK',
      successUrl: 'https://example.com/success',
      failureUrl: 'https://example.com/failure',
    });
    // For Checkout: redirect to paymentWindowUrl
    redirectToCheckout(session.paymentWindowUrl!);
  };

  return (
    <button onClick={handleClick} disabled={loading}>
      Pay with ePay
    </button>
  );
}

Wrap your tree with EpayProvider:

// pages/_app.tsx
import type { AppProps } from 'next/app';
import { EpayProvider } from 'epay-nextjs';

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <EpayProvider
      config={{
        apiKey: 'public-placeholder', // NOT used for server calls; real key stays on server
        environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',
      }}
    >
      <Component {...pageProps} />
    </EpayProvider>
  );
}

Usage – App Router

Route handler: create session

// app/api/epay/create-session/route.ts
import { createSessionRouteHandler } from 'epay-nextjs/app';

export const POST = createSessionRouteHandler({
  apiKey: process.env.EPAY_API_KEY!,
});

Route handler: webhook

// app/api/epay/webhook/route.ts
import { webhookRouteHandler } from 'epay-nextjs/app';

export const POST = webhookRouteHandler({
  secret: process.env.EPAY_WEBHOOK_SECRET!,
  onEvent: async (event) => {
    // handle ePay event
  },
});

Server actions

// app/(shop)/actions.ts
'use server';

import {
  createPaymentSessionAction,
  createTransactionAction,
} from 'epay-nextjs/app';

export async function createCheckoutSession() {
  return createPaymentSessionAction(process.env.EPAY_API_KEY!, {
    pointOfSaleId: process.env.EPAY_POS_ID!,
    amount: 1000,
    currency: 'DKK',
    successUrl: 'https://example.com/success',
    failureUrl: 'https://example.com/failure',
  });
}

Low-level Core Usage

// any server-side file
import { buildEpayConfig, EpayClient } from 'epay-nextjs/server';

const config = buildEpayConfig(process.env.EPAY_API_KEY!);
const client = new EpayClient(config);

async function createSession() {
  const session = await client.createSession({
    pointOfSaleId: process.env.EPAY_POS_ID!,
    amount: 1000,
    currency: 'DKK',
    successUrl: 'https://example.com/success',
    failureUrl: 'https://example.com/failure',
  });

  return session;
}

Admin Panel Components

This package includes optional, themeable React components you can use to build an ePay admin panel in Next.js.

  • AdminLayout – page shell with branding (title, logo, colors, fonts).
  • TransactionsPanel – paginated transaction list with basic filters.
  • MerchantsPanel – merchant accounts & points of sale.
  • SubscriptionsPanel – subscriptions list with customer filter.
  • BillingPanel – billing plans & agreements.

Example (App Router) using server loaders:

// app/(admin)/epay-loaders.ts
import { buildEpayConfig, EpayClient } from 'epay-nextjs/server';

function client() {
  const config = buildEpayConfig(process.env.EPAY_API_KEY!, {
    apiUrl: 'https://payments.epay.eu/public/api',
  });
  return new EpayClient(config);
}

export async function loadTransactions(params?: {
  cursor?: string;
  limit?: number;
  status?: string;
  reference?: string;
}) {
  return client().transactions.list(params);
}

export async function loadAccounts(params?: { cursor?: string; limit?: number }) {
  return client().management.listAccounts(params);
}

export async function loadPointsOfSale(params?: { cursor?: string; limit?: number }) {
  return client().management.listPointsOfSale(params.cursor, params.limit);
}

export async function loadSubscriptions(params?: {
  cursor?: string;
  limit?: number;
  customerId?: string;
}) {
  return client().subscriptions.list(params);
}

export async function loadBillingPlans(params?: { cursor?: string; limit?: number }) {
  return client().billing.listPlans(params);
}

export async function loadBillingAgreements(params?: {
  cursor?: string;
  limit?: number;
  customerId?: string;
}) {
  return client().billing.listAgreements(params);
}
// app/(admin)/epay/page.tsx
'use client';

import {
  AdminLayout,
  TransactionsPanel,
  MerchantsPanel,
  SubscriptionsPanel,
  BillingPanel,
} from 'epay-nextjs';
import {
  loadTransactions,
  loadAccounts,
  loadPointsOfSale,
  loadSubscriptions,
  loadBillingPlans,
  loadBillingAgreements,
} from './epay-loaders';

export default function EpayAdminPage() {
  return (
    <AdminLayout
      title="Payments Admin"
      logoUrl="/your-logo.svg"
      theme={{
        primaryColor: '#0f766e',
        primaryTextColor: '#ffffff',
        surfaceBg: '#ffffff',
        pageBg: '#f1f5f9',
        borderColor: '#e2e8f0',
        mutedTextColor: '#64748b',
        fontFamily: 'system-ui, sans-serif',
      }}
    >
      <div style={{ display: 'grid', gap: 16 }}>
        <MerchantsPanel
          loadAccounts={loadAccounts}
          loadPointsOfSale={loadPointsOfSale}
          pageSize={20}
        />

        <TransactionsPanel loadTransactions={loadTransactions} pageSize={20} />

        <SubscriptionsPanel loadSubscriptions={loadSubscriptions} pageSize={20} />

        <BillingPanel
          loadPlans={loadBillingPlans}
          loadAgreements={loadBillingAgreements}
          pageSize={20}
        />
      </div>
    </AdminLayout>
  );
}

The panels are fully customizable: you control which panels to render, how loaders are implemented, and the visual theme via AdminLayout.theme.


Observability & Logging

  • Every request made through EpayClient emits structured logs (level, message, metadata, timestamp).
  • By default, logs go to console.* with the [epay-nextjs] prefix so hosting platforms capture them automatically.
  • Provide your own logger (e.g., to pipe into Datadog, Sentry, Logtail):
import { EpayClient } from 'epay-nextjs/server';
import type { EpayLogger } from 'epay-nextjs';

const logger: EpayLogger = {
  log(entry) {
    myObservabilityTool.capture(entry);
  },
};

const client = new EpayClient({
  apiKey: process.env.EPAY_API_KEY!,
  environment: 'production',
  logger,
});
  • The included logger captures successes, API failures, and network errors so developers immediately see what happened in their own logs after installing the library.

Testing

This package ships with full unit tests covering all shared utilities, fetch clients, React hooks, and webhook helpers.

# run once
npm test

# watch mode
npm run test:watch

Vitest + JSDOM power the tests, and coverage reports (text + lcov) are generated automatically.


Build & Publish

# build
npm run build

# test in a local Next.js app (optional)
npm link
cd ../your-next-app
npm link epay-nextjs

# publish
npm login
npm publish --access public

This package is designed to be stable, type-safe, and secure for long-term use across both Next.js router paradigms.