Production-grade TypeScript SDK for the Paysafe API.
| Bounded context | Resources |
|---|---|
| Payments | Payment Handles, Payments, Settlements, Refunds, Payouts (standalone & original credits), Verifications |
| Customer Vault | Customer profiles, saved payment handles, single-use customer tokens |
| Payment Scheduler | Plans, Subscriptions (create, update, cancel, suspend, reactivate) |
| Applications | Merchant onboarding: create, update, submit, terms & conditions, document upload |
| Value Added Services | FX Rates, Customer Identity (KYC), Bank Account Validation, Interac Verification (VerifiedMe) |
| Webhooks | HMAC-SHA256 signature verification (constant-time) and event parsing |
npm install paysafe-sdkRequires Node.js >= 18. Also runs on any runtime with fetch and either node:crypto or Web Crypto (SubtleCrypto) — browsers, Deno, Cloudflare Workers, and other edge runtimes.
import { PaysafeClient } from "paysafe-sdk";
const client = new PaysafeClient({
username: process.env.PAYSAFE_USERNAME!,
password: process.env.PAYSAFE_PASSWORD!,
environment: "test", // or "production"
accountId: process.env.PAYSAFE_ACCOUNT_ID!,
});
// 1. Tokenize a card into a Payment Handle.
const handle = await client.paymentHandles.create({
merchantRefNum: "order-1",
amount: 5000, // minor currency units — $50.00
currencyCode: "USD",
paymentType: "CARD",
transactionType: "PAYMENT",
card: {
cardNum: "4111111111111111",
cardExpiry: { month: 12, year: 2030 },
cvv: "123",
holderName: "Jane Doe",
},
});
// 2. Charge it.
const payment = await client.payments.create({
merchantRefNum: "order-1",
amount: 5000,
currencyCode: "USD",
paymentHandleToken: handle.paymentHandleToken,
settleWithAuth: true,
});
console.log(payment.status); // "COMPLETED"Or build a client from PAYSAFE_USERNAME / PAYSAFE_PASSWORD / PAYSAFE_ENVIRONMENT / PAYSAFE_ACCOUNT_ID environment variables:
const client = PaysafeClient.fromEnv();See examples/ for a full payment flow, a subscription signup flow, and a webhook receiver.
Every rejected promise from this SDK rejects with a PaysafeError — never a bare string or plain object:
import { PaysafeError } from "paysafe-sdk";
try {
await client.payments.create(req);
} catch (err) {
if (err instanceof PaysafeError) {
console.error(err.kind); // "api_error" | "http_error" | "rate_limited" | "timeout" | ...
console.error(err.code); // Paysafe error code, e.g. "5068"
console.error(err.httpStatus); // e.g. 400
console.error(err.fieldErrors); // [{ field: "card.cardNum", error: "Invalid card number" }]
console.error(err.retryable); // whether the SDK's own retry policy would retry this
}
}import { WebhooksResource, webhookTopic, PaysafeError } from "paysafe-sdk";
const webhooks = new WebhooksResource();
// In your HTTP handler, using the *raw* request body:
try {
const event = await webhooks.verifyAndParse(rawBody, req.headers["signature"], hmacKey);
switch (webhookTopic(event)) {
case "payment_handle":
// ...
break;
// ...
}
res.status(200).send("OK");
} catch (err) {
if (err instanceof PaysafeError && err.kind === "webhook_signature_mismatch") {
res.status(401).send("Invalid signature");
}
}Signature verification uses a constant-time comparison and works identically on Node.js (node:crypto) and Web Crypto runtimes (browsers, Deno, Cloudflare Workers).
new PaysafeClient({
username: string, // required
password: string, // required
environment?: "test" | "production", // default: "test"
accountId?: string, // default account ID for resources that need one
baseUrlOverride?: string, // override the base URL entirely (e.g. for a mock server in tests)
timeoutMs?: number, // per-request timeout, default 30_000
maxRetries?: number, // default 3
retryBaseDelayMs?: number, // exponential backoff base delay, default 500
rateLimit?: { limit: number; windowMs: number }, // local token bucket, default 100 req / 1000ms
fetch?: (url, init) => Promise<Response>, // custom fetch implementation
telemetryPrefix?: string, // metric/event name prefix, default "paysafe"
});Pass a TelemetryHook as the second constructor argument to observe every request:
const client = new PaysafeClient(options, {
onStart: (meta) => metrics.increment(`${meta.prefix}.${meta.api}.start`),
onStop: (meta) => metrics.timing(`${meta.prefix}.${meta.api}.duration`, meta.durationMs),
});- Zero runtime dependencies. Built entirely on platform
fetchandcryptoAPIs. - Dual ESM/CJS build with full TypeScript declarations, via
tsup. - Retry with exponential backoff + jitter on transient failures (5xx, 429, timeouts, and a documented set of Paysafe API error codes).
- Token-bucket rate limiting per credential, enforced client-side before any network call.
- Structured errors. Every failure mode maps to a typed
PaysafeError.kind. - Strict TypeScript:
strict,noUncheckedIndexedAccess, and friends are all on. - Every resource method accepts an optional
AbortSignalfor cancellation, composed with the client's own timeout.
npm install
npm run build # tsup -> dist/ (ESM + CJS + .d.ts)
npm run test # vitest
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run format # prettier --writeMIT