Para cualquier consulta estamos a un clic

Hacer una pregunta

Web SDK Payment

Descripción

Biblioteca JavaScript para mostrar el formulario de pago en la página del comerciante. Este método es adecuado para comerciantes tanto con alto como con bajo nivel de cumplimiento de los requisitos PCI DCC.

Escenario de trabajo:

  1. El comerciante registra el pedido a través de REST API en la pasarela de pagos
  2. El mdOrder obtenido (número de pedido) el comerciante lo transmite a la página donde se utiliza esta biblioteca js

Web SDK proporciona la posibilidad de agregar a su página de pago campos de entrada de datos de pago a través de iframe directamente desde la pasarela de pagos. La seguridad de la transmisión de datos se garantiza mediante el cifrado del protocolo HTTPS.

Ventajas:

Desventajas:

La biblioteca ayuda en la recopilación de datos de la tarjeta, su validación y verificación, realización del pago y redirección automática del comprador a la página final a través del returnUrl especificado en la configuración.

Cómo usar

Conectar script

Entorno de prueba

<script src="https://dev.bpcbt.com/payment/modules/multiframe/main.js"></script>

Entorno de producción

<script src="https://dev.bpcbt.com/payment/modules/multiframe/main.js"></script>

Preparación

En primer lugar, es necesario crear un formulario HTML para recibir pagos. El formulario debe contener bloques #pan, #expiry, #cvc y botón #pay. No es obligatorio usar exactamente estos nombres de campos. Podrás configurar los nombres que necesites durante la inicialización.

Ejemplo de formulario HTML que no soporta enlaces:

<div class="card-body">
    <div class="col-12">
        <label for="pan" class="form-label">Card number</label>
        <!-- Container for card number field -->
        <div id="pan" class="form-control"></div>
    </div>
    <div class="col-6 col-expiry">
        <label for="expiry" class="form-label">Expiry</label>
        <!-- Container for expiry card field -->
        <div id="expiry" class="form-control"></div>
    </div>
    <div class="col-6 col-cvc">
        <label for="cvc" class="form-label">CVC / CVV</label>
        <!-- Container for CVC/CVV field -->
        <div id="cvc" class="form-control"></div>
    </div>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
    <!-- Payment loader -->
    <span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
    <span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>

Si usas enlaces, necesitas incluir un bloque HTML adicional: #select-binding.

Aquí tienes un ejemplo de formulario HTML que soporta enlaces:

<div class="card-body">
    <div class="col-12" id="select-binding-container" style="display: none">
        <!-- Select for bindings -->
        <select class="form-select" id="select-binding" aria-label="Default select example">
            <option selected value="new_card">Pay with a new card</option>
        </select>
    </div>
    <div class="col-12">
        <label for="pan" class="form-label">Card number</label>
        <!-- Container for card number field -->
        <div id="pan" class="form-control"></div>
    </div>
    <div class="col-6 col-expiry">
        <label for="expiry" class="form-label">Expiry</label>
        <!-- Container for expiry card field -->
        <div id="expiry" class="form-control"></div>
    </div>
    <div class="col-6 col-cvc">
        <label for="cvc" class="form-label">CVC / CVV</label>
        <!-- Container for cvc/cvv field -->
        <div id="cvc" class="form-control"></div>
    </div>
    <label class="col-12" id="save-card-container">
        <!-- Save card checkbox -->
        <input class="form-check-input" type="checkbox" value="" id="save-card" />
        Save card
    </label>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
    <!-- Payment loader -->
    <span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
    <span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>

Puedes agregar al formulario cualquier campo adicional, como Cardholder name (nombre del titular de la tarjeta), Email (correo electrónico), Phone (número de teléfono), etc. Sin embargo, no olvides pasarlos posteriormente al método doPayment().

Inicialización Web SDK

Descripción del formulario de pago

Necesitas ejecutar la función constructor new window.PaymentForm().

window.PaymentForm() puede recibir las siguientes propiedades:

Propiedades de inicialización PaymentForm

mdOrder string required
Identificador del pedido

Descripción de campos en el formulario

apiContext string optional
Contexto (parte de la URL de la pasarela de pago después del dominio) para solicitudes API.
Por defecto, apiContext se toma automáticamente del enlace utilizado para conectar el script modules/multiframe/main.js.

language string optional
Idioma utilizado para la localización de errores y marcadores de posición.
Valor por defecto: en.

autofocus boolean optional
Cambio automático de enfoque al completar campos.
Valor por defecto - true

shouldMaskPan boolean optional
Enmascarar el campo de entrada Pan.
Por defecto false

shouldMaskExpiry boolean optional
Enmascarar el campo de entrada Expiry.
Por defecto false

shouldMaskCvc boolean optional
Enmascarar el campo de entrada CVC.
Por defecto true

showPanIcon boolean optional
Mostrar icono del sistema de pago.
Por defecto true

panIconStyle CSSStyleDeclaration optional
Estilos personalizados para el icono del sistema de pago

Estilos generales para campos de entrada

Estilos personalizados para cada uno de los campos de entrada (pan, expiry, cvc)

containerClassName string optional
Nombre de clase establecido adicionalmente para el contenedor.
Valor por defecto: field-container

onFormValidate (result: boolean) => void optional
Callback para manejar el cambio de validación del formulario.
Por ejemplo:
onFormValidate: (isValid) => {
    alert(isValid ? 'Congratulations!' : 'Oops! We regret.'); 
}

Ejemplo de inicialización Web SDK

const webSdkPaymentForm = new window.PaymentForm({
  // Número de pedido (el registro del pedido ocurre antes de la inicialización del formulario)
  mdOrder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  // Nombre de clase que será establecido para los contenedores con campos de entrada
  containerClassName: "field-container",
  onFormValidate: (isValid) => {
    // Manejo de validación del formulario
  },
  // Contexto para solicitudes API
  apiContext: "/payment",
  // Idioma - utilizado para localización de errores y nombres de placeholders.
  // El idioma debe estar soportado en la configuración del Comerciante
  language: "en",
  // Cambiar automáticamente el foco al completar los campos
  autoFocus: true,
  // Mostrar icono del sistema de pago
  showPanIcon: true,
  // Estilos personalizados para el icono del sistema de pago
  panIconStyle: {
    height: "16px",
    top: "calc(50% - 8px)",
    right: "8px",
  },
  fields: {
    pan: {
      container: document.querySelector("#pan"),
      onFocus: (containerElement) => {
        // Acción al recibir foco el campo
        // (containerElement contiene referencia al elemento contenedor del campo)
      },
      onBlur: (containerElement) => {
        // Acción al perder foco el campo
        // (containerElement contiene referencia al elemento contenedor del campo)
      },
      onValidate: (isValid, containerElement) => {
        // Acción al validar el campo
        // (isValid es igual a true si el campo es válido, sino false)
        // (containerElement contiene referencia al elemento contenedor del campo)
      },
    },
    expiry: {
      container: document.querySelector("#expiry"),
      // ...
    },
    cvc: {
      container: document.querySelector("#cvc"),
      // ...
    },
  },
  // Estilos para campos de entrada
  styles: {
    // Estado base
    base: {
      color: "black",
      padding: '0px 16px',
      fontSize: '18px',
      fontFamily: 'monospace',
    },
    // Estado con foco
    focus: {
      color: "blue",
    },
    // Estado deshabilitado
    disabled: {
      color: "gray",
    },
    // Con valor válido
    valid: {
      color: "green",
    },
    // Con valor inválido
    invalid: {
      color: "red",
    },
    // Estilo para placeholder
    placeholder: {
      // Estilo base
      base: {
        color: "gray",
      },
      // Estilo con foco
      focus: {
        color: "transparent",
      },
    },
  },
});

Método destroy

El método destroy() en Web SDK se utiliza para eliminar todos los recursos y listeners de eventos asociados con una instancia específica de Web SDK. Cuando invocas el método destroy(), limpia todos los listeners de eventos y contenedores de campos de entrada que fueron creados por Web SDK durante su ciclo de vida. Esto es útil cuando ya no necesitas la instancia de Web SDK.

El método destroy() usualmente realiza las siguientes tareas:

  1. Elimina todos los listeners de eventos que fueron agregados a la instancia de Web SDK.
  2. Limpia todos los contenedores de campos de entrada creados.

Ejemplo del método destroy

document.querySelector("#destroy").addEventListener("click", function () {
  webSdkPaymentForm.destroy();
});

Estilización

La estilización de los contenedores a los que se pasan los campos de entrada se define de forma independiente según el diseño de su página. Los diferentes estados de los contenedores de campos de entrada se pueden estilizar utilizando las siguientes clases CSS:

El parámetro className se establece durante la inicialización a través del parámetro containerClassName en las propiedades de window.PaymentForm().

Ejemplo:

<style>

      .field-container {
        width: 100%;
        height: 50px;
        padding: 0;
      }

      .field-container--focus {
        border-color: #86b7fe;
        outline: 0;
        box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
      }

      .field-container--valid {
        border-color: #198754;
      }
      .field-container--valid.field-container--focus {
        box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
      }

      .field-container--invalid {
        border-color: #dc3545;
      }
      .field-container--invalid.field-container--focus {
        box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
      }

  </style>
  <script>
      //...
        const paymentForm = new window.PaymentForm({
          //...
          containerClassName: 'field-container',
          //...

  </script>

Fuentes

En Web SDK se pueden usar fuentes del sistema/preinstaladas en los dispositivos. La fuente se establece durante la inicialización del Web SDK en la propiedad styles o customStyles. Por ejemplo en styles.base.fontFamily.

Estilización de campos de entrada

Puede personalizar la apariencia de los campos de entrada. Para esto

Ejemplo:

const webSdkPaymentForm = new window.PaymentForm({
      // ...
      // estilos por defecto para todos los campos de entrada
      styles: {
        base: {
          color: 'black',
          padding: '0px 16px',
          fontSize: '18px',
          fontFamily: 'Arial, sans-serif',
        },
        // ...
        invalid: {
          color: 'red',
        },
        // ...
      },
      // estilos personalizados para el campo de entrada del número de tarjeta
      customStyles: {
        pan: {
          base: {
            color: 'blue',
            padding: '0px 24px',
            fontSize: '22px',
          },
          invalid: {
              color: 'orange',
          },
        },
      },
    });

Configuraciones adicionales

Configure parámetros adicionales durante la inicialización, como idioma del placeholder, iconos de sistemas de pago, enmascaramiento de campos de entrada, etc. La lista completa de parámetros está disponible en Propiedades de inicialización PaymentForm.

Validación

WebSDK proporciona verificación, validación y protección solo de los campos de entrada principales requeridos para realizar el pago: Pan, Expiry, CVC.

Todos los demás campos adicionales, como Cardholder name, Phone, Email, campos que aseguran el cumplimiento de los requisitos del mandato Visa Secure Data etc., el comerciante debe validarlos independientemente de su lado.

Ejemplos de expresiones regulares para la validación de campos adicionales:

Cardholder name:

// regex: ^[A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]* [A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]*$

export function validateCardholderName({
    errorMessage = 'Invalid cardholder'
} = {}) {
    return (value) => {
        const regex = /^[A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]* [A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]*$/;
        return regex.test(String(value).trim()) ? null : errorMessage;
    };
}

Email:

// regex: ^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$

export function validateEmail({
    errorMessage = 'Invalid email'
} = {}) {
    return (value) => {
        const regex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
        return regex.test(String(value).toLowerCase()) ? null : errorMessage;
    };
}

Phone:

// regex: ^\+\d{7,15}$

export function validatePhone({
    errorMessage = 'Invalid phone number'
} = {}) {
    return (value) => {
        const regex = /^\+\d{7,15}$/;
        return regex.test(String(value).trim()) ? null : errorMessage;
    };
}

Pago sin tarjeta guardada (vinculación)

Paso 1. Ejecutar método de inicialización

Después de definir los parámetros del Web SDK es necesario llamar init(). Esta función devuelve callback, donde puede, por ejemplo, ocultar el cargador o hacer algo más. init() devuelve c.

Por ejemplo:

webSdkFormWithoutBindings
  .init()
  .then((success) => {
    console.log('success', success)
    // Script inicializado exitosamente. Promise devuelve objeto que contiene información útil sobre la orden registrada 
    // en la pasarela de pagos. Después de esto se puede quitar el loader o ejecutar otras acciones:
    document
      .querySelector(".payment-form-loader")
      .classList.remove("payment-form-loader--active");
  })
  .catch((error) => {
    // Durante la inicialización del script surgieron errores. La ejecución posterior es imposible.
    // Promise devuelve mensaje de error que podemos mostrar en la página:
    const errorEl = document.querySelector('#error_1');
    errorEl.innerHTML = e.message;
    errorEl.classList.remove('visually-hidden');
  })
  .finally(() => {
    // Acciones ejecutadas después de la inicialización, independientemente de su ejecución exitosa o no exitosa. 
  });

Paso 2. Manejo del clic del botón de pago. Ejecutar método de pago

Para ejecutar el pago, llame la función doPayment(). No es necesario enviar datos de tarjeta, esto lo hará Web SDK. doPayment() devuelve Promise.

El método acepta los siguientes parámetros:

phone String optional
Número de teléfono del cliente

email String optional
Email del cliente

cardholderName String optional
Nombre del portador de la tarjeta.

jsonParams Object optional
Campos adicionales. Por ejemplo, puede enviar información adicional sobre la orden o cualquier otra información útil para usted. Por ejemplo, jsonParams: { "t-shirt-color": "negro", "size": "M" }

Actualización del mandato Visa Secure Data Field

Tenga en cuenta los requisitos de IPS Visa con respecto a los campos de datos adicionales necesarios para las solicitudes de autenticación EMV 3DS. Los comerciantes deben proporcionar datos completos y precisos sobre las transacciones en sus solicitudes de autenticación. Los comerciantes también deben garantizar que la URL del método 3DS realice la recopilación de datos del dispositivo para apoyar la autenticación exitosa en caso de que la URL del método 3DS sea proporcionada por el emisor.

Por lo tanto, la recopilación de campos adicionales para VISA es responsabilidad del comerciante. Puede familiarizarse con el texto completo de los requisitos en Visa Secure Data Field Mandate.

Los campos adicionales se pasan como propiedades del objeto, que es argumento de la función doPayment().

Ejemplo de llamada:

webSdkFormWithoutBindings
  .doPayment({
    // Parámetros adicionales
    email: "foo@bar.com",
    phone: "4420123456789",
    cardholderName: "JOHN DOE",
    jsonParams: { foo: "bar" },
  })
  .then((result) => {
    console.log("result", result);
  })
  .catch((e) => {
    // Manejo de errores. Para ejemplo mostraremos bloque con error
    errorEl.innerHTML = e.message;
    errorEl.classList.remove("visually-hidden");
  })
  .finally(() => {
    // Se ejecuta en cualquier caso. Por ejemplo, hacer el botón "Pagar" nuevamente activo.
    payButton.disabled = false;
    spinnerEl.classList.add("visually-hidden");
  });

Demostración sin tarjeta guardada

Para el funcionamiento del Web SDK Payment requiere Order ID (identificador del pedido registrado en la pasarela de pagos).
Para fines de trabajo - registre el pedido a través de API.

Este formulario se utiliza solo con fines de demostración - use el valor Order ID = xxxxx-xxxxx-xxxxx-xxxxx

Todo el código de demostración

<div class="container_demo">
    <div class="about">
        <form name="formRunTest">
            <label for="mdOrder"> Order ID (mdOrder) <br>
              <span class="label__desc">(Debe llegar desde el backend. Esta entrada está destinada solo para demostración)</span>
            </label>
            <div class="run-test">
                <input id="mdOrder" type="text" placeholder="Paste the mdOrder registered for sandbox"/>
                <button class="btn-mini" id="load" type="submit">Load</button>
            </div>
        </form>
    </div>
    <div class="payment-form">

        <div class="payment-form-loader payment-form-loader--active">
            Web SDK Payment requiere la presencia de mdOrder (pedido previamente registrado en la pasarela).<br>
            Se puede registrar el pedido a través de Merchant Portal o a través de API. <br><br>
            O intente usar <code>xxxxx-xxxxx-xxxxx-xxxxx</code> si desea obtener solo el formulario de pago.
        </div>

        <div class="card-body">
            <div class="col-12">
                <label for="pan" class="form-label">Número de tarjeta</label>
                <div id="pan" class="form-control"></div>
            </div>
            <div class="col-6 col-expiry">
                <label for="expiry" class="form-label">Fecha de vencimiento</label>
                <div id="expiry" class="form-control"></div>
            </div>
            <div class="col-6 col-cvc">
                <label for="cvc" class="form-label">CVC / CVV</label>
                <div id="cvc" class="form-control"></div>
            </div>
            <!-- Campos adicionales para Visa Mandatory -->
            <div class="col-12 additional-field" style="display:none;">
              <label for="" class="form-label">Cardholder</label>
              <div id="cardholder" class="additional-field-container">
                  <input type="text" class="additional-field-input" placeholder="Surname"value="JOHN DOE">
              </div>
            </div>
            <div class="col-12 additional-field"  style="display:none;">
                <label for="" class="form-label">Mobile phone</label>
                <div id="mobile" class="additional-field-container">
                    <input type="tel" class="additional-field-input" placeholder="+4915112345" value="+4915112345678">
                </div>
            </div>
            <div class="col-12 additional-field" style="display:none;">
                <label for="" class="form-label">Email address</label>
                <div id="email" class="additional-field-container">
                    <input type="text" class="additional-field-input" placeholder="address@mail" value="address@mail.com">
                </div>
            </div>
        </div>
        <button class="btn btn-primary btn-lg" type="submit" id="pay">
            <span
            class="spinner-border spinner-border-sm me-2 visually-hidden"
            role="status"
            aria-hidden="true"
            id="pay-spinner">
            </span>
            <span>Pagar</span>
        </button>
        <!-- Estos botones son necesarios solo para demostrar el funcionamiento del método destroy -->
        <button class="btn btn-secondary" type="button" id="destroyFormWithoutCredentials">
            <span>Destruir</span>
        </button>
        <button class="btn btn-secondary" type="button" id="reinitFormWithoutCredentials" style="display: none;">
            <span>Inicializar</span>
        </button>
        <div class="error my-2 text-center text-danger visually-hidden" id="error"></div>
    </div>
</div>

<script>
document.addEventListener("DOMContentLoaded", () => {
  // Función para inicialización del formulario de pago con posibilidad de reutilización después de destrucción
  function initPaymentForm() {
    const mrOrderInput = document.getElementById("mdOrder");
    mrOrderInput.classList.remove("invalid");
    if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
      mrOrderInput.classList.add("invalid");
      return;
    }

    // Inicialización de Web SDK. Se necesita identificador de pedido mdOrder.
    initPayment(mrOrderInput.value);
  }

  document.formRunTest.addEventListener("submit", function (e) {
    e.preventDefault();
    // Inicialización del formulario de pago
    initPaymentForm();
  });

  let webSdkFormWithoutBindings;
  // Array de objetos para campos adicionales, que contienen id del campo, su plantilla de validación y símbolos permitidos para entrada
  const mandatoryFieldsWithoutBinding = [
    {
        id: '#cardholder',
        template: /^[a-zA-Z '`.\-]{4,24}$/,
        replace: /[^a-zA-Z ' \-`.]/g,
    },
    {
        id: '#mobile',
        template: /^\+?[1-9][0-9]{7,14}$/,
        replace: /[^0-9\+]/g,
    },
    {
        id: '#email',
        template: /^[a-zA-Z0-9._-]{1,64}@([a-zA-Z0-9.-]{2,255})\.[a-zA-Z]{2,255}$/,
        replace: /[^a-zA-Z0-9@._-]/g,
    }
  ]

  function initPayment(mdOrder) {
    webSdkFormWithoutBindings = new window.PaymentForm({
      mdOrder: mdOrder,
      onFormValidate: () => {},
      language: "en", // Idioma (inglés)
      containerClassName: "field-container",
      autoFocus: true,
      showPanIcon: true,
      panIconStyle: {
        height: "16px",
        top: "calc(50% - 8px)",
        right: "8px",
      },
      fields: {
        pan: {
          container: document.querySelector("#pan"),
        },
        expiry: {
          container: document.querySelector("#expiry"),
        },
        cvc: {
          container: document.querySelector("#cvc"),
        },
      },
      styles: {
        base: {
          padding: "0px 16px",
          color: "black",
          fontSize: "18px",
          fontFamily: 'monospace',
        },
        invalid: {
          color: "red",
        },
        placeholder: {
          base: {
            color: "gray",
          },
          focus: {
            color: "transparent",
          },
        },
      },
    });

    // Acción después de la inicialización
    webSdkFormWithoutBindings.init().then(() => {
      document
        .querySelector(".payment-form-loader")
        .classList.remove("payment-form-loader--active");
      })
      .catch((e) => {
        // Visualización de error durante la inicialización del webSDK
        const errorEl = document.querySelector('#error_1');
        errorEl.innerHTML = e.message;
        errorEl.classList.remove('visually-hidden');
      }
      .finally(() => {
        // Validación y reemplazo automático de caracteres no permitidos
        mandatoryFieldsWithBinding.forEach(item => {
          const field = document.querySelector(item.id)
          field.closest(".additional-field").style.display = '';
          field.addEventListener('input', () => {
              let inputValue = field.querySelector('input')
              inputValue.value = item.replace ? inputValue.value.replace(item.replace,'') : inputValue.value;
              if (item.id.includes("#cardholder")) {
                  inputValue.value = inputValue.value.toUpperCase()
              }
              if (item.template) {
                  // Clase CSS ".additional-field-invalid" para visualizar campos no válidos
                  if (item.template.test(inputValue.value)) {
                      field.classList.remove("additional-field-invalid")
                  } else {
                      field.classList.add("additional-field-invalid")
                  }
              }
          })
        })
      })
  }

  // Controlador "Pagar"
  document.querySelector("#pay").addEventListener("click", () => {
    const payButton = document.querySelector("#pay");

    // Hacemos el botón "Pagar" inactivo para evitar pagos dobles
    payButton.disabled = true;

    // Mostramos el cargador al usuario
    const spinnerEl = document.querySelector("#pay-spinner");
    spinnerEl.classList.remove("visually-hidden");

    // Ocultamos el contenedor de error
    const errorEl = document.querySelector("#error");
    errorEl.classList.add("visually-hidden");
    // Validación de campos adicionales Visa Mandatory
    if (document.querySelectorAll('.additional-field-invalid').length) {
      errorEl.innerHTML = "Form is not valid";
      errorEl.classList.remove('visually-hidden');
      spinnerEl.classList.add('visually-hidden');
      return
    }

    // Comenzamos el proceso de pago
    webSdkFormWithoutBindings
      .doPayment({
        // Parámetros adicionales
        email: document.querySelector('#email input').value,
        phone: document.querySelector('#mobile input').value,
        cardholderName: document.querySelector('#cardholder input').value,
        jsonParams: { size: "L" },
      })
      .then((result) => {
        console.log("result", result);
      })
      .catch((e) => {
        // Se ejecuta en caso de error
        errorEl.innerHTML = e.message;
        errorEl.classList.remove("visually-hidden");
      })
      .finally(() => {
        // Se ejecuta en cualquier caso, por ejemplo, hacemos que el botón "Pagar" esté activo nuevamente.
        payButton.disabled = false;
        spinnerEl.classList.add("visually-hidden");
      });
  });

  // Manejador "Destruir"
  document
    .querySelector("#destroyFormWithoutCredentials")
    .addEventListener("click", function () {
      // Eliminamos el botón "Destruir" y mostramos el botón "Inicializar" para demostración
      this.style.display = "none";
      document.querySelector("#reinitFormWithoutCredentials").style.display =
        "";
      // Destruimos el formulario de pago Web SDK
      webSdkFormWithoutBindings.destroy();
    });

  // Manejador "Inicializar formulario"
  document
    .querySelector("#reinitFormWithoutCredentials")
    .addEventListener("click", function () {
      // Eliminamos el botón "Inicializar" y mostramos el botón "Destruir" para demostración
      this.style.display = "none";
      document.querySelector("#destroyFormWithoutCredentials").style.display =
        "";
      // Inicialización del formulario de pago
      initPaymentForm();
    });
});
</script>

Pago con tarjeta guardada (enlace)

Paso 1. Ejecutar el método de inicialización

Después de determinar los parámetros del Web SDK es necesario llamar a init(). Esta función devuelve un callback, donde puede, por ejemplo, ocultar el cargador o hacer cualquier otra cosa. init() devuelve una Promise.

Por ejemplo:

// Inicialización
webSdkFormWithBindings
  .init()
  .then(({ orderSession }) => {
    // El objeto `orderSession` contiene toda la información sobre el pedido, incluyendo información sobre las credenciales guardadas (sobre el enlace).
    console.info("orderSession", orderSession);

    // Mostrar elemento de selección de tarjeta guardada
    document.querySelector("#select-binding-container").style.display =
      orderSession.bindings.length ? "" : "none";

    // Rellenar la selección con credenciales guardadas
    orderSession.bindings.forEach((binding) => {
      document
        .querySelector("#select-binding")
        .options.add(new Option(binding.pan, binding.id));
    });

    // Manejo de selección de credenciales guardadas o nueva tarjeta
    document
      .querySelector("#select-binding")
      .addEventListener("change", function () {
        const bindingId = this.value;
        if (bindingId !== "new_card") {
          webSdkFormWithBindings.selectBinding(bindingId);

          // Ocultar casilla "Guardar tarjeta"
          document.querySelector("#save-card-container").style.display = "none";
        } else {
          // Selección de enlace con null significa transición a nueva tarjeta
          webSdkFormWithBindings.selectBinding(null);

          // Mostrar casilla "Guardar tarjeta"
          document.querySelector("#save-card-container").style.display = "";
        }
      });

    // Cuando el formulario está listo, podemos ocultar el cargador
    document.querySelector("#pay-form-loader").classList.add("visually-hidden");
  })
  .catch((error) => {
    // Ocurrieron errores durante la inicialización del script. No es posible continuar la ejecución.
    // Promise devuelve un mensaje de error, que podemos mostrar en la página:
    const errorEl = document.querySelector('#error_1');
    errorEl.innerHTML = e.message;
    errorEl.classList.remove('visually-hidden');
  });

Paso 2. Manejo del clic del botón de pago. Ejecutar método de pago

Para ejecutar el pago, llame a la función doPayment(). No es necesario enviar los datos de la tarjeta, esto lo hará el Web SDK. doPayment() devuelve una Promise.

El método acepta los siguientes parámetros:

phone String optional
Número de teléfono del cliente

saveCard boolean optional
Opción para guardar la tarjeta. Por ejemplo, document.querySelector('#save-card').checked

email String optional
Email del cliente

cardholderName String optional
Nombre del portador de la tarjeta.

jsonParams Object optional
Campos adicionales. Por ejemplo, puede enviar información adicional sobre el pedido o cualquier otra información útil para usted. Por ejemplo, jsonParams: { "t-shirt-color": "negro", "size": "M" }

Aplicación de tarjetas guardadas

Para pagar con credenciales guardadas, necesita pasar el bindingId seleccionado al formulario antes de llamar a doPayment:
webSdkFormWithBindings.selectBinding('bindingId');

Si cambió de opinión y quiere pagar con una nueva tarjeta, no olvide eliminar el bindingId del formulario:
webSdkFormWithBindings.selectBinding(null);

Ejemplo de llamada:

webSdkFormWithBindings
  .doPayment({
    // Parámetros adicionales
    email: "foo@bar.com",
    phone: "4420123456789",
    saveCard: document.querySelector("#save-card").checked,
    cardholderName: "JOHN DOE",
    jsonParams: { foo: "bar" },
  })
  .then((result) => {
    console.log("result", result);
  })
  .catch((e) => {
    // Manejo de errores. Para el ejemplo mostraremos un bloque con error
    errorEl.innerHTML = e.message;
    errorEl.classList.remove("visually-hidden");
  })
  .finally(() => {
    // Ejecutar en cualquier caso. Por ejemplo, hacer el botón "Pagar" activo nuevamente.
    payButton.disabled = false;
    spinnerEl.classList.add("visually-hidden");
  });

Demostración con tarjeta guardada

Para el funcionamiento de Web SDK Payment requiere Order ID (identificador de pedido registrado en la pasarela de pago).
Para fines de trabajo - registre el pedido a través de API.

Este formulario se usa solo para fines de demostración - use el valor Order ID = xxxxx-xxxxx-xxxxx-xxxxx

Todo el código de demostración

<div class="container_demo">
    <div class="about">
        <form name="formRunTest">
            <label for="mdOrder"> Identificador del pedido (mdOrder) <br/>
              <span class="label__desc">(Se proporciona solo para fines demostrativos. El identificador del pedido debe provenir del backend.)</span>
            </label>
            <div class="run-test">
                <input id="mdOrder" type="text" placeholder="Pegue mdOrder, registrado en Sandbox"/>
                <button class="btn-mini" id="load" type="submit">Cargar</button>
            </div>
        </form>
    </div>
    <div class="payment-form">

        <div class="payment-form-loader payment-form-loader--active">
 Para Web SDK Payment se requiere mdOrder (pedido previamente registrado en la pasarela).<br/>
 Puede registrar un pedido a través del panel de control personal o a través de API. <br/><br/>
 O intente usar <code>xxxxx-xxxxx-xxxxx-xxxxx</code> si solo quiere verificar el formulario de pago.
        </div>

        <div id="pay-form-loader" class="spinner-container visually-hidden">
            <div class="spinner-border" role="status"></div>
        </div>
        <div class="card-body">
            <div class="col-12" id="select-binding-container" style="display: none">
              <select class="form-select" id="select-binding" aria-label="Default select example">
                <option selected value="new_card">Pago con tarjeta nueva</option>
              </select>
            </div>
            <div class="col-12">
                <label for="pan" class="form-label">Número de tarjeta</label>
                <div id="pan" class="form-control"></div>
            </div>
            <div class="col-6 col-expiry">
                <label for="expiry" class="form-label">Fecha de vencimiento</label>
                <div id="expiry" class="form-control"></div>
            </div>
            <div class="col-6 col-cvc">
                <label for="cvc" class="form-label">CVC / CVV</label>
                <div id="cvc" class="form-control"></div>
            </div>
            <label class="col-12" id="save-card-container">
                <input class="form-check-input" type="checkbox" value="" id="save-card" />
                Guardar tarjeta
            </label>
            <!--Campos adicionales Visa Mandatory -->
            <div class="col-12 additional-field" style="display:none;">
              <label for="" class="form-label">Titular de la tarjeta</label>
              <div id="cardholder" class="additional-field-container">
                  <input type="text" class="additional-field-input" placeholder="NAME SURNAME"value="JOHN DOE">
              </div>
            </div>
            <div class="col-12 additional-field"  style="display:none;">
                <label for="" class="form-label">Teléfono</label>
                <div id="mobile" class="additional-field-container">
                    <input type="tel" class="additional-field-input" placeholder="+4915112345" value="+4915112345678">
                </div>
            </div>
            <div class="col-12 additional-field" style="display:none;">
                <label for="" class="form-label">Correo electrónico</label>
                <div id="email" class="additional-field-container">
                    <input type="text" class="additional-field-input" placeholder="address@mail" value="address@mail.com">
                </div>
            </div>
        </div>
        <button class="btn btn-primary btn-lg" type="submit" id="pay">
            <span
            class="spinner-border spinner-border-sm me-2 visually-hidden"
            role="status"
            aria-hidden="true"
            id="pay-spinner">
            </span>
            <span>Pagar</span>
        </button>
        <!-- Estos botones son necesarios solo para demostrar el funcionamiento del método destroy -->
        <button class="btn btn-secondary" type="button" id="destroyFormWithoutCredentials">
            <span>Destruir</span>
        </button>
        <button class="btn btn-secondary" type="button" id="reinitFormWithoutCredentials" style="display: none;">
            <span>Inicializar</span>
        </button>
        <div class="error my-2 visually-hidden" id="error"></div>
    </div>
</div>

<script>
document.addEventListener("DOMContentLoaded", () => {
  // Función para inicializar el formulario de pago para su reutilización después de la destrucción
  function initPaymentForm() {
    const mrOrderInput = document.getElementById("mdOrder");
    mrOrderInput.classList.remove("invalid");
    if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
      mrOrderInput.classList.add("invalid");
      return;
    }
    // Eliminar marcador de posición
    document
      .querySelector(".payment-form-loader")
      .classList.remove("payment-form-loader--active");
    // Agregar cargador de formularios de pago
    document
      .querySelector("#pay-form-loader")
      .classList.remove("visually-hidden");

    // Inicialización de Web SDK. Se requiere mdOrder obligatorio (identificador de pedido).
    initPayment(mrOrderInput.value);
  }

  // Inicialización del manejador para datos de prueba
  function handleSubmit(e) {
    e.preventDefault();
    // Inicializar formulario de pago
    initPaymentForm();
  }

  // Registro del evento para ejemplo de entrada
  document.formRunTest.addEventListener("submit", handleSubmit);

  let webSdkFormWithBindings;
  // Array de objetos para campos adicionales que contienen id del campo, su plantilla de validación y caracteres permitidos para entrada
  const mandatoryFieldsWithoutBinding = [
    {
        id: '#cardholder',
        template: /^[a-zA-Z '`.\-]{4,24}$/,
        replace: /[^a-zA-Z ' \-`.]/g,
    },
    {
        id: '#mobile',
        template: /^\+?[1-9][0-9]{7,14}$/,
        replace: /[^0-9\+]/g,
    },
    {
        id: '#email',
        template: /^[a-zA-Z0-9._-]{1,64}@([a-zA-Z0-9.-]{2,255})\.[a-zA-Z]{2,255}$/,
        replace: /[^a-zA-Z0-9@._-]/g,
    }
  ]

  function initPayment(mdOrder) {
    webSdkFormWithBindings = new window.PaymentForm({
      // Número de pedido (registro del pedido ocurre antes de la inicialización del formulario)
      mdOrder: mdOrder,
      // Manejo de validación del formulario
      onFormValidate: (isValid) => {
        // Por ejemplo, puedes deshabilitar los botones "Pagar" y "Obtener token" si el formulario no es válido, por ejemplo:
        // const payButton = document.querySelector('#pay');
        // payButton.disabled = !isValid;
      },
      // Contexto para llamadas API
      apiContext: "/payment",
      // El idioma se usa para localización de errores y nombres para marcadores de posición.
      // El idioma debe estar soportado en la configuración del comerciante
      language: "en",
      // Nombre de clase para elementos contenedores que contienen iframe
      containerClassName: "field-container",
      // Cambio automático de foco al llenar campos
      autoFocus: true,
      // Mostrar icono del sistema de pago
      showPanIcon: true,
      // Estilos adicionales para el icono del sistema de pago
      panIconStyle: {
        height: "16px",
        top: "calc(50% - 8px)",
        right: "8px",
      },
      // Configuración de campo
      fields: {
        // Elemento contenedor en el que se colocará el iframe con el campo
        pan: {
          container: document.querySelector("#pan"),
        },
        // Fecha de vencimiento de la tarjeta
        expiry: {
          container: document.querySelector("#expiry"),
        },
        // Código CVC/CVV
        cvc: {
          container: document.querySelector("#cvc"),
        },
      },
      // Estilos adicionales para personalizar la apariencia de los campos de entrada en iframes
      styles: {
        base: {
          padding: "0px 16px",
          color: "black",
          fontSize: "18px",
          fontFamily: 'monospace',
        },
        disabled: {
          backgroundColor: "#e9ecef",
        },
        invalid: {
          color: "red",
        },
        placeholder: {
          base: {
            color: "gray",
          },
          focus: {
            color: "transparent",
          },
        },
      },
    });

    // Acción después de la inicialización
    webSdkFormWithBindings
      .init()
      .then(({ orderSession }) => {
        // El objeto `orderSession` contiene toda la información sobre el pedido, incluyendo información sobre credenciales guardadas (sobre vinculación).
        console.info("orderSession", orderSession);

        // Mostrar vinculación seleccionada
        document.querySelector("#select-binding-container").style.display =
          orderSession.bindings.length ? "" : "none";

        // Rellenar selección con credenciales guardadas
        orderSession.bindings.forEach((binding) => {
          document
            .querySelector("#select-binding")
            .options.add(new Option(binding.pan, binding.id));
        });

        // Manejo de selección de credenciales guardadas o nueva tarjeta
        document
          .querySelector("#select-binding")
          .addEventListener("change", function () {
            const bindingId = this.value;
            if (bindingId !== "new_card") {
              // Establecer identificador de vinculación
              webSdkFormWithBindings.selectBinding(bindingId);

              // Hide the 'Save card' checkbox
              document.querySelector("#save-card-container").style.display =
                "none";
            } else {
              // Selección de vinculación con null significa transición a nueva tarjeta
              webSdkFormWithBindings.selectBinding(null);

              // Mostrar casilla "Guardar tarjeta"
              document.querySelector("#save-card-container").style.display = "";
            }
          });

        // Cuando el formulario esté listo, podemos ocultar el cargador
        document
          .querySelector("#pay-form-loader")
          .classList.add("visually-hidden");

        // Eliminar evento para ejemplo de entrada
        document.formRunTest.removeEventListener("submit", handleSubmit);
      })
      .catch((error) => {
        // Ejecutar en caso de error
        const errorEl = document.querySelector("#error");
        errorEl.innerHTML = e.message;
        errorEl.classList.remove("visually-hidden");
      })
      .finally(() => {
        // Validación y sustitución automática de caracteres no válidos
        mandatoryFieldsWithBinding.forEach(item => {
          const field = document.querySelector(item.id)
          field.closest(".additional-field").style.display = '';
          field.addEventListener('input', () => {
              let inputValue = field.querySelector('input')
              inputValue.value = item.replace ? inputValue.value.replace(item.replace,'') : inputValue.value;
              if (item.id.includes("#cardholder")) {
                  inputValue.value = inputValue.value.toUpperCase()
              }
              if (item.template) {
                  // Clase CSS ".additional-field-invalid" para mostrar campos no válidos
                  if (item.template.test(inputValue.value)) {
                      field.classList.remove("additional-field-invalid")
                  } else {
                      field.classList.add("additional-field-invalid")
                  }
              }
          })
        })
      });

  }

  // Manejador de pago
  document.querySelector("#pay").addEventListener("click", () => {
    // Hacer inactivo el botón "Pagar" para evitar pagos dobles
    const payButton = document.querySelector("#pay");
    payButton.disabled = true;

    // Mostrar cargador para el usuario
    const spinnerEl = document.querySelector("#pay-spinner");
    spinnerEl.classList.remove("visually-hidden");

    // Ocultar contenedor de errores
    const errorEl = document.querySelector("#error");
    errorEl.classList.add("visually-hidden");
    // Validación de campos adicionales Visa Mandatory
    if (document.querySelectorAll('.additional-field-invalid').length) {
      errorEl.innerHTML = "Form is not valid";
      errorEl.classList.remove('visually-hidden');
      spinnerEl.classList.add('visually-hidden');
      return
    }

    // Iniciar pago
    webSdkFormWithBindings
      .doPayment({
        // Parámetros adicionales
        email: document.querySelector('#email input').value,
        phone: document.querySelector('#mobile input').value,
        cardholderName: document.querySelector('#cardholder input').value,
        saveCard: document.querySelector("#save-card").checked,
        jsonParams: { foo: "bar" },
      })
      .then((result) => {
        // Aquí se puede hacer algo con el resultado del pago
        console.log("result", result);
      })
      .catch((e) => {
        // Ejecutar en caso de error
        errorEl.innerHTML = e.message;
        errorEl.classList.remove("visually-hidden");
      })
      .finally(() => {
        // Ejecutar en cualquier caso. Por ejemplo, activar de nuevo el botón «Pagar».
        payButton.disabled = false;
        spinnerEl.classList.add("visually-hidden");
      });
  });

  // manejador Destroy
  document
    .querySelector("#destroyFormWithCredentials")
    .addEventListener("click", function () {
      // Eliminar el botón Destroy y mostrar el botón de reinicialización para demostración
      this.style.display = "none";
      document.querySelector("#reinitFormWithCredentials").style.display = "";
      // Ejecutar el método destroy en el formulario web SDK
      webSdkFormWithBindings.destroy();
    });

  // Inicializar el manejador del formulario de pago
  document
    .querySelector("#reinitFormWithCredentials")
    .addEventListener("click", function () {
      // Eliminar el botón Reinit y mostrar el botón Destroy para demostración
      this.style.display = "none";
      document.querySelector("#destroyFormWithCredentials").style.display = "";
      // Inicialización del formulario de pago
      initPaymentForm();
    });
});
</script>

Procesamiento de datos devueltos por los métodos init( ) y doPayment( )

Método init( )

El método devuelve un Promise, que al ejecutarse exitosamente devuelve un objeto con información sobre el pedido registrado. Por razones de seguridad, este objeto no contiene datos de tarjeta y otra información confidencial.

El método devuelve los siguientes parámetros:

mdOrder string optional
Número de pedido, transmitido durante la inicialización

Objeto con información sobre el pedido registrado

Es posible la presencia de campos adicionales, o la ausencia de algunos campos de la lista anterior.

Ejemplo

{
  "mdOrder": "5541f44c-d7ec-7a6c-997d-1d4d0007bc7d",
  "orderSession": {
      "amount": "100000",
      "currencyAlphaCode": "BYN",
      "currencyNumericCode": "933",
      "sessionTimeOverAt": 1740385187287,
      "orderNumber": "27000",
      "description": "",
      "cvcNotRequired": false,
      "bindingEnabled": false,
      "bindingDeactivationEnabled": false,
      "merchantOptions": [
          "MASTERCARD_TDS",
          "MASTERCARD",
          "VISA",
          "VISA_TDS",
          "CARD"
      ],
      "customerDetails": {},
      "merchantInfo": {
          "merchantUrl": "http://google.com",
          "merchantFullName": "Coffee to Go",
          "merchantLogin": "CoffeToGo",
          "captchaMode": "NONE",
          "loadedResources": {
              "logo": true,
              "footer": false
          },
          "custom": false
      },
      "bindings": [
            {
                "cardholderName": "CARDHOLDER NAME",
                "createdAt": 1712321609666,
                "id": "83ffea5d-061f-7eca-912a-02ff0007bc7d",
                "pan": "4111 11** **** 1111",
                "expiry": "12/24",
                "cardInfo": {
                    "name": "TEST BANK-A",
                    "nameEn": "TEST BANK-A",
                    "backgroundColor": "#fbf0ff",
                    "backgroundGradient": [
                        "#fafafa",
                        "#f3f0ff"
                    ],
                    "supportedInvertTheme": false,
                    "backgroundLightness": true,
                    "country": "hu",
                    "defaultLanguage": "en",
                    "textColor": "#040e5d",
                    "url": null,
                    "logo": "logo/main/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
                    "logoInvert": "logo/invert/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
                    "logoMini": "logo/mini/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
                    "design": null,
                    "paymentSystem": "visa",
                    "cobrand": null,
                    "productCategory": null,
                    "productCode": null,
                    "mnemonic": "TEST BANK-A",
                    "params": null
                }
            }
      ]
  }
}

Ejecución no exitosa

En caso de ejecución no exitosa, Promise devuelve un mensaje de error, que podemos mostrar en la página. Ejemplo de mensaje: Error: Formulario inválido.

Ejemplos de código de procesamiento de datos devueltos por el método de inicialización init() se presentan en la sección Paso 1. Ejecutar método de inicialización para pago sin tarjeta guardada y con tarjeta guardada.

Método doPayment( )

El método devuelve Promise, que al ejecutarse exitosamente devuelve un objeto con información sobre el pago realizado.

El método devuelve los siguientes parámetros:

redirectUrl string optional
Dirección de redirección después de realizar el pago

Objeto con información sobre el pago realizado

Es posible la presencia de campos adicionales, o la ausencia de algunos campos de la lista anterior.

Ejemplo

{
    "redirectUrl": "https://bankhost.com/payment/merchants/ecom/finish.html?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
    "finishedPaymentInfo": {
        "paymentSystem": "MASTERCARD",
        "merchantShortName": "CoffeToGo",
        "merchantLogin": "CoffeToGo",
        "merchantFullName": "Coffee to Go",
        "approvalCode": "123456",
        "orderNumber": "4003",
        "formattedTotalAmount": "15.00",
        "backUrl": "https://www.coffeetogo.com/congratulation?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
        "failUrl": "https://www.coffeetogo.com/someproblem?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
        "terminalId": "12345678",
        "orderDescription": "Order 123",
        "displayErrorMessage": "",
        "loadedResources": {
            "footer": false,
            "logo": false
        },
        "currencyAlphaCode": "EUR",
        "orderFeatures": [
            "ACS_IN_IFRAME",
            "BINDING_NOT_NEEDED"
        ],
        "isWebView": false,
        "actionCodeDetailedDescription": "Request processed successfully",
        "transDate": "29.11.2024 15:19:30",
        "currency": "978",
        "actionCode": 0,
        "expiry": "12/2024",
        "formattedAmount": "15.00",
        "actionCodeDescription": "",
        "formattedFeeAmount": "0.00",
        "email": "address@mail.com",
        "amount": "1500",
        "merchantCode": "12345678",
        "ip": "x.x.x.x",
        "panMasked": "555555**5599",
        "successUrl": "https://www.coffeetogo.com/congratulation?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
        "paymentWay": "CARD",
        "processingErrorType": {
            "value": "NO_ERROR",
            "messageCode": "payment.errors.no_error",
            "apiErrorCodeMessage": "payment.errors.no_error.code"
        },
        "panMasked4digits": "**** **** **** 5599",
        "amountsInfo": {
            "currencyDto": {
                "alphabeticCode": "EUR",
                "numericCode": "978",
                "minorUnit": 2
            },
            "depositedAmount": {
                "value": 1500,
                "formattedValue": "15.00"
            },
            "totalAmount": {
                "value": 1500,
                "formattedValue": "15.00"
            },
            "refundedAmount": {
                "value": 0,
                "formattedValue": "0.00"
            },
            "approvedAmount": {
                "value": 1500,
                "formattedValue": "15.00"
            },
            "feeAmount": {
                "value": 0,
                "formattedValue": "0.00"
            },
            "paymentAmount": {
                "value": 1500,
                "formattedValue": "15.00"
            },
            "amount": {
                "value": 1500,
                "formattedValue": "15.00"
            },
            "depositedTotalAmount": {
                "value": 1500,
                "formattedValue": "15.00"
            }
        },
        "errorTypeName": "SUCCESS",
        "feeAmount": "0",
        "totalAmount": "1500",
        "orderParams": {
            "phone": "+4915112345678",
            "foo": "bar",
            "paymentMethod": "multiframe-sdk"
        },
        "orderExpired": false,
        "refNum": "111111111111",
        "finishPageLogin": "ecom",
        "sessionExpired": false,
        "cardholderName": "JOHN DOE",
        "paymentDate": "29.11.2024 15:19:49",
        "merchantUrl": "https://www.coffeetogo.com/",
        "status": "DEPOSITED"
    }
}

Ejecución no exitosa

En caso de ejecución no exitosa, Promise devuelve un mensaje de error que podemos mostrar en la página. Ejemplo de mensaje: Error: Operation declined. Please check the data and available balance of the account.

Los ejemplos de código para el procesamiento de datos devueltos por el método doPayment(), se presentan en la sección Paso 2. Procesamiento del clic del botón de pago. Ejecutar método de pago para el pago sin tarjeta guardada y con tarjeta guardada

Redirección automática después de realizar el pago

Después del pago ocurre una redirección automática desde la página con Web SDK. Para procesar el objeto devuelto por el método doPayment() directamente en la página con Web SDK, se requiere desactivar la redirección automática después de realizar el pago. Para esto se requiere un permiso especial en el sistema ‒ en la pasarela de pagos para este comerciante debe estar habilitado el permiso Soporte Acs IFrame habilitado. Para obtener el permiso, contacte al servicio de soporte técnico del banco. También durante la inicialización del Web SDK en el objeto transmitido debe contener la propiedad shouldHandleResultManually: true.

Por ejemplo:

webSdkForm = new window.PaymentForm({
      ...
      shouldHandleResultManually: true,
      ...
  });

Web SDK en React SPA

Al usar Web SDK en una single page application (SPA) en React es necesario inicializar Web SDK, ejecutando el método webSdkPaymentForm.init() en cada renderizado inicial de la página con el formulario Web SDK en SPA.

En el evento de reinicio de la página con el formulario Web SDK (es decir, al navegar a otra página), es necesario ejecutar el método webSdkPaymentForm.destroy(). Esto es importante, ya que en la página debe permanecer solo un manejador de formulario webSDK (multiframe-commutator).

Al regresar a la página con el formulario Web SDK es necesario realizar nuevamente la inicialización utilizando el método webSdkPaymentForm.init().

Tenga en cuenta que para utilizar esta biblioteca se requiere cumplimiento del estándar PCI DSS, ya que procesa datos de tarjeta. Más información sobre PCI DSS aquí.

Ejemplo de componente React

import { useEffect, useRef } from "react";

function addScript(src) {
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");

    script.setAttribute("src", src);
    script.addEventListener("load", resolve);
    script.addEventListener("error", reject);

    document.body.appendChild(script);
  });
}

function App() {
  const panRef = useRef(null);
  const expiryRef = useRef(null);
  const cvcRef = useRef(null);
  const selectBindingRef = useRef(null);
  const saveCardContainerRef = useRef(null);
  const payButtonRef = useRef(null);
  let webSdkPaymentForm = null;

  useEffect(() => {
    const initPaymentForm = async () => {
      await addScript(
        "https://dev.bpcbt.com/payment/modules/multiframe/main.js",
      );

      webSdkPaymentForm = new window.PaymentForm({
        mdOrder: mdOrder,
        containerClassName: "field-container",
        onFormValidate: (isValid) => {
          // Manejo de validación de formulario
        },
        apiContext: "/payment",
        language: "en",
        autoFocus: true,
        showPanIcon: true,
        panIconStyle: {
          height: "16px",
          top: "calc(50% - 8px)",
          right: "8px",
        },
        fields: {
          pan: {
            container: panRef.current,
            onFocus: (containerElement) => {
              // Manejo de enfoque
            },
            onBlur: (containerElement) => {
              // Manejo de pérdida de enfoque
            },
            onValidate: (isValid, containerElement) => {
              // Manejo de validación
            },
          },
          expiry: {
            container: expiryRef.current,
            // Configuración del campo de fecha de expiración
          },
          cvc: {
            container: cvcRef.current,
            // Configuración del campo CVC/CVV
          },
        },
        styles: {
          base: {
            padding: "0px 16px",  
            color: "black",
            fontSize: "18px",
            fontFamily: 'monospace',
          },
          focus: {
            color: "blue",
          },
          disabled: {
            color: "gray",
          },
          valid: {
            color: "green",
          },
          invalid: {
            color: "red",
          },
          placeholder: {
            base: {
              color: "gray",
            },
            focus: {
              color: "transparent",
            },
          },
        },
      });

      webSdkPaymentForm
        .init()
        .then(({ orderSession }) => {
          console.info("orderSession", orderSession);

          if (orderSession.bindings.length) {
            selectBindingRef.current.style.display = "";
          } else {
            selectBindingRef.current.style.display = "none";
          }

          if (orderSession.bindingEnabled) {
            saveCardContainerRef.current.style.display = "";
          } else {
            saveCardContainerRef.current.style.display = "none";
          }

          orderSession.bindings.forEach((binding) => {
            const option = new Option(binding.pan, binding.id);
            selectBindingRef.current.options.add(option);
          });
        })
        .catch(() => {
          // Manejo de error de inicialización
        });
    };

    initPaymentForm();

    return () => {
      if (webSdkPaymentForm) {
        webSdkPaymentForm.destroy();
      }
    };
  }, []);

  const handlePayment = () => {
    payButtonRef.current.disabled = true;

    webSdkPaymentForm
      .doPayment({})
      .then((result) => {
        // Manejo de pago exitoso
      })
      .catch((e) => {
        alert("Error");
      })
      .finally(() => {
        payButtonRef.current.disabled = false;
      });
  };

  const handleSelectBinding = () => {
    const bindingId = selectBindingRef.current.value;
    if (bindingId !== "new_card") {
      webSdkPaymentForm.selectBinding(bindingId);
      saveCardContainerRef.current.style.display = "none";
    } else {
      webSdkPaymentForm.selectBinding(null);
      saveCardContainerRef.current.style.display = "";
    }
  };

  return (
    <div className="container">
      <div className="websdk-form">
        <div className="card-body">
          <div
            className="col-12"
            id="select-binding-container"
            onChange={handleSelectBinding}
          >
            <select
              className="form-select"
              id="select-binding"
              ref={selectBindingRef}
              aria-label="Default select example"
            >
              <option value="new_card">Pagar con nueva tarjeta</option>
            </select>
          </div>
          <div className="col-12 input-form">
            <label htmlFor="pan" className="form-label">
              Número de tarjeta
            </label>
            <div id="pan" className="form-control" ref={panRef}></div>
          </div>
          <div className="col-6 col-expiry input-form">
            <label htmlFor="expiry" className="form-label">
              Vencimiento
            </label>
            <div id="expiry" className="form-control" ref={expiryRef}></div>
          </div>
          <div className="col-6 col-cvc">
            <label htmlFor="cvc" className="form-label">
              CVC / CVV
            </label>
            <div id="cvc" className="form-control" ref={cvcRef}></div>
          </div>
          <label className="col-12" id="save-card-container">
            <input
              className="form-check-input me-1"
              ref={saveCardContainerRef}
              type="checkbox"
              value=""
              id="save-card"
            />
            Guardar tarjeta
          </label>
        </div>
        <div className="pay-control">
          <button
            className="btn btn-primary btn-lg"
            type="submit"
            id="pay"
            ref={payButtonRef}
            onClick={handlePayment}
          >
            Pagar
          </button>
        </div>
        <div
          className="error my-2 text-center text-danger visually-hidden"
          id="error"
        ></div>
      </div>
    </div>
  );
}

export default App;
Categorías:
eCommerce SDK
Categorías
Resultados de búsqueda