Manejo de eventos con postMessage

Función postMessage en JavaScript

*postMessage es una función de JavaScript crucial para la comunicación segura entre iFrames y ventanas en aplicaciones web. Es especialmente importante en el contexto de pagos online, donde proteger los datos de los usuarios es esencial. Esta función nos permite intercambiar información de forma segura entre el sistema de pago y la página del comercio, garantizando que los datos sensibles, como la información de tarjetas de crédito, se manejen con la máxima seguridad. Utilizamos postMessage** para enviar eventos estandarizados que informan sobre el progreso de las transacciones, mejorando la experiencia del usuario y asegurando transacciones seguras.*

En la siguiente sección, encontrarás detalles sobre estos eventos, ejemplos de implementación y recomendaciones sobre cómo actuar tras recibir y procesar estos eventos, facilitando una integración fluida en tu sistema de pagos.

Tabla de eventos

EventoMeta-PayloadDescripciónComo reproducir en el ambiente de pruebas?
VaultStartedSe emite cuando el widget haya cargado y el usuario ahora pueda interactuar dentro de los elementos de la página.N/A
VaultSaveSuccesscreatedCard: bankName (string), cardId (string), company (string), firstSix (string), lastFour (string), userId (string), storedCard (boolean, sera true si la tarjeta fue seleccionada de las tarjetas guardadas de usuario, de lo contrario es false si es nueva)Emitido cuando se completa con éxito la creación de una tarjeta en DEUNA.N/A
VaultSaveErrorerrorMessage,
errorCode
Se dispara cuando ocurre un error en la creación de la tarjeta.Usar la tarjeta: 1354 1001 4004 955
VaultFailederrorCode,
errorMessage
Para errores generales durante la carga.Al momento de cargar la URL de elements. Cambiar la public key por una valor random.
VaultProcessingN/AEmitir cuando el usuario haga clic en el botón de acción y mientras la API de DEUNA responda.N/A
onBinDetectedAquí se proporciona como metadata:
BIN de la tarjeta (card_bin)
Marca de la tarjeta (card_brand)
Estos datos se enviarán únicamente si se ingresa el BIN. En caso de que el sistema detecte la introducción del BIN y posteriormente se borre, no se enviará metadata, para informar al comercio que no lo envíe en la transacción.
Emitido al reconocer algún cambio en el ingreso del BIN. N/A
onInstallmentSelectedparsedData.data?.metadata?.plan_option_id : dicho ID contiene la opción seleccionada de installment que se debe mandar en el /purchase API)Se emite este evento cuando el usuario seleccionó alguna opción del dropdown de installmentsN/A
vaultSaveClickN/ASe emite cuando el usuario le hace click al botón de continuar/pagar.N/A
paymentMethodsCardNumberEnteredN/ASe emite cuando el usuario termina de agregar todos los dígitos de la tarjeta.
paymentMethodsCardNumberInitiatedN/ASe emite cuando el usuario selecciona el campo de número de tarjeta.
paymentMethodsCardExpirationDateInitiatedN/ASe emite cuando el usuario selecciona el campo de fecha de expiración.
paymentMethodsCardExpirationDateEnteredN/ASe emite cuando el usuario termina de agregar la fecha de expiración.
paymentMethodsCardSecurityCodeInitiatedN/ASe emite cuando el usuario selecciona el campo de CVV.
paymentMethodsCardSecurityCodeEnteredN/ASe emite cuando el usuario termina de agregar el CVV.
paymentMethodsCardNameInitiatedN/ASe emite cuando el usuario selecciona el campo de nombre del tarjetahabiente.
paymentMethodsCardNameEnteredN/ASe emite cuando se cumple con que el usuario agrega al menos dos palabras separadas por un espacio (sin caracteres especiales ni números)

Orden de los eventos

TBD

Ejemplo de código para escuchar los postMessage (Web)

window.addEventListener("message", (event) => {
  // Verificar el origen del mensaje para garantizar seguridad
  if (event.origin !== "URL_DE_CONFIANZA") {
    return;
  }

  // Procesar el evento recibido
  switch (event.data.type) {
    case 'VaultStarted':
      console.log("VaultStarted: Click to Pay está listo para la interacción.");
      // Implementar acciones cuando el proceso de pago ha iniciado
      break;

    case 'VaultSaveSuccess':
      const cardId = event.data.metadata.createdCard.id;
      console.log(`VaultSaveSuccess: Tarjeta guardada con éxito. ID de la tarjeta: ${cardId}`);
      // Manejar el éxito en la creación de la tarjeta y proceder con el proceso de pago
      break;

    case 'VaultSaveError':
    case 'VaultFailed':
      const errorCode = event.data.metadata.errorCode;
      const errorMessage = event.data.metadata.errorMessage;
      console.error(`${event.data.type}: Error en el proceso. Código: ${errorCode}, Mensaje: ${errorMessage}`);
      // Manejar errores en el guardado de la tarjeta o errores generales
      break;

    case 'VaultProcessing':
      console.log("VaultProcessing: Procesando pago con DEUNA.");
      // Implementar acciones mientras se procesa la transacción
      break;
    case 'onBinDetected':
    	// Cuando el usuario ingresa los primeros 6 dígitos de la tarjeta
      const cardBrand = event.data?.metadata?.cardBrand;
    case 'onInstallmentSelected':
    	// Cuando el usuario selecciona las cantidad de cuotas
    	const planOptionId = event.data.metadata.plan_option_id;
   case 'onBinEmpty':
    	// Cuando el usuario limpia el BIN
    default:
      console.warn(`Evento no reconocido: ${event.data.type}`);
      // Manejar cualquier otro caso no especificado
      break;
  }
});

Ejemplo de código para escuchar los postMessage (iOS - Swift)

// Step 1: Implement the following delegates: WKNavigationDelegate, WKUIDelegate 

// Step 2: Define the webView and override the window.open behaviour

 let configuration = WKWebViewConfiguration()
 private var webView: WKWebView?


 var scriptSource = """
    window.open = function(open) {
        return function(url, name, features) {
            location.href = url; // or window.location.replace(url)
        };
    }(window.open);
    """

// Step 3: Initialize the webView

/// Loads the web view with the specified URL.
/// Parameter urlString: The URL string to load.
func loadUrl(urlString: String) {
  let userScript = WKUserScript(
    source: scriptSource,
    injectionTime: .atDocumentStart,
    forMainFrameOnly: false
  )

  self.configuration.preferences.javaScriptEnabled = true
  self.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
  self.configuration.userContentController.addUserScript(userScript)
  self.configuration.userContentController.add(self, name: "deuna") // IMPORTANT the name "deuna"

  self.webView = WKWebView(
    frame: view.bounds,
    configuration: self.configuration
  )

  self.webView!.navigationDelegate = self

  if let url = URL(string: urlString) {
    let request = URLRequest(url: url)
    self.webView?.load(request)
  }
}

// Step 4: Define the function to listen when the URL of the widget
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
  view.addSubview(webView)
  self.adjustScrolling(webView: webView)
}

// Listen to the PostMessages
// Gets the data from the script message as Foundation Data.
//    - Parameter message: The script message.
//    - Returns: The data from the script message, if available.
func getMessageData(message: WKScriptMessage) -> Foundation.Data? {
  guard let jsonString = message.body as? String else {
    return nil
  }
  return jsonString.data(using: .utf8)
}

/// Handler for receiving JavaScript messages.
///
/// - Parameters:
///   - userContentController: The user content controller.
///   - message: The message received.
func userContentController(
  _ userContentController: WKUserContentController,
  didReceive message: WKScriptMessage
) {
  // Attempt to decode the received JSON message
  guard let jsonData = getMessageData(message: message) else {
    return
  }

  do {
    // Decode the JSON message 
    let decoder = JSONDecoder()
    // listen the post message data
  } catch {

  }
}

Ejemplo de código para escuchar los postMessage (Android - Kotlin)

// Step 1: Create and show the WebView
lateinit var webView: WebView
    
private fun initialize() {
  webView = findViewById(R.id.deuna_webview)
}

// Step 2: Enable JS and pass over the interface to listen the postMessages and show the URL
// Load the URL in the WebView
@SuppressLint("SetJavaScriptEnabled")
fun loadUrl(url: String) {

  webView.settings.apply {
    domStorageEnabled = true
    javaScriptEnabled = true
    setSupportMultipleWindows(true) // Enable support for multiple windows
  }

  // Add JavascriptInterface
  webView.addJavascriptInterface(WebViewBridge(), "android") // IMPORTANT the name "android"

  webView.webViewClient = object : WebViewClient() {
    override fun onPageFinished(view: WebView?, url: String?) {
      super.onPageFinished(view, url)
      // When the page finishes loading, the Web View is shown and the loader is hidden
    }
  }
  webView.loadUrl(url)
}

// Step 3: To create a class with a postMessage function using the @javascriptInterface annotation and listen for post message events
// Class to handle communication between the WebView and the native Android code
class WebViewBridge {
  /**
   * The postMessage function is called when a message is received from JavaScript code in a WebView.
   * The message is parsed and the corresponding callbacks are called based on the event type.
  */
  @JavascriptInterface
  fun postMessage(message: String) {
    try {
      val json = JSONObject(message)
      // listen the post message events
    } catch (e: Exception) {
      Log.d("WebViewBridge", "postMessage: $e")
    }
  }

  abstract fun handleEvent(message: String)
}

Acciones post-captura de evento

Tras la captura de un evento, es crucial que el comercio inicie un proceso en su backend para realizar una llamada al endpoint para procesar el pago de nuestro servicio. Esto garantiza la finalización efectiva de la transacción de pago, siguiendo nuestras directrices de seguridad y manejo de datos.