# Stripe Webhook Review

Stripe Webhook Review is a tested SKILL.md that reviews ONE Stripe webhook handler (the route code, in any language or framework) for the five mistakes that let a forged or repeated event move money, grant access or ship goods, and lists only those, one line each with a fixed code, the place, why it is dangerous and the fix - or exactly No findings; an agent buys it once for $0.01 over x402.

- Page: https://aiskills402.com/skills/stripe-webhook-review
- Category: Code & Engineering (https://aiskills402.com/categories/code)
- Price: $0.01 once, USD-priced, paid in USDC on Base over x402. Price as loaded on this page. The 402 response your agent receives is authoritative.
- Version: 1.0.0
- Card (JSON): https://api.aiskills402.com/v1/skills/stripe-webhook-review

## Use it when

Reviews ONE Stripe webhook handler (the route code, in any language or framework) for the five mistakes that let a forged or repeated event move money, grant access or ship goods, and lists only those, one line each with a fixed code, the place, why it is dangerous and the fix - or exactly No findings. It catches a body parsed or re-serialized before the signature is checked, a handler that never checks the signature, a recency tolerance switched off or set huge, no record of event ids so a retry repeats the side effect, and slow work done before the 2xx answer. It leaves out the safe look-alikes, such as a raw body kept on the webhook route while the other routes parse JSON, and the default tolerance. Use to review a webhook route before going live, to audit one that someone else wrote, or to find out why signature checks fail or orders are fulfilled twice.

## Not for

Payment amounts, refunds, API keys, IP allowlists, rolling the signing secret, thin events, Connect and any helper function whose body is missing from the paste. It reads one pasted handler and judges only what is written in it.

## Tested, honestly

Tested 2026-10-19.

- Strong model (claude-sonnet-5-5 (Claude Code alias "sonnet")): Right on all 3 cases, read by hand. On the Express app it named the body rebuilt with JSON.stringify after the app-wide JSON parser, and left out the default tolerance. On the Flask route it gave two lines, the tolerance set to 0 and the credit update with no event id, and left out the raw body next to a JSON route. On the clean Next.js route it answered exactly No findings.
- Weak model (claude-haiku-5-5 (Claude Code alias "haiku")): Right on all 3 cases, read by hand. It gave the same single line for the rebuilt body and the same two lines for the Flask route, and answered exactly No findings on the clean route. On the Flask route it indented its two lines by four spaces, which the format check allows.

Note: Light test: three cases (two planted, one clean), one run per model; no comparison without the skill. The three inputs were written by us; each planted one carries a safe look-alike and neither has a comment pointing at the defect. Facts come from the public pages named in the notes, read on 19 October 2026; there are no owner-measured facts.

Full summary: https://aiskills402.com/skills/stripe-webhook-review/tests

## Example

### English (claude-sonnet-5-5)

Before:

> # app.py import os import stripe from flask import Flask, request from db import db app = Flask(__name__) WEBHOOK_SECRET = os.environ["STRIPE_WEBHOOK_SECRET"] @app.post("/api/profile") def update_profile():     data = request.get_json()     db.execute("UPDATE users SET name = %s WHERE id = %s", (data["name"], data["id"]))     db.commit()     return {"ok": True} @app.post("/stripe/webhook")…

After:

> TOLERANCE-OFF — `stripe_webhook`, `tolerance=0` passed to `stripe.Webhook.construct_event` — a tolerance of 0 switches the recency check off, so anyone who captures one validly signed request can replay it later — remove the argument to use the default 5 minutes…

## What is in the file

- The answer
- What Stripe says (public webhook documentation, read on 19 October 2026)
- The codes
- How to read the handler
- Rules
- Work in this order
- Short example

## Languages

English

## How to buy

Agent (HTTP):

1. GET https://api.aiskills402.com/v1/skills/stripe-webhook-review/file without a payment header. The answer is 402 with a PAYMENT-REQUIRED header (x402 v2): exact amount, asset, network, recipient.
2. Sign `accepts[0]` with an x402 client (for example @x402/core + @x402/evm).
3. Repeat the GET with the signature in the PAYMENT-SIGNATURE header. The answer is 200 with the file, its sha256 and a re-download token.

Agent (MCP): https://mcp.aiskills402.com/mcp — free tools search_skills, get_skill, redownload_skill. Buying itself is over HTTP.

Full flow: https://aiskills402.com/docs

## The file

- Version: 1.0.0
- Size: 8.0 KB (8216 bytes)
- SHA-256: 53616e53e353ee7f7c3dab0d485e6da6cb2868f6973c27fc0b65288b4a61c399
- Updated: 2026-10-19
- New versions are free through your re-download token.

## Versions

### 1.0.0 (2026-10-19)

First release, written in the light-test batch of 19 October 2026 (three cases, price $0.01). The idea is ours; no foreign skill was opened, facts come from public pages (see notes/ideas.md). Output: one line per finding with a fixed code, or exactly `No findings.`

Test: three cases written by hand (an Express app that rebuilds the body, a Flask route with tolerance 0 and no event-id record, and a clean Next.js route), one run on Claude Sonnet and one on Claude Haiku, no run without the skill, no iteration on the skill text after the runs. `test/control.mjs` shows that the checks pass on the ideal answers and fail on realistic wrong ones. See `test-summary.json` for the verdicts read by hand.

## License

Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing. Holder: Georgi Kalchev, aiskills402.com. Terms: https://aiskills402.com/docs#license

## FAQ

### Should a raw body next to JSON routes be reported?

No. Stripe needs the raw bytes of the request for the signature check, so a webhook route that keeps them is the safe form, whatever the neighbouring routes of the app do with JSON. The skill reports a body that was parsed and written out again before the check.

### Does leaving out the tolerance argument count as a defect?

No. The official libraries accept five minutes unless told otherwise, and a handler that passes no tolerance is left alone. A tolerance of 0, which switches the recency check off, is reported, and so is a value of an hour or more, which is the policy of this skill because the documentation gives no figure for huge.

### Which handlers went into the test?

An Express app that serialises the body again, a Flask route with the tolerance at 0 and no record of event ids, and a clean Next.js route. Claude Sonnet and Claude Haiku answered each once, and by hand both were right every time.

### What does a handler without problems get?

The sentence No findings. on its own line, which makes it usable as a merge gate. A defect is a line holding a code, the place, the risk it creates and the correction.

## Measurement limits

- Models other than the two named above were not run.
- Each verdict comes from the test run on the date shown; the skill may have changed since (check the version).
- Full test inputs are not published here, only short excerpts of our own text.
- Results on your own texts, languages and domains can differ.

Offer note: Paid in USDC (USD-pegged) over x402 by an AI agent; one-time.
