Node.js / TypeScript SDK
The TypeScript SDK targets Node 20+ and modern browsers (where the target use case warrants — keep secrets server-side).
npm install @paylera/sdkConstruct a client
import { Paylera } from "@paylera/sdk";
const paylera = new Paylera({ apiKey: process.env.PAYLERA_KEY!, baseUrl: Paylera.Environment.Sandbox, // or Live apiVersion: "2026-04-01",});Make a call
const customer = await paylera.customers.create({ email: "ada@example.com", name: "Ada Lovelace", billing_address: { country: "US", postal_code: "94105" },});The SDK auto-generates an idempotency key. Override:
await paylera.customers.create({ … }, { idempotencyKey: "my-key" });Errors
try { await paylera.invoices.pay(invoiceId);} catch (err) { if (err instanceof Paylera.ApiError) { if (err.problem === "payment.requires_action") { const url = err.nextAction?.url; // … } else { console.warn("paylera error", { problem: err.problem, traceId: err.traceId }); } return; } throw err;}Paylera.ApiError exposes status, problem, detail, traceId,
errors, and nextAction directly.
List with pagination
for await (const sub of paylera.subscriptions.list({ status: "active" })) { console.log(sub.id, sub.plan_code, sub.status);}Async iterators walk pages for you. Limit with a count if you want a slice:
let n = 0;for await (const sub of paylera.subscriptions.list({ status: "active" })) { if (++n > 100) break; // …}Money
import { Money } from "@paylera/sdk";
const a = Money.of("29.00", "USD");const b = Money.of("5", "USD");const sum = a.add(b); // throws if currencies differconsole.log(sum.toString()); // "34.00 USD"Webhook verification (Express)
import express from "express";import { Webhooks } from "@paylera/sdk";
const app = express();
app.post( "/webhooks/paylera", express.raw({ type: "application/json" }), // get the bytes (req, res) => { const sig = req.header("Paylera-Signature")!; if (!Webhooks.verify(req.body, sig, process.env.PAYLERA_WEBHOOK_SECRET!)) { return res.status(401).send("invalid signature"); } const evt = Webhooks.parse(req.body); // dispatch on evt.type, evt.id, evt.data res.status(204).end(); },);The key trick: express.raw for the route, so req.body is a
Buffer of the original bytes. Don’t add express.json ahead of it
on this route.
Webhook verification (Next.js / Edge)
import type { NextApiRequest, NextApiResponse } from "next";import { Webhooks } from "@paylera/sdk";
export const config = { api: { bodyParser: false } };
async function rawBody(req: NextApiRequest): Promise<Buffer> { const chunks: Buffer[] = []; for await (const chunk of req) chunks.push(chunk as Buffer); return Buffer.concat(chunks);}
export default async function handler(req: NextApiRequest, res: NextApiResponse) { const body = await rawBody(req); const sig = req.headers["paylera-signature"] as string; if (!Webhooks.verify(body, sig, process.env.PAYLERA_WEBHOOK_SECRET!)) { return res.status(401).end(); } const evt = Webhooks.parse(body); // … res.status(204).end();}Retries
Defaults: 5 attempts on 429/5xx, jittered exponential backoff. Tune:
new Paylera({ apiKey, retry: { maxAttempts: 5, baseDelayMs: 250, maxDelayMs: 8000 },});maxAttempts: 0 disables retries.
Source
github.com/paylera/paylera-node — issues and PRs welcome.