# Safe retries and orderNumber

> The acquiring API is not idempotent. Exactly what to do when a payment call dies on a timeout.

- kind: concept
- status: stable
- api_version: v1
- last_verified: 2026-10-20
- url: https://tilopay.com/en/developers/reintentos-seguros

> **Warning:** The Tilopay acquiring API is not idempotent. There is no idempotency key and no retry that returns the
> original transaction. If you come from other processors, this is the assumption to dismantle
> before writing code.

## `orderNumber` is unique forever [#ordernumber]

`orderNumber` is unique per merchant **across their entire operation**, forever. It does not
reset daily, monthly or seasonally.

If it repeats, **the new transaction is rejected** with the response **"Transacción
duplicada"** (duplicate transaction). It does not return the original transaction: it returns a
rejection.

## The timeout problem [#timeout]

When a payment call dies on a network error or timeout, you do not know whether the transaction
was created. And neither intuitive way out works:

| What you would do | What happens |
|---|---|
| Retry with the same `orderNumber` | Duplicate rejection, even if the first one went through |
| Retry with a new `orderNumber` | Risk of double charge |

**On a network timeout there is no safe retry.**

## What to do instead [#que-hacer]

Query the state before deciding:

1. Call `POST /api/v1/consult` with the original `orderNumber`.
2. If the transaction exists, use its result. Do not retry.
3. If it does not exist, then you can retry — and you can reuse the same `orderNumber`, because
   it was not consumed.

```text
payment → timeout
           │
           └─→ consult(original orderNumber)
                 ├─ exists     → use that result
                 └─ not found  → retry
```

## Design consequences [#diseno]

- Generate the `orderNumber` in your system **before** calling the payment and persist it. If you
  generate it on the fly, after a timeout you have nothing to query with.
- Never derive the `orderNumber` from something that can repeat (recycled cart number, truncated
  timestamp, resettable counter).
- A "Transacción duplicada" does not mean the charge failed: it means that `orderNumber` was
  already used. Query before showing the customer an error.
