Skip to content

Node.js / TypeScript SDK

The TypeScript SDK targets Node 20+ and modern browsers (where the target use case warrants — keep secrets server-side).

Terminal window
npm install @paylera/sdk

Construct 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 differ
console.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.