Skip to content

Node.js: @tuppence/node

The server-side SDK for Node 18+: typed objects, an idempotency key on every write, safe retries and webhook verification.

Terminal window
npm install @tuppence/node
import { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);
const customer = await tuppence.customers.create({ name: "Ada Lovelace", email: "ada@example.com" });
const payment = await tuppence.payments.create({
amount: 2000,
currency: "gbp",
customer: customer.id,
});
console.log(payment.id, payment.status, payment.approval_url);
// Iterate every item in a list. Pages are fetched as you reach them.
for await (const c of tuppence.customers.listAll({ limit: 100 })) {
console.log(c.id, c.email);
break;
}
import { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);
const customer = await tuppence.customers.create({ name: "Ada Lovelace", email: "ada@example.com" });
const payment = await tuppence.payments.create({
amount: 2000,
currency: "usd",
customer: customer.id,
});
console.log(payment.id, payment.status, payment.approval_url);
// Iterate every item in a list. Pages are fetched as you reach them.
for await (const c of tuppence.customers.listAll({ limit: 100 })) {
console.log(c.id, c.email);
break;
}
import { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);
const customer = await tuppence.customers.create({ name: "Ada Lovelace", email: "ada@example.com" });
const payment = await tuppence.payments.create({
amount: 2000,
currency: "eur",
customer: customer.id,
});
console.log(payment.id, payment.status, payment.approval_url);
// Iterate every item in a list. Pages are fetched as you reach them.
for await (const c of tuppence.customers.listAll({ limit: 100 })) {
console.log(c.id, c.email);
break;
}
TaskCall
Create a payment linktuppence.paymentLinks.create({ name, amount, currency })
Create a paymenttuppence.payments.create({ amount, currency, customer })
Confirm one (test mode)tuppence.payments.confirm(id, { payment_method: "pm_card_gb" })tuppence.payments.confirm(id, { payment_method: "pm_card_us" })tuppence.payments.confirm(id, { payment_method: "pm_card_de" })
Capture, canceltuppence.payments.capture(id), tuppence.payments.cancel(id)
Refundtuppence.refunds.create({ payment, amount })
Customerstuppence.customers.create / retrieve / update / list / listAll
Balancetuppence.balance.retrieve()
Pay outtuppence.payouts.create({}), tuppence.payouts.settings()
Webhook endpointstuppence.webhookEndpoints.create / list / retrieve / update / del
Verify a webhooktuppence.webhooks.constructEvent(rawBody, signatureHeader, secret)
Act as a connected accounttuppence.as("acct_…") returns a client that sends Tuppence-Account
Anything elsetuppence.request("GET", "/v1/…", { body, query }), with the same auth, retries and idempotency

Every list has list(params) for one page and listAll(params), an async iterator over all pages.

TypesFor every object and request, generated from the API’s contract so they can’t lag it
Idempotency-KeyOn every POST, reused across that call’s retries; pass { idempotencyKey } to choose it
RetriesTwice by default, with jittered backoff, honouring Retry-After. Retries network errors, timeouts, 5xx, 429 and a request still in flight; never another 4xx
Typed errorsCardError, InvalidRequestError, AuthenticationError, PermissionError, RateLimitError, IdempotencyError, ApiError, ConnectionError. All extend TuppenceError, with code, param, declineCode, requestId and the full body on raw
import { CardError } from "@tuppence/node";
try {
await tuppence.payments.confirm(paymentId, { payment_method: "pm_card_chargeDeclined" });
} catch (err) {
if (err instanceof CardError) console.log(err.code, err.declineCode, err.requestId);
else throw err;
}

Get a quote first. It says what arrives and when, or why not and where to fix it. The bank is the default destination. The card balance (destination: "card_balance", UK only) is described before it can be used. See Instant payouts.

const quote = await tuppence.payouts.instantQuote({ amount: 5872 });
if (quote.available) {
// £58.13 arrives within 30 minutes. The £0.59 fee comes out of the £58.72.
await tuppence.payouts.create({ method: "instant", amount: quote.total_amount });
} else {
console.log(quote.reason, quote.message, quote.fix_url, quote.available_from);
}
const { instant_payouts } = await tuppence.payouts.settings();
console.log(instant_payouts.destinations.card_balance.message);
const quote = await tuppence.payouts.instantQuote({ amount: 5872 });
if (quote.available) {
// $58.13 arrives within 30 minutes. The $0.59 fee comes out of the $58.72.
await tuppence.payouts.create({ method: "instant", amount: quote.total_amount });
} else {
console.log(quote.reason, quote.message, quote.fix_url, quote.available_from);
}
const { instant_payouts } = await tuppence.payouts.settings();
console.log(instant_payouts.destinations.card_balance.message);
const quote = await tuppence.payouts.instantQuote({ amount: 5872 });
if (quote.available) {
// €58.13 arrives within 30 minutes. The €0.59 fee comes out of the €58.72.
await tuppence.payouts.create({ method: "instant", amount: quote.total_amount });
} else {
console.log(quote.reason, quote.message, quote.fix_url, quote.available_from);
}
const { instant_payouts } = await tuppence.payouts.settings();
console.log(instant_payouts.destinations.card_balance.message);

payouts.setNextDay switches next-day payouts (UK) on and off. Until next_day.available, the call is refused with the reason. account.retrievePricing returns every price the account pays now (list price or one agreed with Tuppence) with its dates.

const { next_day } = await tuppence.payouts.settings();
if (next_day.available) await tuppence.payouts.setNextDay({ enabled: true });
const { prices } = await tuppence.account.retrievePricing();
for (const p of prices) console.log(p.type, p.bps, p.fixed, p.source, p.ends_at);
new Tuppence(key, {
apiVersion: "2026-09-20", // pin a version (default: your account's)
maxRetries: 4,
timeoutMs: 10_000,
});
await tuppence.balance.retrieve({ maxRetries: 0, timeoutMs: 2_000 }); // per request

baseUrl (or TUPPENCE_API_URL in the environment) points it at another API, such as a test environment.

Docs assistant

Open as a page
Loading the assistant…