plugins/stripe-billing-master/skills/stripe-webhook-idempotency/SKILL.md
Server-side Stripe webhook idempotency patterns. PROACTIVELY activate for: (1) Stripe webhook handler design, (2) Transactional dedup via stripe_processed_events, (3) credit_transactions.idempotency_key UNIQUE partial indexes, (4) Idempotency-Key header priority (header > body > server UUID), (5) Idempotency key format/charset/length validation at the handler edge, (6) FOR UPDATE row locking when a UPDATE depends on a prior SELECT, (7) Webhook signature verification (stripe.webhooks.constructEventAsync, tolerance, raw-body reading), (8) Retry-safe endpoints with randomUUID fallback, (9) Durable checkpoint row ordering (checkpoint FIRST, mutation SECOND). Provides: complete webhook handler skeleton, Idempotency-Key validator, dedup SQL, FOR UPDATE pattern, signature verification example.
npx skillsauth add JosiahSiegel/claude-plugin-marketplace stripe-webhook-idempotencyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Concept | Source of truth | Default |
|--|--|--|
| Signature verification | stripe.webhooks.constructEventAsync(rawBody, sig, secret, tolerance) | tolerance = 300s |
| Event dedup (short-term) | stripe_processed_events (30d retention) | primary key: event_id |
| Balance-change dedup (durable) | credit_transactions.idempotency_key UNIQUE partial index | retention = forever |
| Idempotency key priority | Idempotency-Key header > body.idempotency_key > crypto.randomUUID() | validate len<=128, [A-Za-z0-9_-] |
| Checkpoint ordering | Checkpoint INSERT -> user UPDATE | never reverse |
| Row lock | .for("update") on the snapshot row | required when UPDATE depends on prior SELECT |
Use for every Stripe webhook handler and every client-retry-safe mutation endpoint (any POST route that creates a billable entity, e.g., /api/v1/orders, /api/v1/jobs, or your project's equivalent).
Related skills:
stripe-billing-master:stripe-refund-dispute-lifecyclestripe-billing-master:stripe-credit-audit-trailstripe-billing-master:stripe-list-pagination-previous-attributesawait db.transaction(async (tx) => {
// 1. Durable checkpoint FIRST
const [inserted] = await tx.insert(creditTransactions).values({ /* ... idempotencyKey: "dispute_hold:<id>" */ })
.onConflictDoNothing({ target: creditTransactions.idempotencyKey })
.returning({ id: creditTransactions.id });
// 2. Dedup
if (!inserted) return;
// 3. Mutate
await tx.update(users).set({ /* ... */ }).where(/* ... */);
});
const MAX_LEN = 128;
const VALID = /^[A-Za-z0-9_-]+$/;
export function validateIdempotencyKey(key: string | null): { valid: true; key: string | null } | { valid: false; error: string } {
if (!key || key.trim() === "") return { valid: true, key: null }; // caller falls back to randomUUID
if (key.length > MAX_LEN) return { valid: false, error: "Idempotency-Key too long" };
if (!VALID.test(key)) return { valid: false, error: "Idempotency-Key invalid charset" };
return { valid: true, key };
}
// In the handler:
const headerKey = req.headers.get("Idempotency-Key");
const bodyKey = body.idempotency_key;
const raw = headerKey ?? bodyKey ?? null;
const v = validateIdempotencyKey(raw);
if (!v.valid) return apiError(400, "VALIDATION_ERROR", v.error);
const key = v.key ?? crypto.randomUUID();
Use constructEventAsync — synchronous constructEvent blocks the worker on large payloads.
const event = await stripe.webhooks.constructEventAsync(
rawBody, // raw bytes; Next.js: await req.text() BEFORE any json()
sigHeader,
env.STRIPE_WEBHOOK_SECRET,
300, // 5min tolerance
);
export async function POST(req: Request) {
const rawBody = await req.text();
const signature = req.headers.get("stripe-signature");
if (!signature) return apiError(400, "VALIDATION_ERROR", "Missing stripe-signature");
let event: Stripe.Event;
try {
event = await stripe.webhooks.constructEventAsync(
rawBody,
signature,
env.STRIPE_WEBHOOK_SECRET,
300, // 5min tolerance
);
} catch (err) {
logEvent("stripe_webhook_bad_signature", { err: String(err) });
return apiError(400, "INVALID_SIGNATURE", "Signature verification failed");
}
// Transactional dedup: G1 + G9 pattern
const dedup = await db.insert(stripeProcessedEvents)
.values({ eventId: event.id, type: event.type })
.onConflictDoNothing({ target: stripeProcessedEvents.eventId })
.returning({ id: stripeProcessedEvents.id });
if (dedup.length === 0) return apiSuccess({ deduped: true });
await dispatchEvent(event); // handlers apply G1-G9 per rules above
return apiSuccess({ ok: true });
}
const [user] = await tx.select(/* ... */).from(users).where(eq(users.id, userId)).for("update");
// snapshot is safe from concurrent invoice/dispute writes
const prev = user.stripeSubscriptionStatus;
await tx.insert(creditTransactions).values({ /* ... */ metadata: { previousStatus: prev } });
await tx.update(users).set({ stripeSubscriptionStatus: "past_due" });
development
This skill should be used when the user asks to train, debug, scale, or improve ML models. PROACTIVELY activate for: (1) PyTorch, TensorFlow/Keras, JAX, Flax, Hugging Face Trainer/Accelerate training loops, (2) distributed training, DDP/FSDP/DeepSpeed, TPU/GPU setup, (3) mixed precision AMP/bf16, gradient accumulation, checkpointing, seeding, (4) overfitting, imbalance, loss functions, regularization, LR schedules, warmup, (5) memory optimization, gradient checkpointing, offloading, quantization-aware training. Provides: reproducible training best practices across deep learning and classical ML.
development
This skill should be used when the user asks to productionize, track, version, govern, monitor, or automate ML systems. PROACTIVELY activate for: (1) MLflow, Weights & Biases, Neptune, Comet, ClearML experiment tracking, (2) model registry, model versioning, artifact lineage, reproducibility, (3) Kubeflow, SageMaker Pipelines, Vertex AI Pipelines, Azure ML pipelines, Databricks workflows, (4) CI/CD, continuous training/evaluation, A/B tests, canary/shadow deployments, (5) drift detection, model monitoring, data validation, responsible AI governance. Provides: end-to-end MLOps architecture and operational safeguards.
development
This skill should be used when the user asks to optimize, export, serve, compress, or accelerate ML inference. PROACTIVELY activate for: (1) latency, throughput, p95/p99, batching, concurrency, KV cache, memory, or cost issues, (2) quantization INT8/INT4, GPTQ, AWQ, bitsandbytes, pruning, sparsity, distillation, (3) ONNX export, ONNX Runtime, TensorRT, TorchScript, torch.compile, XLA, OpenVINO, Core ML, TFLite, (4) Triton, TorchServe, TF Serving, BentoML, Seldon, KServe configuration, (5) edge deployment, CPU/GPU/TPU/Inferentia serving. Provides: hardware-aware inference optimization and safe benchmarking.
testing
This skill should be used when the user asks to tune hyperparameters, run sweeps, optimize search spaces, or use AutoML. PROACTIVELY activate for: (1) Optuna, Ray Tune, FLAML, AutoGluon, Hyperopt, Nevergrad, KerasTuner, W&B sweeps, (2) grid search, random search, Bayesian optimization, TPE, Gaussian processes, evolutionary search, (3) ASHA, Hyperband, successive halving, multi-fidelity optimization, population-based training, (4) learning-rate finder, batch-size search, early stopping, pruning, (5) reproducible sweep design and experiment analysis. Provides: budget-aware hyperparameter search strategy.