requestAutocomplete

Toma mi dinero, no mi tiempo

Introducción

Me gusta la Web. En general, creo que es una buena idea. Así que participo en muchos debates web o nativos. La otra persona no tardará mucho en hablar sobre la facilidad de los pagos a través de los sistemas nativos. Mi respuesta habitual es dejar caer una bomba de humo y salir corriendo de la sala a reír loco, porque no es un argumento en el que pueda ganar. El abandono del carrito de compras en la Web móvil puede llegar al 97%. Imagina eso en el mundo real. Imagina que el 97% de las personas en un supermercado, con un carrito lleno de cosas que quieren, voltea su carrito y se va. Ahora bien, algunas de esas personas simplemente suben los precios de las cosas y nunca tuvieron la intención de comprar, pero la horrible experiencia del usuario de comprar en la Web contribuye de manera significativa. Aplicaremos un impuesto a los usuarios sobre su estado de ánimo. Piensa en una experiencia de pago placentera que hayas tenido en la Web, especialmente en dispositivos móviles. Es una tienda de aplicaciones, ¿verdad? O, al menos, un sistema cerrado similar que ya tenga tu información de pago. Esto es un problema. Requiere que los sitios se comprometan con un proveedor de pagos en particular con el cual el usuario ya debe tener una cuenta y a la que debe haber accedido, o que se comprometan con una plataforma que requiera que los usuarios accedan a un proveedor de pagos específico, como una tienda de aplicaciones que requiere que usted codifique solo para esa plataforma. Si no haces una de estas cosas, el usuario estará condenado a presionar la pantalla o el teclado hasta que desaparezca toda la piel de su dedo, o se rinda. Debemos solucionar ese problema.

requestAutocomplete

En un mundo de WebGL, WebRTC y otras APIs web de lujo que comienzan con "Web", requestAutocomplete es bastante poco glamoroso. Sin embargo, es una superheroína vestida de color beige. Una API pequeña y aburrida que tiene todo en el corazón del vampiro del tiempo de los pagos web.

En lugar de depender de un proveedor de pagos en particular, el sitio solicita los detalles del pago desde el navegador, el cual los almacena en nombre del usuario. La versión de Chrome de requestAutocomplete() también se integra con la Billetera de Google solo para usuarios de EE.UU. (por el momento). Pruébalo en nuestro sitio de prueba.

form.requestAutocomplete

Los elementos de formulario tienen un único método nuevo, requestAutocomplete, que le solicita al navegador que complete el formulario. El navegador mostrará un diálogo al usuario en el que se le solicitará permiso y le permitirá seleccionar qué detalles desea proporcionar. No puedes llamar a este método cuando lo desees, sino que se debe llamar durante la ejecución de eventos de interacción particulares, como los eventos de clic, teclas, tocar la pantalla, arriba y abajo del mouse. Esta es una restricción de seguridad deliberada.

button.addEventListener('click', function(event) {
  form.requestAutocomplete();
  event.preventDefault();
});

// TODO: listen for autocomplete events on the form

Antes de analizar los eventos, debemos asegurarnos de que el navegador comprenda los campos del formulario...

Requisitos del formulario

Cuando Internet estaba en blanco y negro, Internet Explorer 5 adoptó un nuevo atributo, autocomplete, para los elementos de entrada de formularios. Se podía desactivar para que el navegador dejara de ofrecer sugerencias y eso fue todo. Se amplió esta API para que puedas especificar el contenido esperado del campo sin modificar el atributo "name", y esto es lo que usa requestAutocomplete para vincular los campos del formulario con los datos del usuario.

<input name="fullname" autocomplete="name">

A modo de especificación, requestAutocomplete no es específico de los pagos, pero la implementación actual de Chrome sí lo es. En el futuro, se espera que los navegadores puedan procesar otros tipos de datos, como los datos de acceso y el generador de contraseñas, información de pasaportes y hasta subir un avatar.

Actualmente en Chrome, requestAutocomplete reconoce lo siguiente:

Pago

  • email
  • cc-name: nombre en la tarjeta
  • cc-number - número de tarjeta
  • cc-exp-month: el mes de vencimiento de la tarjeta en dos dígitos
  • cc-exp-year: el año de vencimiento de la tarjeta en cuatro dígitos
  • cc-csc: Código de seguridad de la tarjeta de 3 a 4 dígitos
<input type="email" autocomplete="email" name="email">
<input type="text" autocomplete="cc-name" name="card-name">
<input type="text" autocomplete="cc-number" name="card-num">
<input type="text" autocomplete="cc-exp-month" name="card-exp-month">
<input type="text" autocomplete="cc-exp-year" name="card-exp-year">
<input type="text" autocomplete="cc-csc" name="card-csc">

Los atributos de “nombre” que usé anteriormente son solo ejemplos; no es necesario usar valores particulares. Si vas a reutilizar este formulario para los usuarios que no tienen requestAutocomplete, que es lo ideal, puedes agregar etiquetas, un diseño y una validación de HTML5 básica.

Tampoco estás limitado a los elementos de entrada, puedes usar cualquier tipo de entrada de formulario. Por ejemplo, puedes usar <select> para los campos de vencimiento de la tarjeta.

Mensaje detallado de la consola.
Mensaje detallado de la consola

Dirección

  • name - nombre completo. Tomar un nombre completo como un solo campo es mucho mejor que usar varios campos. Varios campos, como el nombre y el apellido, muestran un sesgo occidental y es posible que no tengan sentido para otras culturas. Además, es más fácil escribir en un solo campo.

  • tel. número de teléfono completo con código de país, también se puede desglosar en

    • tel-country-code: p.ej., +44
    • tel-national - el resto
  • calle-address: dirección completa con componentes separados por comas, puede desglosarse en

    • dirección-línea1
    • address-line2: puede estar vacío
  • localidad: ciudad/pueblo

  • región: código de estado, condado o cantón

  • postal-code [código postal]: código postal, código postal, código postal

  • country

Lo anterior debe usarse en combinación con lo siguiente: - facturación - envíos

<input type="text" autocomplete="billing name" required name="billing-name">
<input type="tel" autocomplete="billing tel" required name="billling-tel">
<input type="text" autocomplete="billing address-line1" required name="billing-address1">
<input type="text" autocomplete="billing address-line2" required name="billing-address2">
<input type="text" autocomplete="billing locality" required name="billing-locality">
<input type="text" autocomplete="billing region" required name="billing-region">
<input type="text" autocomplete="billing postal-code" required name="billing-postal-code">
<select autocomplete="billing country" required name="billing-country">
  <option value="US">United States</option>
  …
</select>

<input type="text" autocomplete="shipping name" name="shipping-name">
…

Como dijimos, los atributos de nombre son ejemplos; puedes usar lo que desees. Obviamente, no todos los formularios deben solicitar una dirección de envío; p.ej., no me preguntes dónde me gustaría que se entregue mi habitación de hotel, su ubicación actual suele ser el argumento de venta. Muy bien. Tenemos nuestro formulario y sabemos cómo solicitar autocompletion. Pero...

¿Cuándo se debe llamar a requestAutocomplete?

Lo ideal es que muestres el diálogo requestAutocomplete en lugar de cargar la página que muestra el formulario de confirmación de la compra. Si todo sale bien, el usuario no debería ver el formulario.

Flujo de pagos

Un patrón común es tener una página del carrito con un botón de “confirmación de la compra” que te lleve al formulario de detalles del pago. En esta situación, quieres cargar tu formulario de facturación en la página del carrito, pero ocultarlo del usuario y llamar a requestAutocomplete cuando este presione el botón "Pagar". Recuerda que deberás publicar la página del carrito mediante SSL para evitar la advertencia de Skeletor. Para comenzar, debemos ocultar nuestro botón de confirmación de la compra de modo que el usuario no pueda hacer clic en él hasta que estemos listos, pero solo queremos hacerlo para los usuarios con JavaScript. En el encabezado de la página:

<script>document.documentElement.className += ' js';</script>

Y en tu CSS:

.js #checkout-button,
#checkout-form.for-autocomplete {
  display: none;
}

Debemos incluir el formulario de facturación en nuestra página del carrito. Puede usarse en cualquier lugar. El CSS anterior se asegura de que no sea visible para el usuario.

<form id="checkout-form" class="for-autocomplete" action="/checkout" method="post">
  …fields for payment, billing address &amp; shipping if relevant…
</form>

Ahora nuestro JavaScript puede comenzar a configurar todo:

function enhanceForm() {
  var button = document.getElementById('checkout-button');
  var form = document.getElementById('checkout-form');

  // show the checkout button
  button.style.display = 'block';

  // exit early if there's no requestAutocomplete support
  if (!form.requestAutocomplete) {
    // be sure to show the checkout button so users can
    // access the basic payment form!
    return;
  }

  button.addEventListener('click', function(event) {
    form.requestAutocomplete();
    event.preventDefault();
  });

  // TODO: listen for autocomplete events on the form
}

Llamarías a enhanceForm en la página del carrito, en algún momento después de que hayas completado el formulario y el botón de confirmación de la compra. Los navegadores compatibles con requestAutocomplete disfrutarán de la nueva experiencia rápida y elegante, mientras que otros recurrirán a la forma de pago normal. Para obtener puntos adicionales, puedes cargar el código HTML del formulario a través de XHR como parte de enhanceForm. Esto significa que solo puedes cargar el formulario en navegadores compatibles con requestAutocomplete y no es necesario que recuerdes agregar el formulario a cada página desde la que puedas llamar a enhanceForm. Así es como funciona el sitio de demostración.

Llamaste a requestAutocomplete, ¿y ahora qué?

El proceso de autocompletado es asíncrono, requestAutocomplete se muestra de inmediato. Para averiguar cómo resultó, escuchamos un par de eventos nuevos:

form.addEventListener('autocomplete', function() {
  // hurrah! You got all the data you needed
});

form.addEventListener('autocompleteerror', function(event) {
  if (event.reason == 'invalid') {
    // the form was populated, but it failed html5 validation
    // eg, the data didn't match one of your pattern attributes
  }
  else if (event.reason == 'cancel') {
    // the user aborted the process
  }
  else if (event.reason == 'disabled') {
    // the browser supports requestAutocomplete, but it's not
    // available at this time. Eg, it wasn't called from an
    // interaction event or the page is insecure
  }
});

Si todo funcionó, puedes hacer lo que quieras con los datos, lo más sencillo es enviar el formulario. Luego, el servidor puede validar los datos y brindarle al usuario una página de confirmación que incluya el costo de entrega. Si los datos no son válidos, puedes mostrar el formulario y destacar los campos que el usuario debe enmendar. De forma alternativa, puedes enviar el formulario y permitir que se haga cargo de la validación habitual del servidor. Si el usuario canceló el proceso, no es necesario que hagas nada. Si la función está inhabilitada, envía al usuario al formulario normal. Así que, en la mayoría de los casos, tus oyentes se verán como...

form.addEventListener('autocomplete', function() {
  form.submit();
});

form.addEventListener('autocompleteerror', function(event) {
  if (event.reason == 'invalid') {
    form.submit();
  }
  else if (event.reason != 'cancel') {
    window.location = '/checkout-page/';
  }
});

¿Dónde almacena mis datos el navegador?

La especificación no determina dónde se almacenan los datos, lo que permite que los navegadores innoven. Si accediste a Chrome, tienes la opción de almacenar los detalles en la Billetera de Google para que puedas acceder a ellos desde otros dispositivos en los que hayas accedido. Si almacenas tus datos en la Billetera, requestAutocomplete no entregará tu número de tarjeta real, lo que aumentará la seguridad. Si no accediste a Chrome o decides no usar la Billetera de Google, tus detalles se almacenan de forma local en el navegador para que puedas volver a usarlos. Este es el estado actual, pero en el futuro Chrome y otros navegadores podrían adoptar otros proveedores de pagos.

Pagos fáciles

Es ridículo que los usuarios tengan que ingresar su información de pago una y otra vez cada vez que quieren realizar una compra. Todo se vuelve más fácil cuando un sitio almacena los detalles del pago. Me preocupa un poco cuántos sitios almacenan los detalles de mi tarjeta. Este es un problema perfecto que deben resolver los estándares web. requestAutocomplete permite realizar pagos con un clic a toda la Web, sin depender de un servicio ni de una plataforma, y con tiempo.

Ronda adicional: Cómo manejar formularios de varias páginas

Es mucho mejor llamar a requestAutocomplete una vez y recopilar todos los datos que necesitas. Si no puedes modificar tu servidor para recibir todos estos datos a la vez, no hay problema. Extrae los datos del formulario completado y envíalos de la forma que te resulte más conveniente. Puedes usar esta función pequeña para capturar todos los datos admitidos actualmente como un objeto simple, sin tener que crear un formulario tú mismo. Una vez que tengas los datos, puedes transformarlos al formato que necesite tu servidor y publicarlos en varios pasos.

checkoutButton.addEventListener('click', function() {
  requestUserData({
    billing: true,
    shipping: true
  }, function(response) {
    if (response.err == 'cancel') {
      // exit silently
      return;
    }
    if (response.err) {
      // fall back to normal form
      window.location.href = '/normal-checkout-form/';
      return;
    }

    // the rest is just made-up pseudo code as an example
    postToServer(data.shipping).then(function() {
      return postToServer(data.billing);
    }).then(function() {
      return postToServer(data.cc);
    }).catch(function() {
      // handle error
    });
  });
});