Production-grade TypeScript client for the Checkout.com API.
All 168 endpoints · OAuth 2.0 · Full-jitter retry · Idempotency · Webhook verification · Zero runtime dependencies
| 100% API coverage | All 168 endpoints, 43 typed service namespaces |
| Zero runtime dependencies | Only Node.js built-ins (node:crypto, node:http) |
| Dual-module | ESM + CommonJS, full TypeScript declarations |
| Deep imports | checkout-client/payments, /vault, /disputes, /issuing, /webhook, /errors |
| OAuth 2.0 | Singleflight token fetch, in-memory cache, proactive refresh 90s before expiry |
| Idempotency | UUID v4 auto-injected on all eligible endpoints; override per-request |
| Full-jitter retry | Exponential backoff on 429 + 5xx; configurable attempts and delays |
| AbortSignal | Pass signal to any request for cancellation and timeout control |
| Webhook verification | HMAC-SHA256 constant-time signature checking |
| AWS PrivateLink | privateLink: true → pl-{prefix}.api.checkout.com |
| Typed errors | CheckoutError with status predicates and full response metadata |
| Structured logging | Bring your own logger via the Logger interface |
| 68 tests | Vitest, mock fetch only — zero live API calls |
Node.js ≥ 18 (uses native fetch and crypto.randomUUID).
npm install checkout-clientimport { CheckoutClient } from "checkout-client";
const client = new CheckoutClient({
prefix: process.env.CHECKOUT_PREFIX!, // first 8 chars of client_id
environment: "sandbox",
accessKeyId: process.env.CHECKOUT_ACCESS_KEY_ID,
accessKeySecret: process.env.CHECKOUT_ACCESS_KEY_SECRET,
});
// Request a payment
const payment = await client.payments.request({
amount: 10_000, // GBP £100.00 in minor units
currency: "GBP",
source: { type: "token", token: "tok_..." },
reference: "ORD-001",
});
// Capture it
await client.payments.capture(payment.id);
// Partial refund (£25)
await client.payments.refund(payment.id, { amount: 2_500 });new CheckoutClient({
prefix: "vkuhvk4v",
accessKeyId: "ack_...",
accessKeySecret: "...",
});Tokens are fetched once, cached in-memory, and proactively refreshed 90 s before expiry. Concurrent requests coalesce onto a single in-flight token fetch.
new CheckoutClient({ prefix: "vkuhvk4v", secretKey: "sk_..." });import type { CheckoutConfig } from "checkout-client";
const config: CheckoutConfig = {
prefix: "vkuhvk4v", // required
environment: "production", // "production" | "sandbox"
accessKeyId: "ack_...", // OAuth 2.0
accessKeySecret: "...",
secretKey: "sk_...", // static key (alternative to OAuth)
publicKey: "pk_...", // tokenization only
privateLink: false, // AWS PrivateLink
idempotencyKeyPrefix: "myapp", // auto-key prefix: "myapp-{uuid}"
timeoutMs: 30_000, // per-request timeout
retry: {
maxAttempts: 4, // 1 initial + 3 retries
baseDelayMs: 500,
maxDelayMs: 30_000,
},
fetch: customFetch, // injectable for testing/proxies
logger: {
debug: (msg, meta) => console.log(msg, meta),
warn: (msg, meta) => console.warn(msg, meta),
},
};import { CheckoutError } from "checkout-client";
// or: import { CheckoutError } from "checkout-client/errors";
try {
await client.payments.request({ currency: "GBP" });
} catch (err) {
if (err instanceof CheckoutError) {
// HTTP status predicates
err.isNotFound(); // 404
err.isRateLimited(); // 429 — already retried maxAttempts times
err.isConflict(); // 409 — idempotency key collision
err.isUnauthorized(); // 401
err.isForbidden(); // 403
err.isValidationError(); // 422
err.isServerError(); // 5xx
// Rich metadata
err.statusCode; // number
err.requestId; // string — include in Checkout.com support tickets
err.errorType; // e.g. "request_invalid"
err.errorCodes; // e.g. ["amount_invalid", "currency_required"]
err.apiMessage; // human-readable summary
err.rawBody; // complete response body for debugging
}
}Cko-Idempotency-Key (UUID v4) is auto-injected on all eligible endpoints:
POST /payments (and all action sub-paths), POST /payment-contexts, POST /transfers.
// Custom key — safe to retry with the same value
await client.payments.request(req, { idempotencyKey: "order-42-attempt-1" });
// Prefix auto-generated keys
new CheckoutClient({ idempotencyKeyPrefix: "myapp" });
// → "myapp-550e8400-e29b-41d4-..."Full-jitter exponential backoff: delay = random(0, min(base × 2^attempt, max))
new CheckoutClient({
retry: {
maxAttempts: 4, // 1 initial + 3 retries
baseDelayMs: 500,
maxDelayMs: 30_000,
},
});Retried: 429, 5xx, network errors.
Not retried: 4xx except 429, 409 Conflict.
Every service method accepts { signal } as its last options argument:
const controller = new AbortController();
setTimeout(() => controller.abort(), 5_000);
const payment = await client.payments.request(
{ amount: 10_000, currency: "GBP" },
{ signal: controller.signal },
);new CheckoutClient({ prefix: "vkuhvk4v", privateLink: true });
// → https://pl-vkuhvk4v.api.checkout.comimport {
verifyWebhook,
InvalidWebhookSignatureError,
} from "checkout-client/webhook";
app.post("/webhooks/checkout", express.raw({ type: "*/*" }), (req, res) => {
try {
const event = verifyWebhook(
req.headers["cko-signature"],
req.body, // Buffer or string
process.env.CKO_WEBHOOK_KEY!,
);
console.log(event.type, event.id);
res.sendStatus(200);
} catch (err) {
if (err instanceof InvalidWebhookSignatureError) {
res.sendStatus(403);
} else {
res.sendStatus(500);
}
}
});Or import directly from the root:
import { verifyWebhook, InvalidWebhookSignatureError } from "checkout-client";// Inject a custom fetch for testing
import { CheckoutClient } from "checkout-client";
const client = new CheckoutClient({
prefix: "testpfx1",
secretKey: "sk_test",
fetch: async (url, init) => {
console.log("→", url);
return globalThis.fetch(url, init);
},
});Each sub-module is independently importable with full tree-shaking support:
import type { PaymentRequest, PaymentResponse } from "checkout-client/payments";
import type { FXRatesResponse } from "checkout-client/vault";
import type { Dispute } from "checkout-client/disputes";
import type { CardResponse } from "checkout-client/issuing";
import { verifyWebhook } from "checkout-client/webhook";
import { CheckoutError } from "checkout-client/errors";| Namespace | Methods |
|---|---|
client.payments |
request, list, get, getActions, incrementAuthorization, cancelRetry, capture, refund, reverse, void, search |
client.flow |
createSession, submitSession, createAndSubmit |
client.paymentLinks |
create, get |
client.hostedPayments |
create, get |
client.paymentContexts |
request, get |
client.paymentSetups |
create, update, get, confirm |
client.paymentMethods |
list |
client.tokens |
create |
client.instruments |
create, get, update, delete, getBankAccountFieldFormatting |
client.customers |
create, get, update, delete |
client.transfers |
create, get |
client.balances |
get |
client.forex |
getRates |
client.cardMetadata |
get |
client.networkTokens |
provision, get, requestCryptogram, delete |
client.accountUpdater |
getUpdatedCredentials |
client.applePay |
generateCSR, uploadCertificate, enrollDomain |
client.googlePay |
enrollEntity, registerDomain, getRegisteredDomains, getEnrollmentState |
client.forward |
sendRequest, getRequest, createSecret, listSecrets, updateSecret, deleteSecret |
client.sessions |
create, get, update, complete, update3DSMethodCompletion |
client.compliance |
get, respond |
client.agenticCommerce |
createDelegatedToken |
client.reports |
list, get, getFile |
client.financialActions |
list |
client.disputes |
list, get, accept, provideEvidence, getEvidence, submitEvidence, submitArbitrationEvidence, getSubmittedArbitrationEvidence, getSubmittedEvidence, getSchemeFiles, uploadFile, getFile |
client.workflows |
list, create, get, patch, delete, addAction, updateAction, removeAction, addCondition, updateCondition, removeCondition, test, getEventTypes, getEvent, getActionInvocations, getSubjectEvents, reflowByEvent, reflowByEventAndWorkflow, reflow, reflowBySubject, reflowBySubjectAndWorkflow |
client.cardholders |
create, get, update, listCards |
client.cardholderAccessTokens |
request |
client.cards |
create, get, update, getCredentials, activate, suspend, revoke, renew, scheduleRevocation, deleteScheduledRevocation, enrol3DS, get3DSEnrollment, update3DSEnrollment |
client.controls |
create, listByTarget, get, update, delete |
client.controlProfiles |
create, list, get, update, delete, addTarget, removeTarget |
client.controlGroups |
create, listByTarget, get, delete |
client.issuingTransactions |
list, get |
client.issuingDisputes |
create, get, cancel, escalate |
client.issuingSandbox |
simulateAuthorization, simulateIncrementalAuthorization, simulateClearing, simulateRefund, simulateReversal |
client.platformEntities |
onboard, get, update, uploadFile, getFile, listMembers, reinviteMember |
client.platformInstruments |
add, get, update, list |
client.payoutSchedules |
get, update |
client.reserveRules |
add, get, update, list |
client.identityApplicants |
create, get, update, anonymize |
client.identityVerification |
createAndStart, create, get, anonymize, createAttempt, listAttempts, getAttempt, getReport |
client.amlScreening |
create, get |
client.faceAuth |
create, get, anonymize, createAttempt, listAttempts, getAttempt |
client.idDocuments |
create, get, anonymize, createAttempt, listAttempts, getAttempt, getReport |
npm test # vitest run
npm run test:watch # vitest watch mode
npm run test:coverage # coverage reportAll 68 tests use mock fetch — no live API calls, no credentials needed.
npm run build # ESM + CJS + type declarations
npm run clean # remove dist/MIT — see LICENSE.