@tenderpay/sdk
v0.2.0
Published
Typed SDK for plugins that call Tender.
Maintainers
Readme
@tenderpay/sdk
Typed npm client for EmDash plugin authors that call Tender. Provides request validation, idempotency key generation, and a consumer event-consumption API.
Installation
pnpm install @tenderpay/sdkKey Features
- Charge / Refund / Subscription APIs: write surfaces with automatic idempotency keys and bounded retry on transient failures.
- Context-aware client (
createTenderClientFromContext): build an authenticated client straight from an EmDash route context + your settings — no hand-derived token,fetch, or base URL, no route-context casts. - Consumer eventing (
watchTransaction/fulfillTransaction): learn terminal payment states (paid,refunded, …) by short-polling Tender's authenticated read route, with exponential backoff and at-least-once delivery + dedup. This is the Candidate C transport from ADR 0005; the full consumer contract lives indocs/consumer-eventing.mdand the copy-paste quickstart indocs/consumer-integration.md. - Durable dedup (
createKvDedupStore): KV-backed, restart-safe dedup for the at-least-once contract (the in-memory store is a single-process convenience). - Type Safety: fully typed request/response and event interfaces.
Usage
import { createTenderClient } from "@tenderpay/sdk";
const client = createTenderClient({
baseUrl: "https://your-site.example",
pluginToken: () => loadAdminScopedToken(), // string or async getter
});
// Create a charge (idempotency key auto-generated).
const result = await client.charge({
amount: 9_999, // minor units (cents)
currency: "USD",
description: "Order #123",
customerEmail: "[email protected]",
returnUrl: "https://your-site.example/return",
});From an EmDash route context (recommended for plugins)
import { createTenderClientFromContext } from "@tenderpay/sdk";
// `ctx` is your sandboxed route context (exposes `http.fetch`); `settings` carries
// `tenderBaseUrl` + an admin-scoped `tenderPluginToken`.
const client = createTenderClientFromContext(ctx, settings);
const result = await client.charge({
/* …as above… */
});Consuming payment events (Carte/Dateline)
import { createKvDedupStore, fulfillTransaction } from "@tenderpay/sdk";
await fulfillTransaction(result.transactionId, {
client,
delivered: createKvDedupStore(ctx.kv), // DURABLE, restart-safe dedup
interestingStatuses: ["paid"], // omit to stop on ANY terminal status
// RETURN the promise so the watcher awaits it before recording the dedup key.
onEvent: (event) => fulfillOrder(event.transaction), // must be idempotent
});fulfillTransaction is the canonical charge → poll → fulfill closer; drive it from an
in-request entry point (return-URL handler, admin action, or cron-route). See the
consumer integration quickstart.
Delivery is at-least-once; consumers MUST dedup on
transactionEventDedupKey(event) and react idempotently. See
docs/consumer-eventing.md for the full
contract (event types, payload schema, versioning).
License
MIT
