Guías

Reservar un turno de punta a punta

Catálogo, horarios, reserva, seña por Mercado Pago y qué pasa cuando la clienta vuelve del pago.

Ver en Markdown

Esta guía arma el recorrido completo desde un sitio propio con la clave pública, y usa la secreta desde tu servidor para confirmar el pago.

1. Mostrar los servicios

const API = "https://turnovapp.com/api/v1";
const CLAVE = "tnv_pk_TU_CLAVE_PUBLICA";
const llamar = async (ruta, init = {}) => {
  const res = await fetch(API + ruta, { ...init, headers: { Authorization: `Bearer ${CLAVE}`, ...init.headers } });
  return { res, r: await res.json() };
};

const { r: catalogo } = await llamar("/catalogo");
const servicios = catalogo.datos.servicios.filter((s) => !s.seReservaPorWhatsapp);

Usá precio y duracionMin solo para mostrar: el importe final lo calcula Turnova al reservar (descuentos, cupón, extras). Un servicio con seReservaPorWhatsapp: true no se puede reservar por la API: ofrecé el contacto por WhatsApp del negocio.

2. Pedir los horarios

const q = new URLSearchParams({ servicioId: servicio.id, desde: hoy, dias: "14" });
const { r: disp } = await llamar(`/disponibilidad?${q}`);
const horasDelDia = disp.datos.horarios[fechaElegida] ?? [];

3. Crear la reserva

Generá una Idempotency-Key al confirmar y reusala si tenés que reintentar (ver Idempotencia).

const { res, r } = await llamar("/reservas", {
  method: "POST",
  headers: { "Content-Type": "application/json", "Idempotency-Key": idem },
  body: JSON.stringify({
    turnos: [{ servicioId: servicio.id, fecha: "2030-01-10", hora: "10:00" }],
    cliente: { nombre: "Ana Pérez", telefono: "3515550101", codigoPais: "+54", email: "[email protected]" },
  }),
});

4. Leer el resultado

ResultadoQué significaQué hacer
ok: true, progreso.principal: CONFIRMADOEl negocio no cobra seña (o quedó cubierta). El turno está confirmado.Mostrá la confirmación. A la clienta le llega el mail de Turnova.
ok: true, principal: GUARDADO, pago: PENDIENTEEl turno está reservado y espera la seña.Redirigí a datos.sena.checkoutUrl (Mercado Pago).
ok: true, requiereRevision: trueLa reserva se guardó pero el link de pago no se pudo confirmar.Reintentá con la misma clave de idempotencia.
409 HORARIO_NO_DISPONIBLEAlguien tomó el horario.Volvé al paso 2 y ofrecé otros horarios.
400 ENTRADA_INVALIDAFalta o sobra un dato.Mirá error.campos y corregí.
if (!r.ok) return mostrarError(r.error);
if (r.datos.sena?.checkoutUrl) {
  localStorage.setItem("ultimaReserva", r.datos.compraId);
  window.location.href = r.datos.sena.checkoutUrl;
} else {
  mostrarConfirmacion(r.datos.turnos);
}

5. Seña por Mercado Pago

La seña se cobra en la cuenta de Mercado Pago del negocio. El link es el mismo que usa la página de reservas de Turnova, con la misma vigencia. La seña se confirma solo cuando Mercado Pago avisa a Turnova (webhook): nunca la des por pagada porque la clienta volvió a tu sitio.

6. Qué pasa al volver del pago

En esta versión, al terminar el pago Mercado Pago lleva a la clienta a la página de resultado de Turnova del negocio, que le muestra si el pago se acreditó y el detalle de su turno. No vuelve automáticamente a tu sitio.

Si querés mostrar el estado en tu sitio, consultalo desde tu servidor con la clave secreta y el compraId que guardaste:

curl "https://turnovapp.com/api/v1/reservas/cmh4k2n9x0001" \
  -H "Authorization: Bearer $TURNOVA_CLAVE_SECRETA"
estadoPagoQué significa
PAGADAMercado Pago acreditó la seña. El turno está confirmado.
PENDIENTETodavía no hay pago. sena.checkoutUrl sigue sirviendo mientras esté vigente.
VENCIDAEl link venció sin pago. El turno puede liberarse: mirá turnos[].estado.
EN_EL_LOCALEl negocio registra la seña a mano (por ejemplo, la cobró en el local).
EN_REVISIONEl pago necesita que alguien del negocio lo revise.
SIN_SENALa reserva no tenía seña.