PAY-812Awaiting decidertech-choice

Which idempotency key shape for refund retries?

The settlement-v2 webhook handler retries failed refunds. Today the dedup table uses a server-generated UUID per call, which means a transport retry produces a duplicate row and a duplicate refund attempt against the PSP.

Decider
@maren · staff-eng
due 3h
85%

Drafted memo

We want the smallest change that makes refund retries idempotent end-to-end.

`refund_id + attempt` is stable across the wire — the client picks attempt N once and replays the same key on transport failures.

A new UUID per retry forces server-side dedup logic and a wider window in Postgres; we already saw this break under burst load in PAY-744.

Recommendation: ship `refund_id + attempt` behind the existing key column; rolling deploy, no migration.

Options

refund_id + attempt — stable across retries
Client-derived, deterministic, replay-safe
Recommended
cost: 30 lines in the handler; no schema change
New UUID per retry, dedup server-side
Doesn't require client coordination
cost: Wider dedup window; PAY-744-class regression risk
tenant_id + refund_id hash
Partitions cleanly across tenants
cost: Hash collisions in long tail; harder to debug

When you ship Cloud, deciders pick from Slack or here. The decision is committed back as a memo under .crastinating/decisions/.