Introducción

En nuestro post anterior vimos cómo funcionan las pasarelas de pago en general y porque nos hace falta tirar de ellas y no ir como espartanos a manejar nosotros mismos tarjetas de crédito.

En este post pasamos de la teoría a la práctica y nos metemos en código, integrándonos con una pasarela en concreto: el proveedor de soluciones de pago Stripe

TL;DR;

Stripe tiene una solución de pago llamada CheckOut que nos permite trabajar en modo pasarela de pago, ¿ Qué es esto de una pasarela de pago? Echale un ojo a nuestro post anterior.


Te puedes descargar un ejemplo básico en este enlace y ver como funciona, el flujo sería:

  • Inicio de compra.

  • Comunicación vía back channel para obtener un id de sesión para la compra.

  • Redirección en cliente a la pasarela de pago pasándole el id de sesión de la compra.

  • Una vez finaliza el pago, redirección de la pasarela de pago a las páginas de ok / ko de nuestro sitio.


Si quieres configurar un webhook para recibir notificaciones en tu backend cuando, por ejemplo,una transacción ha finalizado (sea con éxito o no), en este ejemplo con webhooks puedes ver como funciona, peeeero de primeras no podrás ejecutarlo, te hará falta crearte una cuenta, cambiar los token privados y públicos (de test) del ejemplo por lo de tu cuenta e instalarte el stripe-cli para poder redirigir a tu localhost las notificaciones. Una vez que quieras ir a producción, tendrás que usar los tokens de producción, y configurar tu endpoint en el dashboard de tu cuenta de Stripe.


Si quieres ver como funciona esto en detalle sigue leyendo... :-)

Agenda

  • Cómo funciona Stripe.

  • Cuanto te va a costar.

  • Analizando un ejemplo

  • Pago.

  • Notificaciones.

  • Pasando a producción.

  • Conclusiones.

Como funciona Stripe

Stripe es una solución de pago que te permite trabajar de varias maneras:

  • Stripe Checkout: una pasarela en la que los datos de tarjeta no pasan por tu aplicación.

  • Stripe UI: te permite insertar widgets de Stripe en tu sitio y gestionar pagos.

  • Stripe API: maneja la entrada de tarjetas desde tu sitio web y conecta con una API Rest para gestionar los pagos.


Nos vamos a centrar en la modalidad Checkout ¿Por qué? Porque no queremos tener que manejar números de tarjetas de crédito en nuestro sitio web (en el post anterior encontrarás los argumentos detallados de esta decisión)

Cuanto te va a costar

Salvo que vayas a manejar unos volumenes de venta considerables, las tarifas que a día de hoy ofrecen (España):

  • Cada transacción lleva un fijo asociado de 0,25 €

  • Además de esto te cobran un porcentaje de la cantidad que vayas a cargar:

    • Si son tarjetas europeas un 1,4% del total de la venta.

    • Si son tarjetas no europeas un 2,9% del total de la venta.


Puedes consultar esta información sobre precios en su web (también allí puedes consultar información de otros países).

Para saber si se adapta a tu modelo de negocio, en el post anterior comentamos pros y contras.

Analizando un ejemplo ya montado

Interacción básica

Una de las cosas que más me ha gustado de Stripe es que puedes "bichear" un ejemplo básico (en diferentes tecnologías) y echarlo a andar sin tener que crearte una cuenta

En la demo con el codesandbox podemos encontrar un enlace para descargar el fuente

En la demo con el codesandbox podemos encontrar un enlace para descargar el fuente

En nuestro caso vamos a analizar la versión nodejs (no te preocupes si vienes de otra tecnología te va a ser fácil de seguir y aplican los mismos principios).

Descargamos el ejemplo, descomprimimos el zip, abrimos el terminal (shell, cmd o el terminal de VSCode) en la carpeta donde hemos descomprimido el zip, e instalamos los paquetes de librería que traiga (si no lo tenías ya, te hace falta que hayas instalado previamente nodejs):

npm install

Y la podemos arrancar:

npm start

Podemos ver en el terminal que tenemos un servidor web ligero corriendo en el puerto 4242.

Terminal en el que indica que la aplicación de ejemplo está corriendo el puerto 4242

Terminal en el que indica que la aplicación de ejemplo está corriendo el puerto 4242

En el navegador introducimos la siguiente ruta http://localhost:4242/checkout.html y podemos ver la página de prueba de compra de un producto.

Página de compra

Página de compra

Podemos interactuar y ver como hacer una simulación de compra:

  • Sólo tenemos que hacer clic en el botón de CheckOut

  • Nos redirige a la pasarela de pago de Stripe.

  • Si quieres que funcione prueba con el número de tarjeta "4242 4242 4242 4242"

  • Si el pago requiere confirmación, prueba con el número de tarjeta "4000 0025 0000 3155"

  • Si quieres probar con un pago fallido prueba con la tarjeta "4000 0000 0000 9995"

  • Una vez realizada la operación nos vuelve a redirigir a nuestro sitio web (a la página de éxito, o error)

Ejemplo básico en funcionamiento

Ejemplo básico en funcionamiento

Vamos a ver la estructura de ficheros del ejemplo:

Checkout.html para lanzar proceso checkout y redirigir, server para pedir un sessión Id a Stripe, sucess.html y error.html para las páginas de ok/ko

Checkout.html para lanzar proceso checkout y redirigir, server para pedir un sessión Id a Stripe, sucess.html y error.html para las páginas de ok/ko


Y el flujo sería:

¡¡Genial!! pero yo no he creado mi cuenta, y no he usado ningún secreto con Stripe y eso funciona ¿Magia? En este ejemplo estás usando claves de desarrollo genéricas que te da Stripe, en un proyecto real tendrías que usar las tuyas (te da dos pares unas para desarrollo y otra para producción), si no usas las tuyas no podrás probar una funcionalidad muy importante: la de notificaciones / web hooks. Todo esto lo iremos cubriendo en los siguientes apartados de este post.


Partes importantes de código:

En este fragmento de checkout.html podemos ver como se envía la petición a nuestro backend para que le pida la petición al backend de stripe crear un nuevo session Id para esa compra.

./checkout.html

checkoutButton.addEventListener("click", function () {
  // A Backend a pedir session id de stripe para nuestra transacción
  fetch("/create-checkout-session", {
    method: "POST",
  })

Si vamos a la parte de servidor, este es el endpoint que se llama desde checkout.html y es el que establece conexión con el backend de Stripe para pedir que cree una sesión con la compra a realizar, a tener en cuenta:

  • Fíjate que la sesión la firmamos con un secreto que compartimos con Stripe, de esta manera, la plataforma sabe que la petición viene de nosotros y no de cualquier impostor. ¡Muy importante! ese "secreto/token" (clave privada) debemos de guardarlo a buen recaudo, es la razón principal por la que la comunicación no se realiza directamente en el frontend (Front Channel).

  • En la información que le enviamos:

    • Le indicamos la cantidad a pagar.

    • Le podemos indicar información de lo que el cliente va a comprar (así se le muestra para confirmar)

    • Le tenemos que indicar cuales es la página a la que tiene que volver una vez que se realiza la compra (si tienes éxito success.html, si no error.html)

  • Stripe nos devuelve un session Id que le pasaremos al front (sólo podemos redirigir en el navegador, esta es la razón por la que lo tenemos que pasarlo de vuelta), y ese session Id tendrá una validez de unos minutos.

./server.js

// Ojo que aquí le pasamos la clave privada
const stripe = require("stripe")(
  "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
);

// (...)

app.post("/create-checkout-session", async (req, res) => {
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    line_items: [
      {
        price_data: {
          currency: "usd",
          product_data: {
            name: "Stubborn Attachments",
            images: ["https://i.imgur.com/EHyR2nP.png"],
          },
          unit_amount: 2000,
        },
        quantity: 1,
      },
    ],
    mode: "payment",
    // Aquí es donde indicamos a que páginas de nuestro sitio web
    // tiene que redirigir si la transacción ha tenido éxito
    // o si ha sido erronea
    success_url: `$/success.html`,
    cancel_url: `$
  );

  // Le envíamos al Front end el id de session que tiene que informar al redirigir a la
  // pasarela de pago
  res.json({ id: session.id });
});

Vamos a volver al código de checkout.html, fijate que para enviar la petición de redirección a Stripe:

  • Por un lado tenemos que enviarlo con otro token (es una clave pública), esta clave sólo sirve para identificar nuestra cuenta de stripe, no es secreto.

Fíjate que directamente referenciamos al SDK desde una CDN, seguro que se nos viene a la cabeza buscar el paquete de turno y hacer un npm install, pueees no, en Stripe te obligan a usarlo de esta manera por motivos de seguridad.


Aquí referenciamos directamente al CDN de Stripe

./checkout.html

<head>
  <title>Buy cool new product</title>
  <link rel="stylesheet" href="style.css" />
  <script src="https://polyfill.io/v3/polyfill.min.js?version=3.52.1&features=fetch"></script>
  <!-- tenemos que referenciarlo directamente, no vale meterlo en un bundle -->
  <script src="https://js.stripe.com/v3/"></script>
</head>

Creamos la instancia de Stripe y le pasamos nuestro token público para identificarnos como clientes.

./server.js

  <script type="text/javascript">
    // Create an instance of the Stripe object with your publishable API key
    var stripe = Stripe("pk_test_TYooMQauvdEDq54NiTphI7jx");

Aquí recibimos la respuesta de nuestro backend con el session Id, y regidirigimos al sitio de pago de Stripe.

./server.js

    checkoutButton.addEventListener("click", function () {
      fetch("/create-checkout-session", {
        method: "POST",
      })
        .then(function (response) {
          return response.json();
        })
        .then(function (session) {
          // Nuestro backend nos devuelve el id de session que ha obtenido de Stripe
          // Le decimos a stripe que nos rediriga pasandole esa información
          return stripe.redirectToCheckout({ sessionId: session.id });
        })

Por ultimo la página de success es una página tonta, si te fijas en sitio web de comercio electrónico como Pc Componentes u otros, cuando haces la compra te redirige a una página de ese estilo con un mensaje del tipo: tu compra ha sido completada con éxito, pincha aquí para volver a la página principal o seguir comprando.

./success.html

<html>
  <head>
    <title>Thanks for your order!</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <section>
      <p>
        We appreciate your business! If you have any questions, please email
        <a href="mailto:orders@example.com">orders@example.com</a>.
      </p>
    </section>
  </body>
</html>

Oye pues parece fácil, pero... ¿Cómo me entero en mi servidor que el cliente ha completado el pago con éxito? Vamos a por el siguiente apartado y ejemplo.

Web Hooks

Primer contacto

Vamos a ver como recibir una notificación en nuestro servidor cuando un pago se culmina con éxito, para ello:

  • Definimos un endpoint en nuestra API de backend que recibirá dicha información (tenemos que cumplir con la firma que nos diga Stripe).

  • En el portal de Stripe, en el dashboard de nuestra cuenta, le indicamos cuales son las direcciones a nuestros endpoints de notificaciones.

  • De esta manera, cuando una compra de Stripe se complete contactará con nuestro endpoint y le enviará dicha información.


Es decir siguiendo con el ejemplo del apartado anterior, definimos un endpoint que será invocado por Stripe, en este casó sólo estamos mostrando por consola la respuesta que nos da ese servidor, pero sería el sitio ideal para guardar el resultado de la transacción en base de datos, enviar un email al cliente confirmando la compra, etc.

Tenemos que incluir la siguiente entrada en el fichero server.js (insertamos este contenido justo después de la línea const YOUR_DOMAIN = 'http://localhost:4242')

./server.js

// Si estás usando una versión antigua de express
// si no directamente puedes hacer app.use(express.json())
const bodyParser = require("body-parser");

// Este va a ser el punto de entrada que buscará stripe
app.post(
  "/webhook",
  bodyParser.raw({ type: "application/json" }),
  (request, response) => {
    const payload = request.body;

    // Aquí simplemente mostramos por consola lo que nos devuelve Stripe
    console.log("Got payload: " + payload);

    response.status(200);
  }
);

Ojo, pero debemos tener en cuenta algo muy importante: cualquiera podría enviar una confirmación de compra, ¿Cómo sabemos que es Stripe? Porque el cuerpo del mensaje viene firmado, podemos usar un token que nos da Stripe para verificar que la confirmación viene de ellos (esto es importante, os imagináis que nos los saltamos y nos empiezan a trollear enviando confirmaciones de compra...), para ello Stripe nos provee de un secreto asociado a nuestro endpoint (el webhook que acabamos de crear) y con este podemos validar que la respuesta y garantizar que la información viene de Stripe y no de un impostor.

Antes de continuar, tenemos que hacer una pausa, nos falta una pieza del puzzle para poder desarrollar en local:

  • Una herramienta que nos permita probar las notificaciones de Stripe en localhost (desarrollo).

  • Una forma de generar el secreto asociado a nuestro endpoint de desarrollo.

Stripe CLI al rescate

Muy bien, pero...¿Cómo narices pruebo esto en local? Claro poner un endpoint que apunte a localhost no tiene mucho sentido :), para ello nos podemos bajar el Stripe cli, esta utilidad nos permite autenticarnos desde línea de comandos (nos redirige al navegador) y decirles que para nuestra cuenta en desarrollo rediriga al web hook que tengamos en localhost... ¡La leche!, son listos estos de Stripe :)


Lo primero: puedes ver como instalar ese CLI en tu máquina local, en este enlace, ahí podrás encontrar información detallada de como instalarlo en macOs, Linux, Windows y Docker


Si estás en Mac y tienes homebrew, puedes hacer

brew install stripe/stripe-cli/stripe

También tienes opción de instalártelo manualmente (en enlace anterior podrás encontrar como)

Si estás en Windows puedes usar scoop para instalarlo o descargarlo de este enlace, elegir de la lista el fichero stripe_1.5.5_windows_x86_64.zip descomprimirlo y ejecutar el fichero exe para instalarlo.


Vamos a seguir con el ejemplo anterior

  • Aquí ya tenemos que crearnos una cuenta.

  • Ejecutar el stripe CLI y asociar temporalmente las notificaciones de desarrollo a nuestro endpoint en localhost.

Creando una cuenta

Si nos vamos a la página de Stripe podemos crear una cuenta (de primeras no tiene coste, su modelo de negocio va por comisión sobre ventas), lo que si tendremos que hacer es meter una serie de datos nuestro o de nuestra empresa si queremos tenerlas activas (aquí ya te puedes plantear poner los datos de tu empresa, o ponerte como autónomo / freelance y más adelante cuando pases a producción ver si creas otra cuenta o mantienes esta).

Stripe crear cuenta España

Stripe crear cuenta España

Cambiando de claves genéricas a claves propias

Vale ya tenemos nuestra cuenta Activa, ahora nos vamos a nuestro Dashboard en el portal de Stripe, y en la página principal podemos encontrar las claves de desarrollo, copiamos la publica y privada de test.

En el Dashboard podemos encontrar nuestra clave publica y privada para hacer pruebas

En el Dashboard podemos encontrar nuestra clave publica y privada para hacer pruebas

Abrimos el ejemplo del apartado anterior y sustituimos las claves:

Clave publica

checkout.html

  <script type="text/javascript">
    // AQUI ES DONDE DEBEMOS REEMPLAZAR ESTE TEXTO QUE EMPIEZA POR pk POR NUESTRA CLAVE PUBLICA
    // Si usamos webpack o similar es buena idea meter este valor en una variable de entorno
    // y que dependiendo del entonro en que este ya meta uno u otro valor (test / producción)
    var stripe = Stripe("pk_test_TYooMQauvdEDq54NiTphI7jx");
    var checkoutButton = document.getElementById("checkout-button");

    checkoutButton.addEventListener("click", function () {

Clave privada

server.js

// Aqui es donde reemplazamos este texto que empieza por sk por nuestra clave privada
// MUY IMPORTANTE: en un proyecto real nuestra clave privada no debemos de ponerla a fuego en nuestro
// código fuente, lo aconsejable es guardarlas en variables de entorno
const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');

Antes de seguir con el caso del webhook, vamos a hacer una pausa y comprobamos que podemos hacer una compra de prueba, y todo sigue funcionando (en la página de la pasarela de pago verás que aparece el nombre de tu empresa).

npm start

E intenta completar una compra siguiendo los pasos que comentamos en el primer apartado.

Si no te funciona:

  • Mira si te falta información para completar el perfil en tu cuenta.

  • Mira si no se te ha olvidado cambiar la clave pública (o la privada).

  • Mira si no has bailado las claves al copiar y pegar.

De vuelta a Stripe CLI

Vamos por fin a activar las notificaciones de compra en nuestro backend.


Nos quedamos con la duda de cómo decirle a Stripe que notifique de una compra a un servidor que tenemos en local ¿Por qué esto de primeras no tiene sentido? Bueno Stripe no es capaz de encontrar tu localhost:4242 no está registrado en ningún DNS, ni publicado en internet.

Ahora toca una parte interesante:

Nos aseguramos que tenemos nuestra aplicación web funcionando, el endpoint de notificaciones va a estar en http://localhost:4242/webhook

npm start

En otra ventana desde del terminal arrancamos stripe-cli, este nos pedirá autenticarnos, para ello abrirá una ventana del navegador y si ya estamos logados directamente nos pedirá autorizar al stripe-cli que tenemos en local (si no tendremos que hacer login previamente).

stripe login
Desde el terminal ejecutamos el comando Stripe login

Desde el terminal ejecutamos el comando Stripe login

Nos aparece como cuando emparejamos la tele con un dispositivo, tenemos un texto tanto en el terminal como en el navegador que tiene que coincidir.

Nos aparece un código de emparejamiento y se nos indica que pulsemos intro para abrir una ventana del navegador

Nos aparece un código de emparejamiento y se nos indica que pulsemos intro para abrir una ventana del navegador

Le damos a enter y se nos abre el navegador, si no estamos ya logados en Stripe tendremos que hacerlo, comprobamos que el código de emparejamientos que nos aparece es el mismo y lo autorizamos.

Ventana de emparejamiento que se nos abre en el navegador

Ventana de emparejamiento que se nos abre en el navegador

Una vez autenticados con una comando de stripe-cli le decimos que las notificaciones de compra de prueba las redirija a http://localhost:4242/webhook (este comando lo ejecutamos desde el terminal)

stripe listen --forward-to localhost:4242/webhook

Dejamos abierto este terminal, nos hace de puente para que cuando haya una notificación de compra Stripe la acabe enviando a nuestro localhost:4242/webhook, este comando nos devuelva un código valido por 90 días (empieza por whsec...), que será el que usaremos en nuestro webhook para comprobar que la notificación viene de Stripe y no de un impostor.

Ejecutamos stripe listen y nos hace de puente con las notificaciones de Stripe

Ejecutamos stripe listen y nos hace de puente con las notificaciones de Stripe

Vamos a ver en el código de nuestro webhook cómo verificar que la llamada viene de Stripe.

Dos temas importantes:

- El signing secret que nos ha dado el comando stripe listen lo metemos en la constante endPointSecret (en un proyecto real este valor iría en una variable de entorno).

- El código que hay dentro de app.post(“webhook“, lo reemplazamos por el que puedes ver en el snippet de código (la principal diferencia es que estamos validando si el mensaje viene del Stripe o de un impostor, usando para ello el signing secret que acabamos de asignar a la constante endPointSecret.

./server.js

// Aquí copiamos y pegamos el código que nos dió el comando _stripe listen --forward_ (el que empieza por whsec_)
const endpointSecret = "whsec_...";

// ...

app.post(
  "/webhook",
  bodyParser.raw({ type: "application/json" }),
  (request, response) => {
    const payload = request.body;
    console.log("Got payload: " + payload);

    let event;

    try {
      // Tiramos de la librería de stripe para validar la respuesta usando el endPointSecret
      // esto nos permite ver si el mensaje no viene de un impostor
      event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
    } catch (err) {
      return response.status(400).send(`Webhook Error: ${err.message}`);
    }

    response.status(200);
  }
);

Ahora que lo tenemos configurado, ya podemos probar el proceso de compra y veremos que cuando realizamos la misma, en la consola donde tenemos el servidor de node arrancado veremos la notificación de la compra (tenemos que arrancar una compra simulada y completarla).

Mostramos por consola la transacción cuando nos llega una notificación de Stripe a nuestro webhook

Mostramos por consola la transacción cuando nos llega una notificación de Stripe a nuestro webhook

Y en la consola en la que tenemos corriendo el stripe-cli (stripe listen --forward), podemos ver el movimiento:

stripe-cli listen log de transacciones

stripe-cli listen log de transacciones

Si esto no te funciona:

  • Mira a ver si tienes tu clave de test publica en checkout.html

  • Haz lo mismo con la clave de test privada en server.js

  • Realiza el mismo chequeo con el endpointSecret en server.js (asegurate que es el te da el comando stripe listen…)

  • Prueba a parar y ejecutar de nuevo el comando de stripe listen y tu apliación de ejemplo.

Pasando a producción

  • Ya tenemos nuestro ejemplo funcionando, ahora toca pasar esto a producción, ¿Qué debemos tener en cuenta? En el Dashboard de Stripe, en la misma página en la que obtuvimos nuestros tokens de test, podemos obtener los de producción, ¡Ojo! El token privado solo se visualiza una vez, así que busca primero un sitio donde guardarlo a buen recaudo (también lo puedes regenerar si se te pierde).

  • La clave pública de producción: la puedes poner en tu código, no es secreta (lo ideal es que lo tengas configurado en tu proceso de bundling como variable de entorno para así poder cambiar de forma fácil de modo desarrollo a modo producción).

  • La clave privada de producción: esta clave se queda en el backend (servidor), no la puedes exponer en el Front End, y debes tenerla a muy buen recaudo:

    • No la tengas a fuego en tu repositorio de código fuente.

    • Utiliza variables de entorno, en tu repositorio estaría sólo la clave de desarrollo.

    • La clave de producción, la introducirías directamente como variable de entorno en tu servidor de producción.

    • Lo ideal es que el equipo de desarrollo no tenga acceso a ella, o sólo un número muy limitado de personas.

productionkeys.png
  • Ahora nos toca definirle en el Dashboard la dirección a nuestro endpoint de notificaciones para enganchar el webhook de Stripe, aquí ya no vamos con localhost sino con nuestro dominio de producción y ruta completa al endpoint, también podemos elegir que tipo de notificaciones queremos recibir.

  • En el menú lateral elegimos la opción de menu Developers >> WebHook

En el menu lateral opcion developer &gt;&gt; Web Hooks

En el menu lateral opcion developer >> Web Hooks

Y allí podemos añadir un endpoint y decirle la url de producción y los eventos que queremos escuchar:

Botón añadir endpoint de producción

Botón añadir endpoint de producción

Configuración endpoint producción, url, eventos que escuchar...

Configuración endpoint producción, url, eventos que escuchar...

Conclusiones

Stripe es una alternativa interesante para gestionar pagos con tarjetas, jugando un poco con ella podemos tener un sistema básico de pago montado en muy poco tiempo, liberándonos de manejar información sensible.

El funcionamiento de otras pasarelas es similar, con lo que parte de los fundamentos que hemos visto en este post aplicarían a otras soluciones de pago.

Recursos

Ejemplo básico checkout: https://stripe.com/docs/payments/accept-a-payment?integration=checkout


Ejemplo recibir notificaciones: https://stripe.com/docs/payments/checkout/fulfill-orders

En nuestro siguiente post...

Si te ha gustado este artículo ¿Te parece interesante si en el siguiente nos metemos a integrarnos con PayPal?

¿Front, Dev o Back?

Si tienes ganas de ponerte al día ¿Te apuntas a alguno de nuestros Masters Online o Bootcamps?