This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: stripe-webhook-idempotency

Stripe Payment Webhook & Cryptographic Signature Verification Standard

Bank-grade payment webhook ingestion architecture. Mandates raw-body cryptographic signature verification via stripe.webhooks.constructEvent, atomic database idempotency keys to prevent double-crediting, and immediate 200 responses with async task dispatching.

Prohibited Anti-Pattern: AI parses JSON body before signature verification causing hash mismatch errors, and processes payments without idempotency locks.

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Stripe Payment Webhook & Cryptographic Signature Verification Standard - Bank-grade payment webhook ingestion architecture. Mandates raw-body cryptographic signature verification via stripe.webhooks.constructEvent, atomic database idempotency keys to prevent double-crediting, and immediate 200 responses with async task dispatching.
globs: **/*stripe*.ts, **/*webhook*.ts, **/*payment*.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/stripe-webhook-idempotency"
---

# Stripe Payment Webhook & Cryptographic Signature Verification Standard

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: AI parses JSON body before signature verification causing hash mismatch errors, and processes payments without idempotency locks.

## System Constraints
You are a financial payment systems engineer. When handling Stripe webhooks, you MUST verify the cryptographic signature using the RAW unparsed request payload buffer. Never use JSON.parse() before verification. Always enforce strict idempotency by recording the Stripe event.id in an atomic database lock before fulfilling value to prevent double crediting.

## Strict Guidelines
- Pass the raw Buffer request body into stripe.webhooks.constructEvent; do NOT use express.json() or JSON.parse() beforehand.
- Store event.id in an idempotency table or Redis atomic lock before fulfilling credits or shipping orders.
- Return HTTP 200 immediately to Stripe upon signature validation; offload heavy business tasks to an asynchronous queue or worker.
- Never trust unverified webhook endpoints in production; always rotate and secure STRIPE_WEBHOOK_SECRET.

## Bad vs Good Implementations
### Raw Body Verification & Idempotency Locking vs Insecure Webhook Handler

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: express.json() modifies raw bytes, causing Stripe HMAC verification to fail. Lacking an event ID check causes duplicate credit grants during network retries.
```typescript
// ANTI-PATTERN: Body parsed as JSON before verification, and zero idempotency check
import express from "express";

const app = express();
app.use(express.json()); // Mutates raw payload, breaking HMAC!

app.post("/webhook/stripe", async (req, res) => {
  const event = req.body; 
  if (event.type === "checkout.session.completed") {
    await grantCreditsToUser(event.data.object.customer, 500); // Double charges on retry!
  }
  res.json({ received: true });
});
```

#### Production Standard (Correct)
> **Standard**: Raw payload buffer guarantees valid cryptographic signature verification, and the database transaction prevents duplicate credit fulfillment.
```typescript
// PRODUCTION STANDARD: Raw buffer signature verification + atomic idempotency
import express from "express";
import Stripe from "stripe";
import { db } from "../lib/db";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const app = express();

app.post(
  "/api/webhooks/stripe",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const sig = req.headers["stripe-signature"] as string;
    let event: Stripe.Event;

    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        sig,
        process.env.STRIPE_WEBHOOK_SECRET!
      );
    } catch (err: any) {
      console.error("Stripe signature verification failed:", err.message);
      return res.status(400).send(`Webhook Error: ${err.message}`);
    }

    const alreadyProcessed = await db.processedEvent.findUnique({
      where: { eventId: event.id },
    });
    if (alreadyProcessed) {
      return res.status(200).json({ received: true, duplicate: true });
    }

    await db.$transaction(async (tx) => {
      await tx.processedEvent.create({ data: { eventId: event.id, eventType: event.type } });

      if (event.type === "checkout.session.completed") {
        const session = event.data.object as Stripe.Checkout.Session;
        await tx.user.update({
          where: { stripeCustomerId: session.customer as string },
          data: { subscriptionStatus: "active", plan: "pro" },
        });
      }
    });

    return res.status(200).json({ received: true });
  }
);
```

### Next.js 15 App Router Route Handler for Stripe Webhooks

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Calling req.json() prevents access to the raw byte stream needed for constructEvent.
```typescript
// ANTI-PATTERN: Calling req.json() inside Next.js Route Handler
export async function POST(req: Request) {
  const json = await req.json(); // Breaks raw signature!
}
```

#### Production Standard (Correct)
> **Standard**: req.text() in Next.js App Router keeps the exact raw payload intact for Stripe signature verification.
```typescript
// PRODUCTION STANDARD: req.text() in Next.js 15 Route Handler
import { headers } from "next/headers";

export async function POST(req: Request) {
  const body = await req.text(); // Preserves raw text string for HMAC
  const headerList = await headers();
  const sig = headerList.get("stripe-signature")!;
  const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
  return new Response(JSON.stringify({ received: true }), { status: 200 });
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Stripe Payment Webhook & Cryptographic Signature Verification Standard

Stripe Payment Webhook & Cryptographic Signature Verification Standard defines an industrial-grade behavioral standard for backend engineering. Bank-grade payment webhook ingestion architecture. Mandates raw-body cryptographic signature verification via stripe.webhooks.constructEvent, atomic database idempotency keys to prevent double-crediting, and immediate 200 responses with async task dispatching. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: AI parses JSON body before signature verification causing hash mismatch errors, and processes payments without idempotency locks. Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftAI parses JSON body before signature verification causing hash mismatch errors, and processes payments without idempotency locks.Critical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Pass the raw Buffer request body into stripe.webhooks.constructEvent

Pass the raw Buffer request body into stripe.webhooks.constructEvent; do NOT use express.json() or JSON.parse() beforehand.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Store event.id in an idempotency table or Redis atomic lock before fulfilling credits or shipping orders.

Store event.id in an idempotency table or Redis atomic lock before fulfilling credits or shipping orders.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Return HTTP 200 immediately to Stripe upon signature validation

Return HTTP 200 immediately to Stripe upon signature validation; offload heavy business tasks to an asynchronous queue or worker.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Never trust unverified webhook endpoints in production

Never trust unverified webhook endpoints in production; always rotate and secure STRIPE_WEBHOOK_SECRET.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Stripe Payment Webhook & Cryptographic Signature Verification Standard critical for AI coding assistants?

Bank-grade payment webhook ingestion architecture. Mandates raw-body cryptographic signature verification via stripe.webhooks.constructEvent, atomic database idempotency keys to prevent double-crediting, and immediate 200 responses with async task dispatching. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (AI parses JSON body before signature verification causing hash mismatch errors, and processes payments without idempotency locks.) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

Stripe