Observed since 20 Oct, 22:00 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
@021.is/spine-webhooks
Inbound webhook verifier: HMAC signature + replay/idempotency dedupe. Generalized from DC's Resend/Stripe handlers.
Inbound webhook HMAC verifier (4 formats: raw, GitHub sha256=…, Stripe t=…,v1=… with tolerance window, Resend svix-style v1,<b64>) + idempotency-key dedupe store. Timing-safe comparisons throughout.
import { makeHmacVerifier, makeMemoryIdempotencyStore } from "@021.is/spine-webhooks";
const stripe = makeHmacVerifier({
secret: env.STRIPE_WEBHOOK_SECRET,
toleranceSec: 5 * 60, // 5min — Stripe's recommended max clock skew
});
export const POST = withErrorHandling(async (req: Request) => {
const rawBody = await req.text();
const signature = req.headers.get("stripe-signature");
if (!signature) throw new BadRequestException("missing signature");
stripe.verify({ payload: rawBody, signature, headerScheme: "stripe" });
const event = JSON.parse(rawBody);
const seen = await store.seen(event.id);
if (seen) return Response.json(ok({ duplicate: true }));
await handleStripeEvent(event);
return Response.json(ok({ processed: true }));
});
| What | |
|---|---|
raw | Plain HMAC-SHA256 hex of body |
github | sha256=<hex> prefix |
stripe | t=<unix>,v1=<sig> — body is ${ts}.${rawBody}; tolerance window enforces freshness |
resend | svix-style v1,<base64> |
Webhooks retry on network failure. Without dedup you charge twice / send twice / publish twice. store.seen(key, ttlSec) returns true if the key has been seen within TTL — first call returns false + records; subsequent calls return true.
Memory store for tests + tiny apps. Wire to your Prisma idempotency table for production.
All signature checks use crypto.timingSafeEqual — string compare is timing-attackable.
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
@021.is/spine-errorsEnvironment{
"local_name": "@021.is/spine-errors",
"alias": false,
"peer_metadata": null
} | ^0.4.0 | runtime |
Source distribution
{
"shasum": "e1ece28db125af4a46fcbaa4b6faf87e035989d0",
"tarball": "https://registry.npmjs.org/@021.is/spine-webhooks/-/spine-webhooks-0.4.0.tgz",
"fileCount": 4,
"integrity": "sha512-RfKUSk96fmBnTRvh3uGe8cJbfBeh5orU8EEHJRYlxKTCYphAp/VYjJqyI1ZI6Q4nwqHQxaq76M82Q6EqDV1Hxg==",
"signatures": [
{
"sig": "MEQCIFGf5mrbi6WK2hJq1C1oUu4Qs6jFIuHbva7mQA5XFc9NAiAxFCjK+SN3W3KeGaPaVb+z6Q4wpUreG9gmMfgoRsajWQ==",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
}
],
"unpackedSize": 7640
}Observed since 20 Oct, 22:00 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:4e5b34274d4a3dbf53c88909ffd1dcdb528e95eb8488793c51fb325ba74b3a94