stripe-webhook-idempotency
Stripe-specific webhook idempotency -- signature verification with the constructEvent API, retry-id reuse via `event.id`, double-fulfilment guards on…
Skill by MILTONADINA
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/miltonadina-stripe-webhook-idempotency
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: backend, security, finance
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/stripe-webhook-idempotency
What's inside
- Skill name
stripe-webhook-idempotency- Description
- Stripe-specific webhook idempotency -- signature verification with the constructEvent API, retry-id reuse via `event.id`, double-fulfilment guards on PaymentIntent / Charge / Invoice events. Extends the universal `security/webhook-idempotency` skill with Stripe's event-id discipline and signing-secret rotation patterns. Triggers on Stripe webhook handler authoring (`stripe.webhooks.constructEvent` references, Stripe-Signature header handling). OWASP ASI02 (Tool Misuse) defense.
- License
- MIT
- Instructions
- 246 lines in SKILL.md · view on GitHub
Agent Skills spec issues
- name: "stripe-webhook-idempotency" does not match folder "webhook-idempotency"
Files (1)
- SKILL.md
SKILL.md (first lines)
# Stripe Webhook Idempotency
> Stripe-specific extension of the universal
> [`security/webhook-idempotency`](../../../universal/security/webhook-idempotency/SKILL.md)
> skill. Read that first for the provider-agnostic patterns
> (idempotency-key, replay-window, storage strategies). This skill layers
> Stripe's specifics on top: `constructEvent` for signature verification,
> `event.id` discipline, signing-secret rotation, and the per-event-type
> fulfilment guards that close ASI02 (Tool Misuse) for Stripe handlers.
**Tradeoff:** Stripe is the highest-volume webhook surface for most
applications and Stripe events drive money-moving side effects -- the
strictest place to get idempotency right. The cost: extra discipline on
signature verification, event-id handling, and event-type-specific
fulfilment checks beyond what the universal pattern provides.
---
## Why this matters
Stripe retries webhook delivery for up to 3 days on transient failures.
Every retry that lands after the receiver successfully processed the
event (but before the response made it back) is a duplicate. Production
incidents from missing this:
- Two charges on the same `payment_intent.succeeded`.
- Two emails on the same `invoice.payment_succeeded`.
- Two database updates on the same `customer.subscription.updated`.
- Stale fulfilment if a later retry of an earlier event arrives after a
newer event has already been processed.
The universal patterns close the dedupe-key gap. Stripe-specific patterns
close the verification + fulfilment-guard gaps.
---
## Pattern 1 -- Verify the signature with `constructEvent`, not raw HMAC
Stripe signs every webhook delivery with the signing secret you set when
creating the webhook endpoint in the Stripe dashboard (or via the API).
Use `stripe.webhooks.constructEvent` -- it verifies the signature AND
parses the event into a typed `Stripe.Event`. Never roll your own HMAC
compare against the raw body; the library handles signature-version
quirks, replay-window enforcement, and constant-time comparison.
```ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const SIGNING_SECRET = process.env.STRIPE_WEBHOOK_SECRET!;
async function handleStripeWebhook(rawBody: string, signature: string) {
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(rawBody, signature, SIGNING_SECRET);
} catch (err) {
// Invalid signature, malformed payload, OR replay-window exceeded
// (Stripe SDK's default tolerance is 300 seconds).
return new Response('invalid signature', { status: 400 });
}
// event is now a verified Stripe.Event with typed event.data.object.
}
```
Crucially: pass the RAW request body (the unparsed bytes), not a re-
serialized JSON. Next.js / Express / FastAPI / Fastify all have a
"raw body" middleware mode you must enable on the webhook route. A
re-serialized body has different bytes than what Stripe signed; verification
fails. This is the #1 first-time Stripe webhook failure mode.
---
## Pattern 2 -- Use `event.id` as the idempotency key, never derive from body
Stripe assigns every event a unique `event.id` (format `evt_...`). This is
the canonical retry identifier; Stripe guarantees the same id across
delivery attempts of the same event. Use it directly as your dedupe key
in whichever storage strategy fits your throughput profile (see the
universal skill for the Redis-TTL / DB-unique-constraint / in-memory
tradeoff).
```ts
const dedupeKey = `stripe:event:${event.id}`;
const claimed = await redis.set(dedupeKey, 'processing', { NX: true, EX: 86400 * 4 });
if (claimed !== 'OK') {
return new Response('duplicate (idempotent)', { status: 200 });
}
```
The 4-day TTL covers Stripe's full 3-day retry schedule plus headroom.
Anything shorter risks a very-late retry slipping past your dedupe window.
---
## Pattern 3 -- Per-event-type fulfilment guards
`stripe.webhooks.constructEvent` returns a discriminated-union typed event.
Switch on `event.type` and apply fulfilment-state checks specific to that
type, NOT just the idempotency-key check. The idempotency key prevents
double-processing of the SAME event; fulfilment guards prevent processing
events in the wrong order.
```ts
switch (event.type) {
case 'payment_intent.succeeded': {
const pi = event.data.object; // typed Stripe.PaymentIntent
// Guard: do not fulfill if order is already shipped.
const order = await db.order.findUnique({ where: { piId: pi.id } });
if (order?.status === 'fulfilled') {
return new Response('already fulfilled', { status: 200 });
}
await fulfillOrder({ orderId: order!.id, paymentIntentId: pi.id });
break;
}
case 'charge.refunded': {
const charge = event.data.object; // typed Stripe.Charge
await markOrderRefunded({ chargeId: charge.id });
break;
}From MILTONADINA/DevOPs, MIT.
More from MILTONADINA/DevOPs
- 0.565score
openapi-first
MILTONADINA
Generate API code, types, validators, mock servers, and client SDKs from a single OpenAPI 3.1 specification.
SkillAFreshness: AClaude Code +9 - 0.565score
proof-of-work
MILTONADINA
Produce structured proof artifacts for every completed claim.
SkillAFreshness: AClaude Code +9 - 0.565score
plan-decomposition
MILTONADINA
Break an approved spec into atomic, ordered, verifiable tasks before any code is written.
SkillAFreshness: AClaude Code +9 - 0.565score
multi-tool-failover
MILTONADINA
Manage session continuity when switching between Claude Code, Codex CLI, Cursor, Antigravity, Kiro, or local LLMs (OpenCode, Aider).
SkillAFreshness: AClaude Code +9 - 0.565score
goal-loop
MILTONADINA
Execute the spec-tests-implement-verify cycle for one atomic task at a time.
SkillAFreshness: AClaude Code +9 - 0.565score
baton-handoff
MILTONADINA
Write a complete handoff document at session end so the next agent (Claude Code, Codex, Cursor, Antigravity, Kiro, or local LLM) resumes exactly where this…
SkillAFreshness: AClaude Code +9 - 0.565score
observability-instrument
MILTONADINA
Add OpenTelemetry spans with user_id and tenant_id in baggage to any new code path that handles requests, runs agents, or performs billable operations.
SkillAFreshness: AClaude Code +9 - 0.565score
cost-attribution
MILTONADINA
Track per-client and per-feature LLM and cloud spend with cryptographic audit trail.
SkillAFreshness: AClaude Code +9 - 0.565score
ears-spec-writing
MILTONADINA
Write requirement specifications in EARS (Easy Approach to Requirements Syntax) notation.
SkillAFreshness: AClaude Code +9 - 0.561score
session-summary
MILTONADINA
Generate the end-of-session human-reviewable report.
SkillAFreshness: AClaude Code +9 - 0.561score
coppa-audit
MILTONADINA
Audit code, data flows, and UI for COPPA (Children's Online Privacy Protection Act) compliance.
SkillAFreshness: AClaude Code +9 - 0.560score
supabase-rls-policies
MILTONADINA
Supabase Row-Level Security policy patterns and failure modes -- policy bypass via service-role key in client code, USING-vs-WITH-CHECK confusion, missing…
SkillAFreshness: AClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 12
- Last commit (this folder)
- May 23, 2026 (5 months ago)
- Last release
- Sep 23, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/MILTONADINA/DevOPs
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from MILTONADINA
- 0.565score
baton-handoff
MILTONADINA
Write a complete handoff document at session end so the next agent (Claude Code, Codex, Cursor, Antigravity, Kiro, or local LLM) resumes exactly where this…
SkillAFreshness: AClaude Code +9 - 0.565score
observability-instrument
MILTONADINA
Add OpenTelemetry spans with user_id and tenant_id in baggage to any new code path that handles requests, runs agents, or performs billable operations.
SkillAFreshness: AClaude Code +9 - 0.565score
openapi-first
MILTONADINA
Generate API code, types, validators, mock servers, and client SDKs from a single OpenAPI 3.1 specification.
SkillAFreshness: AClaude Code +9 - 0.565score
ears-spec-writing
MILTONADINA
Write requirement specifications in EARS (Easy Approach to Requirements Syntax) notation.
SkillAFreshness: AClaude Code +9 - 0.565score
cost-attribution
MILTONADINA
Track per-client and per-feature LLM and cloud spend with cryptographic audit trail.
SkillAFreshness: AClaude Code +9 - 0.565score
goal-loop
MILTONADINA
Execute the spec-tests-implement-verify cycle for one atomic task at a time.
SkillAFreshness: AClaude Code +9
Cite this page
stripe-webhook-idempotency skill. Agent Mart. https://agentmart.dev/skills/miltonadina-stripe-webhook-idempotency (last updated 2026-10-24)