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 MarkdownEsta 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
| Resultado | Qué significa | Qué hacer |
|---|---|---|
ok: true, progreso.principal: CONFIRMADO | El 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: PENDIENTE | El turno está reservado y espera la seña. | Redirigí a datos.sena.checkoutUrl (Mercado Pago). |
ok: true, requiereRevision: true | La reserva se guardó pero el link de pago no se pudo confirmar. | Reintentá con la misma clave de idempotencia. |
409 HORARIO_NO_DISPONIBLE | Alguien tomó el horario. | Volvé al paso 2 y ofrecé otros horarios. |
400 ENTRADA_INVALIDA | Falta 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"| estadoPago | Qué significa |
|---|---|
PAGADA | Mercado Pago acreditó la seña. El turno está confirmado. |
PENDIENTE | Todavía no hay pago. sena.checkoutUrl sigue sirviendo mientras esté vigente. |
VENCIDA | El link venció sin pago. El turno puede liberarse: mirá turnos[].estado. |
EN_EL_LOCAL | El negocio registra la seña a mano (por ejemplo, la cobró en el local). |
EN_REVISION | El pago necesita que alguien del negocio lo revise. |
SIN_SENA | La reserva no tenía seña. |