@shieldz/sdk

Official Node / TypeScript SDK for Shieldz — non-custodial crypto payments with $0 fees.
Accept USDC/USDT across Base, Arbitrum, Optimism, Polygon, and Ethereum, plus native Bitcoin and shielded Zcash. Funds settle straight to your own wallet — Shieldz never holds them, and never asks for your keys.
- 🪪 Non-custodial — payments go wallet → wallet; you keep the keys.
- 💸 $0 platform fees — you only pay network gas.
- 🧩 Stripe-shaped API — create an invoice, send the hosted checkout, get a signed webhook.
- 🌐 Runs anywhere — zero dependencies, pure web standards (
fetch + Web Crypto). Works on Node 18+, Deno, Bun, Cloudflare Workers, and Vercel/Netlify Edge. Ships dual ESM + CommonJS (import and require).
- 🔁 Resilient — automatic retries with backoff on transient failures, idempotent invoice creation, and async-iterator auto-pagination.
Install
npm install @shieldz/sdk
Requires Node 18+ (or any runtime with fetch + Web Crypto). Get an API key (sk_live_… / sk_test_…) from your merchant dashboard → Developers.
Quickstart
import Shieldz from "@shieldz/sdk";
const shieldz = new Shieldz(process.env.SHIELDZ_API_KEY!);
const invoice = await shieldz.invoices.create({
amount_usd_cents: 5000,
memo: "Order #1234",
metadata: { order_id: "1234" },
});
console.log(invoice.id, invoice.status, invoice.pay_url);
CommonJS works too:
const { Shieldz } = require("@shieldz/sdk");
const shieldz = new Shieldz(process.env.SHIELDZ_API_KEY);
Retrieve & list
const inv = await shieldz.invoices.retrieve("Qgvz8WQw0mnv2M8");
const page = await shieldz.invoices.list({ limit: 20, status: "paid" });
console.log(page.data, page.has_more);
for await (const invoice of shieldz.invoices.listAll({ status: "paid" })) {
console.log(invoice.id);
}
Retries & idempotency
Transient failures (network errors, timeouts, 429, and 5xx) are retried automatically with exponential backoff + jitter — 2 retries by default, honouring Retry-After. To make a retried POST /invoices safe, the SDK auto-attaches an idempotency_key, so a create can never duplicate. Pass your own idempotency_key to tie it to your order id:
await shieldz.invoices.create({ amount_usd_cents: 5000, idempotency_key: "order_1234" });
Replaying the same key returns the original invoice (idempotent_replay: true) instead of creating a second one. Tune or disable retries via maxRetries (see Configuration).
Webhooks
Register an HTTPS endpoint in the dashboard and save the whsec_… signing secret. Shieldz sends invoice.paid and invoice.failed events, signed with HMAC-SHA256.
Verification uses the Web Crypto API, so it's async and runs on any runtime (Node, Deno, Bun, Cloudflare Workers, Edge). Always verify against the raw request body (not a re-serialized object):
import express from "express";
import { constructEvent } from "@shieldz/sdk";
const app = express();
app.post("/webhooks/shieldz", express.raw({ type: "application/json" }), async (req, res) => {
try {
const event = await constructEvent(
req.body,
req.header("X-Shieldz-Signature") ?? "",
process.env.SHIELDZ_WEBHOOK_SECRET!,
);
if (event.type === "invoice.paid") {
}
res.sendStatus(200);
} catch {
res.sendStatus(400);
}
});
On Cloudflare Workers / Edge it's the same call — pass the raw body and header:
export default {
async fetch(req: Request, env): Promise<Response> {
const raw = await req.text();
try {
const event = await constructEvent(raw, req.headers.get("X-Shieldz-Signature") ?? "", env.SHIELDZ_WEBHOOK_SECRET);
return new Response("ok");
} catch {
return new Response("bad signature", { status: 400 });
}
},
};
await verifySignature(rawBody, header, secret) is also exported if you just want a boolean check. During the 24h after a secret rotation, the header carries both signatures (v1=…,v1=…) and either matches.
Errors
Any non-2xx response throws ShieldzError:
import { ShieldzError } from "@shieldz/sdk";
try {
await shieldz.invoices.create({ amount_usd_cents: 1 });
} catch (err) {
if (err instanceof ShieldzError) {
console.log(err.status, err.type, err.code, err.param, err.requestId);
}
}
err.requestId is a correlation id for the failed request — quote it to support to trace it in the logs.
Configuration
const shieldz = new Shieldz({
apiKey: "sk_live_…",
baseUrl: "https://shieldz.cash/api/v1",
timeoutMs: 30_000,
maxRetries: 2,
maxRetryDelayMs: 8_000,
fetch: globalThis.fetch,
});
Links
License
MIT © Deniz Yanbollu / Shieldz