# agent retried adyen /payments with a new idempotency key each time: duplicate charges

## TL;DR
Each retry got a fresh idempotency key, so Adyen treated every attempt as a brand-new payment. The fix is one key per payment intent: generate the key once, reuse it across all retries of that attempt, and refund the duplicates. New key per retry is the opposite of what idempotency is for.

```text
agent retried adyen /payments with a new idempotency key each time: duplicate charges
```

## Steps

1. Stop the retry loop and count the damage: list every charge created for the affected orders. Success check: you know exactly how many duplicates exist.
2. Refund the duplicate charges, keeping the earliest successful one per order. Success check: each order has exactly one live charge.
3. Fix the retry code: generate the idempotency key once per payment intent and pass the same key on every retry. Success check: retries reuse the key; only genuinely new payments get new keys.
4. Verify with a test: fail the first attempt artificially and confirm the retry returns the original result instead of a new charge. Success check: the test proves retries are idempotent.
5. Notify affected customers about the duplicate charge and the refund before they discover it. Success check: the message goes out proactively, with refund timing.

## When this applies

- Adyen charged a customer multiple times because retries used new idempotency keys.
- You need retry logic that cannot duplicate payments.
- An agent regenerates keys per attempt.

## When it doesn't

- Duplicate orders placed by the customer; those are separate intents.
- Deliberate separate charges like installments.
- Duplicate webhooks; those are a notification dedupe problem.

## Tool + version compatibility

Adyen /payments API idempotency (all current API versions).

### "adyen duplicate charges retry"
One key per payment intent, reused across retries.

### "adyen idempotency key new each retry"
That is the bug; the key must be stable across retries.

### "adyen charged twice same order"
Refund duplicates, keep the earliest, fix the key lifecycle.

## Why it happens

An idempotency key tells Adyen: if you have seen this key, return the original result instead of charging again. A new key per retry tells Adyen the opposite: each attempt is a different payment the customer supposedly authorized. The gateway is doing exactly what it was told. Agent retry wrappers commonly generate a fresh UUID per attempt because that pattern is correct everywhere except payment APIs.

## Edge cases / pitfalls

- Refunding is not instant; tell customers the timeline so they do not dispute the duplicate.
- Check whether the duplicates also created duplicate fulfillment; refunds alone may not unwind the order.
- The key must be stable across process restarts too; store it with the payment intent, not in memory.
- Distinguish retry of one intent (same key) from a customer placing a second order (new key); conflating them either duplicates or blocks.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_KYWiOEokkAVDLoOgFhn7iA
