Skip to content

feat(compliance): make kyc session initiation idempotent - #286

Merged
tiana-code merged 1 commit into
mainfrom
feat/E-04-kyc-idempotency
Jun 19, 2026
Merged

feat(compliance): make kyc session initiation idempotent#286
tiana-code merged 1 commit into
mainfrom
feat/E-04-kyc-idempotency

Conversation

@tiana-code

Copy link
Copy Markdown
Owner

E-04 #277 (LAST E-04 slice) - Idempotency-Key dedup on POST /v1/kyc/sessions, mirroring the merged payments #211 service-level pattern.

What

  • Migration v0.1/022-idempotency-keys.sql (idempotent): compliance.idempotency_keys (key_hash VARCHAR(64) PK, kyc_session_id UUID, created_at), wired into the master changelog.
  • KycIdempotencyStore.reserveOrRun (@transactional): replay the session by the stored id if the key exists; else run the action and record the key; a duplicate-PK DataIntegrityViolationException -> KycIdempotencyRaceException.
  • KycServiceImpl.initiate is no longer @transactional (the retry must span transactions; the store owns the boundary): Sha256.hex(key) -> bounded retry (MAX_ATTEMPTS=3) -> KycConcurrencyException on a persistent race. A lost race retries in a fresh transaction, finds the winner's committed key, and replays.
  • Controller requires Idempotency-Key (400 via a new MissingRequestHeaderException handler); concurrency -> 503 retryable with Retry-After. Only the SHA-256 hash is stored, never the raw header.

Gate chain

  • critic: GO-WITH-CHANGES (header on all 4 controller-test POSTs + a dedicated missing-header-400 test; retryable(type,detail,request) verbatim; exception keeps its message - all applied).
  • security-auditor (opus): PASS - idempotency correct under concurrency (fresh-tx retry sees the committed winner, no double-create), raw key never persisted/logged, fail-closed 400/503, §5.3 clean, 4/4 ACs.
  • code-reviewer: CHANGES-REQUIRED, but all findings declined with source evidence - the var-vs-val, store-calls-toDomain, and missing-header-200 claims each contradict the merged payments template (which uses var, calls adapter.toDomain in its store, and returns a bare ProblemDetail); the missing-header->400 test ran and passed (7 tests, 0 failures).
  • evaluator: PASS (0.88).
    All local gates green; the replay + screening + round-trip ITs run on CI.

With this merged, epic E-04 Compliance #162 is complete.

Closes #277

Add Idempotency-Key support to POST /v1/kyc/sessions, mirroring the
payments service. A new compliance.idempotency_keys table keyed on the
SHA-256 of the header backs a reserve-or-run store: the first call
creates the session and records the key in the same transaction, and a
repeat with the same key replays the original session instead of
creating a second one. The controller now requires the header (400 if
absent), a lost race retries in a fresh transaction and falls back to a
503 after the bounded attempts, and only the key hash is stored, never
the raw header value.

Closes #277
@tiana-code tiana-code added this to the v0.3.0 milestone Jun 19, 2026
@tiana-code
tiana-code merged commit 8eb8cf7 into main Jun 19, 2026
8 checks passed
@tiana-code
tiana-code deleted the feat/E-04-kyc-idempotency branch June 19, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

F-04.5c KYC initiate idempotency (Idempotency-Key)

1 participant