checkout-client
v1.0.0
Published
Unofficial Production-grade TypeScript client for the Checkout.com API — all 168 endpoints, OAuth 2.0, retry, idempotency, webhook verification, zero runtime dependencies.
Maintainers
Readme
checkout-client
Production-grade TypeScript client for the Checkout.com API.
All 168 endpoints · OAuth 2.0 · Full-jitter retry · Idempotency · Webhook verification · Zero runtime dependencies
Features
| | |
| ----------------------------- | --------------------------------------------------------------------------------- |
| 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 |
Requirements
Node.js ≥ 18 (uses native fetch and crypto.randomUUID).
Installation
npm install checkout-clientQuick start
import { 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 });Authentication
OAuth 2.0 (recommended)
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.
Static secret key
new CheckoutClient({ prefix: "vkuhvk4v", secretKey: "sk_..." });Configuration
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),
},
};Error handling
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
}
}Idempotency
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-..."Retry
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.
AbortSignal
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 },
);AWS PrivateLink
new CheckoutClient({ prefix: "vkuhvk4v", privateLink: true });
// → https://pl-vkuhvk4v.api.checkout.comWebhook verification
import {
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";Custom fetch (testing, proxies, OpenTelemetry)
// 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);
},
});Deep imports
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";Complete API
| 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 |
Testing
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.
Building
npm run build # ESM + CJS + type declarations
npm run clean # remove dist/License
MIT — see LICENSE.
