Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 25, 2025

Thanks for assigning this issue to me. I'm starting to work on it and will keep this PR's description up to date as I form a plan and make progress.

Original prompt

This section details on the original issue you should resolve

<issue_title>[Phase 1] Idempotency Key & Request Replay Safety Layer</issue_title>
<issue_description>## Priority: P0 (Critical)
Phase: 1 - E-Commerce Core
Estimate: 2 days
Type: Story

Context

Prevent duplicate side effects (double charges, duplicate orders, inventory double-decrement) by introducing a generic idempotency layer for write-intent endpoints.

Scope

  • Table: IdempotencyKey (key, actorId, route, requestHash, responseJson, status, expiresAt)
  • Middleware: Extract Idempotency-Key header, validate format (uuid or slug), enforce uniqueness per route + actor until expiry
  • Safe replay: Return cached response if prior success; block if in-flight
  • In-flight protection: row-level status PENDING → COMPLETED / ERRORED; consider Redis optimization later
  • Apply to: order create, payment attempt create, refund create, inventory reservation create

Acceptance Criteria

  • Duplicate POST with identical body + Idempotency-Key returns 200 with identical payload (no additional side effects)
  • Concurrent identical POST produces single side effect
  • Conflicting body with same key returns 409 + explanatory error
  • Key expiry configurable (default 24h, min 5m, max 72h)
  • Keys cleaned by scheduled sweep (success + expired) preserving last 7d metrics
  • Metrics emitted: idempotency.replay.count, idempotency.conflict.count, idempotency.inflight.gauge
  • Structured log includes key, route, status, duration

Data Model (Draft)

model IdempotencyKey {
  id           String   @id @default(cuid())
  key          String   @unique
  actorId      String?  // user or system client
  route        String
  requestHash  String
  responseJson String? // minimal JSON snapshot for replay
  status       IdempotencyStatus @default(PENDING)
  expiresAt    DateTime
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  @@index([route])
  @@index([expiresAt])
}

enum IdempotencyStatus {
  PENDING
  COMPLETED
  ERRORED
}

Dependencies

Metrics Targets

  • Replay ratio (duplicate vs unique) < 5%
  • Conflict error detection: 100% logged
  • Average lookup latency < 3ms (SQLite dev), < 1ms (Postgres prod) after index tuning

Testing Checklist

  • Duplicate POST same body same key returns cached response
  • Duplicate POST different body same key returns 409
  • Concurrent requests do not double-create order under load (simulate 10 parallel)
  • Expired key allows new side effect

Risk

High financial & integrity impact if absent (score: 17). Prevents double charges & inventory corruption.

References

  • Stripe Idempotency design patterns
  • docs/GITHUB_ISSUES_COMPARISON_ANALYSIS.md
    </issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link

vercel bot commented Nov 25, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
stormcomui Ready Ready Preview Comment Nov 25, 2025 2:35am

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

[Phase 1] Idempotency Key & Request Replay Safety Layer

2 participants