Idempotencia
Cómo reintentar una petición sin cobrar dos veces, con la llave de idempotencia de cada integración.
La idempotencia garantiza que una petición de pago se procese una sola vez, aunque la misma petición se envíe varias veces. Cuando ocurre un timeout de red o un error del servidor, puedes reintentar la petición de forma segura sin arriesgar un cargo duplicado.
Cómo funciona
Cuando Tonder recibe una petición con una clave de idempotencia, comprueba si ya procesó una petición con esa clave y un cuerpo idéntico:
- Primera petición — Tonder procesa el pago y guarda la respuesta asociada a la clave.
- Petición posterior con la misma clave y el mismo cuerpo — Tonder devuelve la respuesta guardada, sin crear una nueva transacción.
- Petición con la misma clave pero un cuerpo distinto — Tonder rechaza la petición. Genera una nueva clave para cualquier payload modificado.
Por modo de integración
El header varía según el tipo de integración:
| Integración | Header idempotencia | Formato | Ventana |
|---|---|---|---|
| Hosted Checkout | x-idempotency-key | string libre (ej. test-001) | 5 segundos |
| API Direct | X-Request-Id | UUID v4 (ej. 550e8400-e29b-41d4-a716-446655440000) | por petición |
| SDK | Gestionado por el SDK | — | Contacta a soporte para más detalles |
API Direct
Incluye el header X-Request-Id en cada petición POST a /process/. Usa un UUID v4 por cada
operación de pago distinta.
POST /api/v1/process/
Authorization: Token <YOUR_API_KEY>
X-Request-Id: <UNIQUE_IDEMPOTENCY_KEY>
Content-Type: application/jsonConsulta Autenticación para los detalles completos del header
Authorization.
Generar y enviar la clave
Usa un UUID v4 para cada operación de pago distinta. Los UUID son únicos a nivel global, fáciles de generar en cualquier lenguaje y seguros de guardar para depuración.
import { v4 as uuidv4 } from 'uuid';
const idempotencyKey = uuidv4();
// Ejemplo: "550e8400-e29b-41d4-a716-446655440000"
const response = await fetch('https://stage.tonder.io/api/v1/process/', {
method: 'POST',
headers: {
'Authorization': 'Token YOUR_API_KEY',
'X-Request-Id': idempotencyKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(paymentData)
});import uuid
import requests
idempotency_key = str(uuid.uuid4())
# Ejemplo: "550e8400-e29b-41d4-a716-446655440000"
response = requests.post(
'https://stage.tonder.io/api/v1/process/',
headers={
'Authorization': 'Token YOUR_API_KEY',
'X-Request-Id': idempotency_key,
'Content-Type': 'application/json'
},
json=payment_data
)curl -X POST https://stage.tonder.io/api/v1/process/ \
-H "Authorization: Token YOUR_API_KEY" \
-H "X-Request-Id: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"operation_type": "payment",
"amount": 100.00,
"currency": "MXN"
}'Hosted Checkout
Al crear una sesión con POST /checkout/v1/sessions puedes incluir el header x-idempotency-key
(opcional pero recomendado) para evitar sesiones duplicadas ante errores de red o reintentos. La
ventana de protección por defecto es de 5 segundos:
| Escenario | Resultado |
|---|---|
| Misma clave, dentro de 5 segundos | Devuelve la misma sesión — no se crea un duplicado |
| Misma clave, después de 5 segundos | Crea una nueva sesión |
| Clave distinta | Siempre crea una nueva sesión |
Reutilizar vs. regenerar la clave
La regla es simple: la clave debe coincidir con la intención. Conserva la misma clave al reintentar exactamente el mismo pago tras una falla. Genera una nueva clave cuando cambie cualquier campo del cuerpo —monto, moneda, método de pago o datos del cliente.
const idempotencyKey = uuidv4();
async function processWithRetry(paymentData, key, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await processPayment(paymentData, key); // misma clave en cada intento
} catch (error) {
if (attempt === maxAttempts) throw error;
// Backoff exponencial antes del siguiente reintento
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
}
}
}Generar una nueva clave en cada reintento anula la protección de idempotencia y puede causar cargos duplicados. Guarda la clave junto al registro del pedido antes de enviar la primera petición, para recuperarla en el reintento.
Manejo de errores
Buenas prácticas
- Incluye
X-Request-Iden cada peticiónPOSTa/process/, no solo en la lógica de reintento —protege ante fallas de red silenciosas. - Guarda las claves de idempotencia en tu base de datos junto al pedido, antes de enviar la petición.
- Usa backoff exponencial entre reintentos (empieza en 1 segundo y duplícalo).
- No uses valores predecibles (enteros secuenciales, solo el ID de pedido, o timestamps) como clave —aumentan el riesgo de colisiones.
- En un reintento, la respuesta guardada puede mostrar
PendingoFailed: verifica el estado actual con Get Transaction Status en lugar de asumir que el reintento tuvo éxito.
