> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vitawallet.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotencia y reintentos

> Patrones seguros para reintentar operaciones sin duplicar transacciones.

Toda integración de pagos necesita manejar fallos de red, timeouts y respuestas ambiguas. Si no reintentas con cuidado, puedes **duplicar pagos**. Este es el patrón que soporta la API de Vita.

## Idea general

Cuando creas una transacción (`POST /api/businesses/transactions`), siempre envías un campo `order` — un identificador único **generado por ti**. Vita lo persiste con la transacción. Si pierdes la respuesta y necesitas saber si la operación se registró, consultas `GET /api/businesses/transactions?order=<tu-order>` antes de reintentar.

> [card] Tú generas `order`
>
> Único por intención de operación en tu sistema. UUID v4, ULID, o un identificador propio que ya tengas (invoice ID, por ejemplo).

> [card] Vita consulta por `order`
>
> `GET /api/businesses/transactions?order=<tu-order>` resuelve la ambigüedad después de un timeout.

## Patrón recomendado

```mermaid theme={null}
flowchart TD
  Start[Intención de pago] --> Gen[Genera un order único]
  Gen --> Post[POST /transactions]
  Post --> Resp{¿Respuesta?}
  Resp -- 2xx --> Done[Guarda el ID de Vita]
  Resp -- 4xx validación --> Fix[Corrige payload]
  Resp -- 5xx / timeout --> Check[GET /transactions?order=X]
  Check --> Exists{¿Existe?}
  Exists -- Sí --> Done
  Exists -- No --> Retry[Reintentar POST con el mismo order]
  Retry --> Post
```

**1. Genera un `order` único antes del POST**

Guárdalo en tu base de datos junto con la intención (`created`, pendiente de envío). No lo generes dentro del request — si pierdes el response, tienes que poder recuperarlo.

**2. Envía el POST**

Timeout agresivo del lado cliente (ej. 30s). Si recibes 2xx, guarda el `id` que te devuelve Vita y marca la operación como `sent`.

**3. Si recibes 5xx o timeout, NO reintentes aún**

La transacción puede haberse creado del lado de Vita aunque no te llegó la respuesta.

**4. Haz idempotency check**

`GET /api/businesses/transactions?order=<tu-order>`. Si vuelve un resultado, significa que Vita sí la creó — guarda el ID y marca como `sent`. Si no vuelve nada, puedes reintentar el POST con el **mismo** `order`.

**5. Limita reintentos y usa backoff**

Reintenta con backoff exponencial (ej. 1s, 2s, 4s, 8s), máximo 5 veces. Después, marca para revisión manual y alerta a tu equipo.

## Qué NO hacer

> [accordion] Generar un nuevo `order` en cada reintento
>
> Rompe idempotencia. Vita no puede saber que estás reintentando una operación anterior y creará duplicados.

> [accordion] Reintentar POST sin idempotency check después de 5xx
>
> Riesgo alto de duplicar el pago. El 5xx no significa "no se creó" — significa "no sé si se creó".

> [accordion] Confiar solo en el response HTTP para el estado final
>
> El 2xx del POST indica que la transacción **fue aceptada**, no que llegó a estado final. El estado final (`completed`, `denied`, `failed`) solo lo sabes vía webhook o re-consultando con `GET /transactions/:id`.

> [accordion] Procesar un webhook sin deduplicar
>
> Vita reintenta webhooks que no reciben 200. Deduplica usando `event_id` (opaco, no asumas que es un UUID) + el `id` de la transacción + el estado.

## Qué hacer con los webhooks

Los webhooks también requieren idempotencia del lado tuyo:

**1. Responde 200 rápido (\<5s)**

Procesa la lógica pesada de forma asíncrona (queue). Si tardas en responder, Vita cierra la conexión y reintenta.

**2. Deduplica antes de procesar**

Guarda `(event_id, transaction_id, status)` como clave. Si ya lo procesaste, responde 200 y ya.

**3. Valida la firma HMAC-SHA256**

Rechaza (`401`) cualquier webhook con firma inválida. Ver [Webhooks → Signature calculation](https://docs.vitawallet.io/api-reference/webhooks/overview.md).

**4. Nunca re-serialices el body antes de validar**

La firma se calcula sobre los bytes exactos que Vita envió. Un reformateo (incluso solo re-pretty-print) invalida la firma.

## Reconciliación periódica

Aun con webhooks + idempotency check, agenda un job diario que:

1. Consulte `GET /api/businesses/transactions/get_all_transactions` con un rango de fecha.
2. Compare contra tu base de datos.
3. Marque discrepancias para revisión (ej. transacción creada en Vita pero no en tu DB, o viceversa).

Esto captura casos borde que escaparon a los webhooks (DNS caído, deploys, timeouts en cascada) y es un requisito de auditoría estándar en pagos.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.