Reservas y pagos
Cómo se hace, se paga, se confirma y se cancela una reserva: tarjeta con Stripe, PayPal, pago a la llegada, el simulador incluido, los webhooks que confirman un pago, los reembolsos y los pasos que da el anfitrión en el panel.
Para el paquete Full Stack
Tres formas de pagar
| Método | Se ofrece cuando | Dónde paga el huésped |
|---|---|---|
| Tarjeta | Con Stripe cuando STRIPE_SECRET_KEY está definida; con el simulador incluido mientras no haya ningún procesador configurado | La página de Checkout alojada de Stripe, así que ningún número de tarjeta llega a esta aplicación; con el simulador, un formulario de tarjeta en el pago |
| PayPal | Con PayPal cuando PAYPAL_CLIENT_ID y PAYPAL_CLIENT_SECRET están definidas; con el simulador mientras no haya ningún procesador configurado | La página de aprobación de PayPal |
| Pago a la llegada | Mientras el ajuste allow_pay_on_arrival esté activado (Ajustes, Pagos) | Nada en línea: el anfitrión lo cobra en la puerta |
| Configurado en la API | Se ofrece en línea | Rechazado |
|---|---|---|
| Ni Stripe ni PayPal | Tarjeta y PayPal, ambos liquidados por el simulador | Nada |
| Solo Stripe | Tarjeta, con Stripe | PayPal |
| Solo PayPal | PayPal, con PayPal | Tarjeta |
| Ambos | Tarjeta con Stripe, PayPal con PayPal | Nada |
El pago a la llegada se ofrece junto a cualquiera de estos mientras su ajuste esté activado, y se rechaza mientras esté desactivado. El pago lee la lista de GET /api/payments/methods, así que nunca ofrece un método que la API no pueda aceptar, y un método alojado no pide datos de tarjeta en el sitio web. Una reserva enviada con un método rechazado recibe la respuesta "Ese método de pago no está disponible. Elige otra forma de pagar." antes de que se escriba nada, así que no retiene noches. El dinero va a tu propia cuenta de Stripe o PayPal; el producto nunca lo retiene.
El simulador incluido
Una instalación nueva no tiene procesador, así que el simulador liquida todas las reservas con tarjeta y PayPal al instante sin contactar con nadie. Todo el proceso se puede recorrer en un portátil sin claves, sin cuenta de comercio y sin red, y la reserva, la confirmación, el calendario y las cifras de ingresos son filas reales escritas por el proceso real. Un número de tarjeta que termina en 0 es rechazado, para que se vea el camino del fallo.
Mientras se ejecuta, el formulario de tarjeta del pago dice "Modo de prueba: no se cobra ninguna tarjeta.", GET /api/payments/methods responde simulated: true, y la API escribe una línea en su registro al iniciarse:
[Payments] Payment simulator active: card and PayPal bookings are confirmed without payment until a processor is configured (STRIPE_SECRET_KEY, or PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET).No cobra nada
Una reserva que confirma el simulador nunca se pagó, así que solo se ejecuta mientras ni Stripe ni PayPal estén configurados. Configura el procesador con el que pagarán tus huéspedes antes de abrirles el sitio web: desde entonces el simulador queda apagado, y el pago ofrece solo los métodos que aceptan tus procesadores, más el pago a la llegada mientras esté activado. La pestaña Pagos de los ajustes del panel indica cuándo se usa el simulador.
El ciclo de vida de una reserva
| Estado | Significado | Siguiente |
|---|---|---|
| Pendiente | Hecha en el sitio web, a la espera de su pago o del anfitrión | Confirmada o cancelada |
| Confirmado | Pagada, o aceptada por el anfitrión para el pago a la llegada | Con check-in realizado o cancelada |
| Check-in realizado | El huésped ha llegado. Una reserva con pago a la llegada se marca como pagada aquí | Completada o cancelada (una estancia interrumpida) |
| Completada | La estancia ha terminado, y el huésped puede reseñarla | Ninguno |
| Cancelada | Por el huésped, por el anfitrión, o porque una retención sin pagar caducó | Ninguno |
- Una reserva con tarjeta o PayPal se confirma con su pago, nunca a mano: el Confirmar del anfitrión se rechaza con "Esta reserva no se puede confirmar hasta que llegue el pago." hasta que se pague. Una reserva con pago a la llegada espera a que el anfitrión la confirme.
- El anfitrión avanza una reserva en su página del panel con Confirmar, Registrar entrada, Completar y Cancelar, y guarda una nota interna que el huésped nunca ve. Un paso que la reserva no puede dar ahora se muestra desactivado con su motivo.
- Una reserva solo avanza, y completada y cancelada son finales.
- Cada reserva tiene una referencia asignada en orden (
B0001,B0002y así sucesivamente), y conserva la tarifa por noche, la tarifa de limpieza y el total con los que se hizo. - La campana del panel avisa a todos los administradores que pueden ver reservas cuando se paga una reserva (por el webhook, la captura de PayPal o el simulador), cuando una reserva con pago a la llegada espera al anfitrión, y cuando se cancela una reserva, indicando si la terminó el huésped, el equipo o una retención sin pagar que caducó.
Lo que confirma un pago
El webhook del procesador, nunca que el huésped vuelva al sitio. Un huésped enviado a Stripe o PayPal vuelve a su propia página de reserva, en SITE_URL, que dice que el pago se está confirmando hasta que llega el webhook. Un pago que falla deja la reserva pendiente, así que el huésped puede intentarlo de nuevo hasta que se agote la retención de 30 minutos.
- PayPal necesita un paso más que Stripe: aprobar deja el pedido aprobado y no cobra nada, así que la página de retorno lo captura, y el propio resultado de la captura o el webhook liquida la reserva.
- Un pago que llega tarde para una reserva ya cancelada se registra como pagado y se deja para que el anfitrión lo reembolse: las noches pueden pertenecer ya a otra persona.
- Todos los proveedores reintentan sus webhooks, y un evento que llega dos veces confirma la reserva una sola vez.
Configura Stripe
En el .env de la API, con una clave de prueba mientras lo pruebas (una clave sk_live_ mueve dinero real, y el registro de la API lo indica al iniciarse):
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...| Evento | Qué hace |
|---|---|
checkout.session.completed | Confirma la reserva. Es el único evento que lo hace, porque es el único que significa que la sesión se pagó |
payment_intent.payment_failed | Marca el pago como fallido y deja la reserva pendiente, para que el huésped pueda pagar de nuevo |
charge.refunded | Marca el pago como reembolsado |
charge.dispute.created | Una devolución de cargo, tratada como un reembolso |
En un servidor, crea un endpoint de webhook en Stripe con la dirección pública de tu API seguida de /api/webhooks/payments/stripe, para esos cuatro eventos, y pon su secreto de firma en STRIPE_WEBHOOK_SECRET. En local, reenvía los eventos con la CLI de Stripe y pon el secreto que imprime en .env:
stripe listen --forward-to localhost:8000/api/webhooks/payments/stripeLuego completa un pago de prueba real en el navegador con la tarjeta 4242 4242 4242 4242. stripe trigger checkout.session.completed no sirve aquí: un evento provocado no lleva referencia de reserva, así que se ignora. Sin STRIPE_WEBHOOK_SECRET se rechaza todo webhook de Stripe y una reserva con tarjeta nunca se liquida.
Configura PayPal
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
PAYPAL_WEBHOOK_ID=...
PAYPAL_ENV=sandbox- PayPal se ofrece cuando el id de cliente y el secreto están definidos.
PAYPAL_ENVessandboxsalvo que definaslive, así que un despliegue mal configurado no cobra dinero real. - Suscribe un webhook a
PAYMENT.CAPTURE.COMPLETED(confirma la reserva),PAYMENT.CAPTURE.DENIED(el pago falló, la reserva sigue pendiente),PAYMENT.CAPTURE.REFUNDEDyPAYMENT.CAPTURE.REVERSED(marcados como reembolsados) en la dirección de tu API seguida de/api/webhooks/payments/paypal, y pon el id de la suscripción enPAYPAL_WEBHOOK_ID: es lo que verifica la firma de PayPal. CHECKOUT.ORDER.APPROVEDse ignora a propósito: confirmar ahí retendría noches por dinero que no se ha movido.
La ruta del webhook
POST /api/webhooks/payments/:provider, con stripe o paypal como proveedor. No necesita inicio de sesión: cada petición se comprueba con la propia firma del proveedor sobre el cuerpo en bruto y una marca de tiempo, así que no se puede ni editar ni repetir. Una firma incorrecta responde 401 y un proveedor desconocido 404; todo evento verificado responde 200, incluso los que tratan de otra cosa, para que el proveedor no los reintente durante días.
La API debe ser accesible desde internet en esa dirección. Detrás de un proxy, reenvía el cuerpo de la petición sin tocar: un cuerpo reserializado ya no coincide con su firma.
Cancelaciones y reembolsos
- Un huésped cancela desde la página de su reserva, gratis, mientras la reserva está pendiente o confirmada y la hora local de check-in está al menos a siete días. Dentro de ese plazo, la página indica que contacte con el anfitrión. El texto de la política que leen los huéspedes es el ajuste
cancellation_policy; los siete días son una constante de la API. - El anfitrión cancela desde el panel en cualquier momento antes de que la reserva se complete.
- El producto nunca devuelve dinero. Cancelar una reserva pagada marca su pago como
refund_pending; el anfitrión la reembolsa en el propio panel de Stripe o PayPal, y el webhook de reembolso es lo que escriberefunded. Una reserva pagada a la llegada la reembolsa directamente el anfitrión. - Cancelar libera las noches al instante y avisa a todos los administradores que pueden ver reservas.
Quién puede hacer qué
| Permiso | Permite |
|---|---|
bookings.view | Leer reservas, y recibir avisos de pagos, solicitudes de pago a la llegada y cancelaciones |
bookings.edit | Confirmar, Registrar entrada, Completar y Cancelar, y la nota interna |
settings.view, settings.edit | Ver y cambiar la pestaña Pagos: el pago a la llegada y la política de cancelación |
Los dos roles de la carga inicial tienen bookings.view y bookings.edit; solo el Super Admin tiene los permisos de ajustes.
Las rutas que hay detrás
GET /api/payments/methods responde { methods: [{ method, hosted }], allowPayOnArrival, simulated }. POST /api/bookings es público y responde con la referencia de la reserva, su access_token privado, su estado y estado de pago, el total y un redirect_url a la página del procesador para un método alojado; un método que la lista no contiene responde 422 con payment_method_unavailable. Las rutas propias del huésped aceptan ese token, o el correo electrónico con el que se hizo la reserva: GET /api/bookings/:reference, POST /api/bookings/:reference/settle (la captura de la página de retorno), /cancel y /review. El panel lee y mueve las reservas con GET y PATCH /api/bookings/manage/:reference, esta última con { "action": "confirm" }, check_in, complete o cancel, y admin_notes.
Cómo encajan las pasarelas de pago
Incluido con tu compra. Inicia sesión para leerlo o ábrelo en tu descarga.
La interfaz de la pasarela, cómo un método encuentra su procesador y cómo añadir el tuyo.