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 +
timingSafeEqualand 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-nextjsPeer dependencies (installed in your Next.js app):
npm install next react react-domEnvironment Variables
In your Next.js app, configure:
EPAY_API_KEY– ePay secret API keyEPAY_WEBHOOK_SECRET– signing secret for webhooksEPAY_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
EpayClientemits 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:watchVitest + 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 publicThis package is designed to be stable, type-safe, and secure for long-term use across both Next.js router paradigms.
