Docs / Sdk

Hotelpay SDK

El SDK de Hotelpay proporciona una forma sencilla de integrar pagos en tu aplicación web. Soporta dos modos de integración: Overlay e Iframe.

Instalación

Agrega el script del SDK a tu página:

<script src="https://js.hotelpay.net/v2/sdk/hotelpay.umd.js"></script>

Referencia API

Hotelpay: {
  Checkout: {
    // Configuración General
    setApiKey: (key: string) => Promise<void>;
    setCurrency: (currency: "MXN" | "USD") => void;
    setTotal: (total: number) => void;
    setDetails: (data: Details) => void;
    setMode: (mode: "testing" | "production") => void;
    setLanguage: (lang: "es" | "en") => void;
    setAddress: (address: Address) => void;

    // Modo Overlay
    showOverlay: () => void;
    onSuccess: (callback: () => void) => void;
    onCancel: (callback: () => void) => void;
    onFail: (callback: (error?: any) => void) => void;

    // Modo Iframe
    renderIframe: (selector: string) => Promise<void>;
    on: (event: string, callback: Function) => void;
    processPayment: () => Promise<void>;

    // Personalización
    setCSS: (css: string) => void;
  }
}

Ambientes

Testing

Ambiente para pruebas y desarrollo. Usa tarjetas de prueba para simular transacciones sin realizar cargos reales. Este ambiente es crucial para verificar la integración y el flujo de pago antes de ir a producción.

Hotelpay.Checkout.setMode("testing");

Production

Ambiente de producción para transacciones reales. Asegúrate de haber probado exhaustivamente en el ambiente de testing antes de cambiar a producción, ya que aquí se realizarán cargos reales a las tarjetas.

Hotelpay.Checkout.setMode("production");

Configuración Básica

setApiKey

Establece la clave API para autenticar las solicitudes. Esta clave es única para cada comercio y ambiente.

Hotelpay.Checkout.setApiKey("tu_api_key");

setCurrency

Define la moneda para las transacciones. Actualmente se soportan pesos mexicanos (MXN) y dólares americanos (USD).

Hotelpay.Checkout.setCurrency("MXN"); // o "USD"

setTotal

Establece el monto total de la transacción. Es importante notar que solo se permiten hasta 2 decimales.

⚠️
Nota importante: El monto solo puede ser modificado antes del primer intento de procesamiento de pago. Una vez que se intenta procesar el pago, aunque este falle, no será posible modificar el monto. Solo se podrá intentar nuevamente con una tarjeta diferente manteniendo el monto original.
// El monto debe tener máximo 2 decimales
Hotelpay.Checkout.setTotal(444.44);

setLanguage

Define el idioma de la interfaz. Actualmente se soportan español (es) e inglés (en).

Hotelpay.Checkout.setLanguage("es"); // o "en"

setDetails

Configura los detalles de la reservación. Este método requiere información específica sobre la reserva, los items a cobrar y el cliente.

Hotelpay.Checkout.setDetails({
  // ID interno opcional para referencia del comercio
  referenceId: "reserva-123",

  // Datos de la reserva (requerido)
  reserve_data: {
    booking_number: "00001", // Número de reserva
    checkin: "2024-12-01",   // Fecha de entrada
    checkout: "2024-12-31",  // Fecha de salida
    guests_adults: 2,        // Número de adultos
    guests_children: 1       // Número de niños
  },

  // Items a cobrar (requerido)
  // Cada item debe especificar descripción, precio y cantidad
  items: [
    {
      description: "Habitación doble vista al mar",
      price: 56.79,
      quantity: 1
    }
  ],

  // Información del cliente (requerido)
  customer_info: {
    first_name: "Juan",
    last_name: "Pérez",
    phone: "5512345678",
    email: "juan@ejemplo.com"
  }
});

setAddress

Establece la dirección de facturación del cliente. Esta información es importante para la validación de la tarjeta.

Hotelpay.Checkout.setAddress({
  address: "Calle Principal 123",
  city: "Ciudad de México",
  state: "CDMX",
  country: "México",
  country_code: "MX", // Código ISO del país
  zip: "01234"
});

processPayment

Este método inicia el proceso de pago con la información proporcionada. Es asíncrono y debe ser llamado con await o manejado como una promesa.

⚠️
Nota importante: Antes de llamar a este método, asegúrate de que:
  1. El formulario de tarjeta esté completo y válido (complete-form-card con is_valid: true)
  2. Hayas establecido toda la información requerida (setDetails, setAddress, etc.)
try {
  // Iniciar el proceso de pago
  await Hotelpay.Checkout.processPayment();
  // El pago se procesará y se emitirán los eventos correspondientes
  // (paymentCompleted o paymentError)
} catch (error) {
  // Manejar cualquier error inesperado
  console.error('Error al procesar el pago:', error);
}

Modo Overlay

El modo overlay muestra el formulario de pago en una ventana modal sobre tu sitio. Es la forma más sencilla de integrar el SDK.

Implementación Básica

// Configurar callbacks para manejar los diferentes estados del pago
Hotelpay.Checkout.onSuccess(() => {
  // Se ejecuta cuando el pago es exitoso
  console.log("Pago exitoso");
});

Hotelpay.Checkout.onFail((error) => {
  // Se ejecuta cuando hay un error en el pago
  // El parámetro error contiene detalles sobre el problema
  console.log("Error en el pago:", error);
});

Hotelpay.Checkout.onCancel(() => {
  // Se ejecuta cuando el usuario cancela el pago
  console.log("Pago cancelado");
});

// Mostrar el overlay de pago
Hotelpay.Checkout.showOverlay();

Modo Iframe

El modo iframe integra el formulario de pago directamente en tu página. Este modo ofrece mayor flexibilidad en la integración y personalización.

Implementación Básica (Iframe)

<!DOCTYPE html>
<html>
<head>
    <title>Checkout con Iframe</title>
    <script src="https://cdn.hotelpay.com/sdk/v2/sdk.js"></script>
    <style>
        #iframe-container {
            width: 100%;
            max-width: 600px;
            margin: 0 auto;
        }
        #payment-button {
            display: block;
            width: 100%;
            padding: 12px;
            background: #4F46E5;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 16px;
            cursor: pointer;
            margin-top: 20px;
        }
        #payment-button:disabled {
            background: #9CA3AF;
            cursor: not-allowed;
        }
    </style>
</head>
<body>
    <div id="iframe-container">
        <div id="payment-form"></div>
        <button id="payment-button" disabled>Pagar $100.00 MXN</button>
    </div>

    <script>
        const { Checkout } = Hotelpay;
        const payButton = document.getElementById('payment-button');

        // Configuración inicial
        Checkout.setApiKey('tu_api_key');
        Checkout.setMode('testing');
        Checkout.setLanguage('es');
        Checkout.setCurrency('MXN');
        Checkout.setTotal(100);

        // Renderizar iframe
        Checkout.renderIframe('#payment-form');

        // Escuchar evento de formulario completo
        Checkout.on('complete-form-card', (data) => {
            payButton.disabled = !data.is_valid;
        });

        // Escuchar inicio de transacción
        Checkout.on('transaction-start', () => {
            payButton.disabled = true;
            payButton.textContent = 'Procesando...';
        });

        // Escuchar transacción completada
        Checkout.on('transaction-completed', (data) => {
            payButton.textContent = '¡Pago Exitoso!';
            console.log('Transacción completada:', data);
        });

        // Manejar clic en botón de pago
        payButton.addEventListener('click', async () => {
            try {
                await Checkout.processPayment();
            } catch (error) {
                console.error('Error al procesar el pago:', error);
                payButton.disabled = false;
                payButton.textContent = 'Reintentar Pago';
            }
        });
    </script>
</body>
</html>

Este ejemplo muestra:

  • Configuración básica del SDK
  • Renderizado del iframe de pago
  • Manejo del estado del botón basado en la validación del formulario
  • Procesamiento de pago al hacer clic en el botón
  • Actualización de la UI durante el proceso de pago

Eventos Disponibles

El SDK proporciona varios eventos que puedes escuchar para controlar el flujo de pago:

complete-form-card

Se ejecuta cuando cambia el estado de validación del formulario de tarjeta.

Hotelpay.Checkout.on("complete-form-card", (data) => {
  // data.is_valid será true cuando:
  // - El número de tarjeta sea válido (pasa validación Luhn)
  // - La fecha de expiración sea válida y futura
  // - El CVV tenga la longitud correcta (3-4 dígitos)
  // - El nombre del tarjetahabiente esté completo
  if (data.is_valid) {
    // Formulario válido
  }
});

transaction-start

Se ejecuta cuando inicia el proceso de pago.

Hotelpay.Checkout.on("transaction-start", () => {
  // Buen momento para mostrar un indicador de carga
});

toggle-split-payment

Se ejecuta cuando se activa o desactiva el pago dividido.

Hotelpay.Checkout.on("toggle-split-payment", (data) => {
  // data.enabled indica si el pago dividido está activo
});

require-installments

Se ejecuta cuando la tarjeta es elegible para meses sin intereses.

Hotelpay.Checkout.on("require-installments", (data) => {
  // data.supported_installments contiene los plazos disponibles
  // Ejemplo: [{ months: 3, minimum: 1000 }, { months: 6, minimum: 2000 }]
});

paymentCompleted

Se ejecuta cuando el pago se completa exitosamente.

Hotelpay.Checkout.on("paymentCompleted", () => {
  // Pago exitoso
});

paymentError

Se ejecuta cuando ocurre un error durante el pago.

Hotelpay.Checkout.on("paymentError", (error) => {
  // error contiene detalles sobre el problema
  // error.message puede contener un mensaje en español o inglés
  // según el idioma configurado
});

Eventos

El SDK emite varios eventos que puedes escuchar para manejar diferentes estados del proceso de pago:

complete-form-card

Emitido cuando se completa el formulario de tarjeta. Recibe un objeto con la propiedad is_valid que indica si los datos son válidos.

Checkout.on('complete-form-card', (data) => {
  if (data.is_valid) {
    // Habilitar botón de pago
  }
});

transaction-start

Emitido cuando inicia el proceso de transacción.

Checkout.on('transaction-start', () => {
  // Deshabilitar botón de pago y mostrar estado de procesamiento
});

toggle-split-payment

Emitido cuando se activa o desactiva el pago dividido. Recibe un objeto con la propiedad enabled que indica si está activado.

Checkout.on('toggle-split-payment', (data) => {
  console.log('Split payment enabled:', data.enabled);
});

require-installments

Emitido cuando se requiere seleccionar meses sin intereses.

Checkout.on('require-installments', (data) => {
  // Mostrar selector de meses sin intereses
});

3ds-start

Emitido cuando se requiere autenticación 3D Secure para una transacción.

Checkout.on('3ds-start', (data) => {
  // Manejar inicio de autenticación 3DS
  // Se mostrará el desafío 3DS al usuario
});

3ds-end

Emitido cuando finaliza el proceso de autenticación 3D Secure.

Checkout.on('3ds-end', (data) => {
  // Manejar finalización de autenticación 3DS
  // La transacción continuará su proceso normal
});

adding-card-start

Emitido cuando comienza el proceso de agregar una tarjeta.

Checkout.on('adding-card-start', () => {
  // Mostrar indicador de carga
  // Deshabilitar botones de interacción
});

adding-card-completed

Emitido cuando se completa el proceso de agregar una tarjeta.

Checkout.on('adding-card-completed', (data) => {
  // La tarjeta se agregó exitosamente
  // Habilitar siguiente paso del proceso
});

transaction-completed

Emitido cuando una transacción se completa exitosamente.

Checkout.on('transaction-completed', (data) => {
  // La transacción fue exitosa
  // data contiene información de la transacción
});

transaction-failed

Emitido cuando una transacción falla por cualquier motivo.

Checkout.on('transaction-failed', (error) => {
  // La transacción falló
  // error contiene detalles del error
});

Personalización

setCSS(css: string)

Permite personalizar los estilos del iframe de pago. El CSS se aplicará solo dentro del iframe.

// Ejemplo de personalización de estilos
Checkout.setCSS(`
  .card-field-wrapper {
    margin-bottom: 20px;
  }
  
  .card-field-error {
    color: #dc2626;
  }
  
  .amount-field-error {
    color: #dc2626;
  }
  
  .installments-field-error {
    color: #dc2626;
  }
  
  .remove-arrows::-webkit-outer-spin-button,
  .remove-arrows::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }
  
  .remove-arrows {
    -moz-appearance: textfield;
  }
`);

Nota: Los estilos solo se aplicarán cuando se use el modo iframe. En modo overlay se mantienen los estilos por defecto.

Flujo de Meses Sin Intereses (MSI)

El SDK soporta pagos a meses sin intereses (MSI) cuando la tarjeta y el comercio lo permiten. El flujo es el siguiente:

  1. El usuario ingresa los datos de la tarjeta
  2. Cuando el formulario está completo y válido, se habilita el botón de pago
  3. Al hacer clic en el botón de pago, se inicia processPayment()
  4. Si la tarjeta soporta MSI, el SDK emite el evento require-installments con los plazos disponibles
  5. El iframe se ajusta automáticamente para mostrar el selector de plazos
  6. El usuario selecciona un plazo y confirma
  7. La transacción continúa su proceso normal

Ejemplo de implementación de MSI

const { Checkout } = Hotelpay;
const payButton = document.getElementById('payment-button');

// Escuchar validación del formulario
Checkout.on('complete-form-card', (data) => {
    payButton.disabled = !data.is_valid;
});

// Escuchar cuando se requiere selección de MSI
Checkout.on('require-installments', (data) => {
    console.log('Plazos disponibles:', data.supported_installments);
    // El SDK muestra automáticamente el selector de plazos
    // No se requiere acción adicional
});

// Escuchar inicio de transacción
Checkout.on('transaction-start', () => {
    payButton.disabled = true;
    payButton.textContent = 'Procesando...';
});

// Escuchar transacción completada
Checkout.on('transaction-completed', (data) => {
    payButton.textContent = '¡Pago Exitoso!';
    console.log('Transacción completada:', data);
});

// Manejar clic en botón de pago
payButton.addEventListener('click', async () => {
    try {
        await Checkout.processPayment();
    } catch (error) {
        console.error('Error al procesar el pago:', error);
        payButton.disabled = false;
        payButton.textContent = 'Reintentar Pago';
    }
});

El evento require-installments proporciona un objeto data con la siguiente estructura:

{
    supported_installments: Array<{
        months: number;    // Número de meses (ej: 3, 6, 12, etc)
        minimum: number;   // Monto mínimo requerido para este plazo
    }>
}

Nota: El selector de plazos solo se muestra si la tarjeta y el comercio soportan MSI y el monto de la transacción cumple con el mínimo requerido.

Flujo de Pago Combinado

El SDK permite realizar pagos utilizando dos tarjetas diferentes cuando el comercio tiene habilitada esta funcionalidad. El flujo es el siguiente:

  1. Si el comercio soporta pago combinado y el monto es mayor al mínimo requerido, se muestra un toggle dentro del iframe
  2. Al activar el toggle:
    • Se habilita la captura de una segunda tarjeta
    • Se muestra un selector de montos para distribuir el pago entre ambas tarjetas
    • Se agrega una comisión por pago combinado (si está configurada)
  3. El usuario puede:
    • Ingresar los datos de ambas tarjetas
    • Ajustar los montos a pagar con cada tarjeta
    • El total debe coincidir con el monto de la transacción más la comisión
  4. El flujo de MSI aplica independientemente para cada tarjeta:
    • Cada tarjeta puede tener sus propios plazos disponibles
    • El usuario puede seleccionar plazos diferentes para cada tarjeta

Ejemplo de implementación de pago combinado

const { Checkout } = Hotelpay;
const payButton = document.getElementById('payment-button');

// Escuchar cambios en el toggle de pago combinado
Checkout.on('toggle-split-payment', (data) => {
    console.log('Pago combinado activado:', data.enabled);
    // El SDK maneja automáticamente la UI para pago combinado
});

// Escuchar validación del formulario
Checkout.on('complete-form-card', (data) => {
    payButton.disabled = !data.is_valid;
});

// Escuchar cuando se requiere selección de MSI (puede ocurrir para ambas tarjetas)
Checkout.on('require-installments', (data) => {
    console.log('Plazos disponibles:', data.supported_installments);
    // El SDK muestra automáticamente el selector de plazos
});

// Escuchar inicio de transacción
Checkout.on('transaction-start', () => {
    payButton.disabled = true;
    payButton.textContent = 'Procesando...';
});

// Escuchar transacción completada
Checkout.on('transaction-completed', (data) => {
    payButton.textContent = '¡Pago Exitoso!';
    console.log('Transacción completada:', data);
});

// Manejar clic en botón de pago
payButton.addEventListener('click', async () => {
    try {
        await Checkout.processPayment();
    } catch (error) {
        console.error('Error al procesar el pago:', error);
        payButton.disabled = false;
        payButton.textContent = 'Reintentar Pago';
    }
});

El evento toggle-split-payment proporciona un objeto data con la siguiente estructura:

{
    enabled: boolean;    // true cuando se activa el pago combinado
}

Nota: El pago combinado solo estará disponible si:

  • El comercio tiene habilitada la funcionalidad
  • El monto de la transacción es mayor al mínimo requerido
  • La comisión por pago combinado está configurada (opcional)

Integración con SweetAlert2

El SDK no incluye una interfaz visual para mensajes y alertas, pero se puede integrar fácilmente con librerías como SweetAlert2 para mejorar la experiencia del usuario.

Instalación de SweetAlert2

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

Ejemplo de Implementación

// Configuración de mensajes por idioma
const messages = {
  es: {
    processing: "Procesando...",
    pleaseWait: "Por favor, espere mientras procesamos su pago.",
    cardVerificationFailed: "La verificación de la tarjeta ha fallado. Intenta con otra tarjeta.",
    paymentFailed: "No pudimos procesar el pago",
    paymentSuccess: "Pago completado exitosamente"
  },
  en: {
    processing: "Processing...",
    pleaseWait: "Please wait while we process your payment.",
    cardVerificationFailed: "Card verification failed. Please try another card.",
    paymentFailed: "We couldn't process the payment",
    paymentSuccess: "Payment completed successfully"
  }
};

// Función para mostrar el loader
const showProcessingModal = (text) => {
  Swal.fire({
    title: messages[currentLanguage].processing,
    html: `<p>${text}</p>`,
    showConfirmButton: false,
    allowOutsideClick: false
  });
};

// Función para mostrar error
const showErrorModal = (text) => {
  Swal.fire({
    icon: 'error',
    title: messages[currentLanguage].paymentFailed,
    html: `<p>${text}</p>`,
    showConfirmButton: true
  });
};

// Implementación con el SDK
Hotelpay.Checkout.on("transaction-start", () => {
  showProcessingModal(messages[currentLanguage].pleaseWait);
});

Hotelpay.Checkout.on("paymentCompleted", () => {
  Swal.fire({
    icon: 'success',
    title: messages[currentLanguage].paymentSuccess
  });
});

Hotelpay.Checkout.on("paymentError", (error) => {
  showErrorModal(error.message[currentLanguage]);
});

Este ejemplo muestra cómo integrar SweetAlert2 para:

  • Mostrar un loader durante el procesamiento
  • Mostrar mensajes de éxito
  • Mostrar mensajes de error
  • Manejar múltiples idiomas

La integración con SweetAlert2 mejora significativamente la experiencia del usuario al proporcionar retroalimentación visual clara durante todo el proceso de pago.