Conceptos

Idempotencia

Una clave por reserva para poder reintentar sin crear dos turnos ni dos cobros.

Ver en Markdown

POST /api/v1/reservas exige la cabecera Idempotency-Key. Si la conexión se corta y no sabés si la reserva se creó, repetí el pedido con la misma clave y el mismo cuerpo: vas a recibir la misma reserva, sin otra fila, sin otro mail y sin otro link de pago.

CasoRespuesta
Clave nueva201 con la reserva y operacion.repetida: false.
Misma clave, mismo cuerpo200 con la misma reserva y operacion.repetida: true.
Misma clave, otro cuerpo409 CONFLICTO_INTENCION. No se crea nada.
Dos pedidos a la vez con la misma claveSe crea una sola reserva; los dos reciben el mismo resultado.
Sin la cabecera400 IDEMPOTENCY_KEY_FALTANTE, sin escribir nada.

Cómo elegir la clave

  • Entre 8 y 100 caracteres: letras, números, punto, guion, guion bajo o dos puntos.
  • Generala una vez por intento de reserva de la clienta (por ejemplo, al abrir el paso de confirmación) y guardala hasta tener respuesta. Un UUID sirve.
  • Si la clienta cambia el horario o los datos, es otra reserva: usá otra clave.
  • La clave es del negocio, no de la credencial: si rotás la clave de la API, el reintento con la nueva recupera la reserva hecha con la anterior.
// Una clave por intento de reserva, reutilizada en cada reintento.
const idem = sessionStorage.getItem("reservaIdem") ?? crypto.randomUUID();
sessionStorage.setItem("reservaIdem", idem);

async function reservar(cuerpo, intentos = 3) {
  for (let i = 0; i < intentos; i++) {
    try {
      const res = await fetch("https://turnovapp.com/api/v1/reservas", {
        method: "POST",
        headers: {
          Authorization: "Bearer tnv_pk_TU_CLAVE_PUBLICA",
          "Content-Type": "application/json",
          "Idempotency-Key": idem,
        },
        body: JSON.stringify(cuerpo),
      });
      const r = await res.json();
      if (r.ok || !r.error.reintentable) return r;
      const espera = Number(res.headers.get("Retry-After") ?? 2);
      await new Promise((ok) => setTimeout(ok, espera * 1000));
    } catch {
      // Red caída: reintentar con la MISMA clave es seguro.
    }
  }
  throw new Error("No pudimos confirmar la reserva");
}

Las lecturas (GET) no llevan clave de idempotencia: se pueden repetir libremente.