Skip to content

fix(rest/nodejs): scope idempotency hashes to checkout operations - #171

Open
XiaolongZhang-TT wants to merge 1 commit into
Universal-Commerce-Protocol:mainfrom
XiaolongZhang-TT:fix/nodejs-scope-idempotency-hashes
Open

fix(rest/nodejs): scope idempotency hashes to checkout operations#171
XiaolongZhang-TT wants to merge 1 commit into
Universal-Commerce-Protocol:mainfrom
XiaolongZhang-TT:fix/nodejs-scope-idempotency-hashes

Conversation

@XiaolongZhang-TT

Copy link
Copy Markdown
Contributor

Description

The Node.js sample keyed idempotency records only by the Idempotency-Key header and fingerprinted the request body alone, so the same key reused against a different checkout (or a different operation) replayed the first response instead of conflicting. This was most severe for cancel, whose hash was always the empty body, so any two cancels sharing a key were treated as identical regardless of target.

Repro: create two checkouts A and B, then cancel both with the same Idempotency-Key → the second cancel returns 200 with A's canceled checkout (silent replay) instead of conflicting.

Root causerest/nodejs/src/api/checkout.ts, computeHash(data): the fingerprint was the body only (cancel: {}), with no operation or target checkout.

Fix: include the operation and, for update/complete/cancel, the target checkout id in the fingerprint — computeHash(operation, data, resourceId?). This mirrors the Python sample (#166): create is keyed by body, update/complete/cancel additionally by the path checkout id, so a key can neither cross checkouts nor cross operations.

Category (Required)

  • Samples / Conformance: Maintaining samples and the conformance suite. (Requires Maintainer approval)

Related Issues

None — parity with the Python sample's idempotency scoping landed in #166.

Checklist

  • I have followed the Contributing Guide (Conventional Commits title requirements and ! for breaking changes).
  • I have updated the documentation (if applicable). — N/A, no documentation changes.
  • My changes pass all local linting and formatting checks. (tsc --noEmit clean; prettier clean.)
  • I have added tests that prove my fix is effective or that my feature works. (an idempotency key is scoped to the checkout (cancel) and …(update) — both fail before the fix, pass after.)
  • New and existing unit tests pass locally with my changes. (Node.js suite: 118 passed.)
  • (For Core/Capability) I have included/updated the relevant JSON schemas. — N/A, no schema changes.
  • I have regenerated Python Pydantic models by running generate_models.sh under python_sdk. — N/A, no schema changes.

Screenshots / Logs (if applicable)

Before/after — reusing one Idempotency-Key against two different checkouts:

Before (bug):

cancel A (key=k) -> 200
cancel B (key=k) -> 200   # replayed A's canceled checkout

After (fix):

cancel A (key=k) -> 200
cancel B (key=k) -> 409   # Idempotency key reused with different parameters

Full Node.js suite:

# tests 118
# pass 118
# fail 0

The Node.js sample keyed idempotency records only by the Idempotency-Key
header and fingerprinted the request body alone, so the same key reused
against a different checkout (or a different operation) replayed the first
response instead of conflicting. This was most severe for cancel, whose
hash was always the empty body, so any two cancels sharing a key were
treated as identical regardless of target.

Include the operation and, for update/complete/cancel, the target checkout
id in the fingerprint — mirroring the Python sample (Universal-Commerce-Protocol#166). Add regression
tests showing a shared key across two checkouts (cancel and update) now
returns 409 instead of replaying.
@damaz91 damaz91 added the status:needs-triage Signal that the PR is ready for human triage label Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:needs-triage Signal that the PR is ready for human triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants