Repair a webhook handler that pays twice

Your handler rejects some genuine deliveries and records replayed payments twice. Fix the function below, then test valid, changed, replayed, and concurrent deliveries.

Requires Node.js 22 or newer. No package installation or server needed. To work in a container instead, open the folder with VS Code Dev Containers or GitHub Codespaces: .devcontainer/devcontainer.json uses the Dev Containers Node.js 24 image, pinned by digest.
1. Unzip all files into one folder.
2. In that folder, run: node --test exercise.test.mjs
3. Edit handler.mjs, then run the same command again.

The starter deliberately fails some tests. A successful repair passes every case, including cases that catch common wrong fixes. The browser runs this same cases.mjs with the same fixtures; locally Node runs your code directly. If you completed the optional first stage in the browser, Download my work adds your input as stage1-input.json and the tests include it. Only run code you wrote or reviewed.

1. Reproduce both bugs
Run the starter. “Accept a genuinely signed, formatted body” fails because re-serialization changes the signed bytes. “Deliver the same event twice” records two payments because the tag remains valid on replay.

2. Authenticate, then parse and validate
Call await verify(delivery.rawBody, delivery.tag) before parsing. Return rejected immediately when verification is false. After a valid signature, parse the original body and pass the result to validateEvent. The signed-malformed-json case deliberately has a genuine tag but invalid JSON: catch parsing or validation failures and return rejected without recording a payment.

3. Apply the effect once
Replace ledger.record with await ledger.applyOnce(event.id, event), and return its processed or duplicate result. Use the ID inside the authenticated event.

4. Test retries and real new work
Run every case. Concurrent retries must create one payment; two distinct valid events must create two. Blanket rejection or skipping every later event fails these checks.

SUPPLIED CONTRACT
delivery.rawBody is the unchanged UTF-8 text supplied by this synthetic fixture; delivery.tag is its HMAC-SHA-256 tag. services.verify authenticates the UTF-8 encoding with real Web Crypto. In an HTTP integration preserve the provider-signed byte sequence before text decoding or body middleware can change it. validateEvent returns the validated event and checks its account and fields. ledger.applyOnce(event.id, event) is a supplied atomic operation returning processed or duplicate. deliveryId is an unsigned header and is not the event identity.

REFERENCE
reference.mjs contains an explained solution: The formatted body has a valid tag for its whitespace, so verifying its original text succeeds while re-serialization fails. Two copies of evt_042 return processed then duplicate and produce one payment. Changing the unsigned delivery header still produces duplicate. The signed evt_043 is genuinely new and creates a second payment. A signed wrong account or unexpected event type passes cryptographic verification but fails validation and produces no payment.

SCOPE
This exercise signs only the body. A production integration must follow its provider’s exact envelope, signed timestamp and freshness rules, size limits, key rotation, and account authorization. applyOnce is a supplied model; its durable transaction and recovery design must be implemented in the real service.

NEXT TASK
Trace a real handler from raw input to side effect. Which values are authenticated, how are retries identified, and can two simultaneous deliveries both claim the same event?

SOURCE
https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries
