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.
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).
Vita consulta por order
GET /api/businesses/transactions?order=<tu-order> resuelve la ambigüedad después de un timeout.Patrón recomendado
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
Generar un nuevo order en cada reintento
Generar un nuevo order en cada reintento
Rompe idempotencia. Vita no puede saber que estás reintentando una operación anterior y creará duplicados.
Reintentar POST sin idempotency check después de 5xx
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ó”.
Confiar solo en el response HTTP para el estado final
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.Procesar un webhook sin deduplicar
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.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:- Consulte
GET /api/businesses/transactions/get_all_transactionscon un rango de fecha. - Compare contra tu base de datos.
- Marque discrepancias para revisión (ej. transacción creada en Vita pero no en tu DB, o viceversa).