Cómo crear un formulario de tarjeta de crédito usando Stripe.js con React.js en Next.js

Cómo crear un formulario de tarjeta de crédito usando Stripe.js y Stripe Elements, así como también cómo recuperar el valor de ese formulario de tarjeta de crédito y generar un token fuente de Stripe.

Primeros pasos

Para este tutorial, para darnos un punto de partida para nuestro trabajo, vamos a usar CheatCode Next.js Boilerplate. Clonemos una copia de Github ahora:

Terminal

git clone https://github.com/cheatcode/nextjs-boilerplate.git

A continuación, cd en el proyecto e instalar sus dependencias:

Terminal

cd nextjs-boilerplate && npm install

Finalmente, continúe e inicie el servidor de desarrollo:

Terminal

npm run dev

Con eso, estamos listos para comenzar.

Accediendo a nuestras claves API de Stripe

Antes de profundizar en el código, para este tutorial, vamos a necesitar acceso a una cuenta de Stripe. Dirígete a la página de registro en su sitio y crea una cuenta si aún no lo has hecho.

Una vez que tenga una cuenta, inicie sesión en el tablero. Debería ser algo como esto:

Donde queremos navegar es la página que se muestra arriba. Para llegar:

  1. En la esquina superior derecha, asegúrese de haber activado el botón "Modo de prueba" para que se ilumine (en el momento de escribir esto, se volverá naranja cuando esté activado).
  2. A la izquierda de ese interruptor, haga clic en el botón "Desarrolladores".
  3. En la página siguiente, en el menú de navegación de la izquierda, seleccione la pestaña "Claves API".
  4. En el bloque "Claves estándar" de esta página, localice su "Clave publicable".
  5. Copie esta clave (no se preocupe, está destinada a ser expuesta al público).

A continuación, una vez que tengamos nuestra clave publicable, debemos abrir el proyecto que acabamos de clonar y navegar hasta el /settings/settings-development.js archivo:

/configuración/configuración-desarrollo.js

const settings = {
  graphql: { ... },
  meta: { ... },
  routes: { ... },
  stripe: {
    publishableKey: "<Paste your publishable key here>",
  },
};

export default settings;

En este archivo, en orden alfabético en la parte inferior del settings exportado objeto, queremos agregar una nueva propiedad stripe y configúrelo en un objeto con una sola propiedad:publishableKey . Para el valor de esta propiedad, queremos pegar la clave publicable que copió del panel de Stripe anterior. Péguelo y luego guarde este archivo.

A continuación, para usar Stripe en el navegador, debemos cargar la biblioteca Stripe.js a través de Stripe CDN.

Inicializando Stripe.js en el navegador

Por motivos de seguridad, cuando se trata de alojar la biblioteca Stripe.js (lo que usaremos a continuación para generar nuestro formulario de tarjeta de crédito y recuperar un token de tarjeta de crédito), Stripe no permitirnos hospedarnos nosotros mismos. En su lugar, necesitamos cargar la biblioteca a través de un enlace CDN (red de entrega de contenido), alojado por Stripe.

Para cargar la biblioteca, vamos a abrir el /pages/_document.js archivo en nuestro repetitivo, que es donde Next.js configura la plantilla HTML base para nuestro sitio:

/pages/_document.js

import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class extends Document {
  static async getInitialProps(ctx) { ... }

  render() {
    const { styles } = this.props;

    return (
      <Html lang="en">
        <Head>
          ...
          <script
            src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf"
            crossOrigin="anonymous"
          ></script>
          <script src="https://js.stripe.com/v3/"></script>
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

Aquí, hacia la mitad inferior central del <Head></Head> etiqueta que vemos aquí (debajo del cdn.jsdelivr.net/npm/bootstrap script), queremos pegar una etiqueta de script que apunte a la versión alojada en CDN de Stripe.js:<script src="https://js.stripe.com/v3/"></script> .

Esto es todo lo que tenemos que hacer. Cuando cargamos nuestra aplicación ahora, Next.js cargará esta etiqueta de secuencia de comandos. Cuando se ejecute, este script cargará automáticamente Stripe en el navegador y nos dará acceso a la biblioteca a través de la variable global Stripe .

Escribiendo un script para inicializar Stripe

Ahora que tenemos acceso a Stripe, a continuación, debemos escribir un script que nos permita inicializar Stripe con la clave publicable que copiamos anteriormente y luego reutilizar fácilmente esa copia inicializada de la biblioteca.

/lib/stripe.js

import settings from "../settings";

const stripe =
  typeof Stripe !== "undefined" ? Stripe(settings.stripe.publishableKey) : null;

export default stripe;

Aquí, en el /lib carpeta del repetitivo que clonamos anteriormente, estamos agregando un archivo stripe.js que extraerá nuestro publishableKey que configuramos en nuestro archivo de configuración y luego, después de verificar que el Stripe global la variable está definida, llámela como una función Stripe() , pasando nuestro publishableKey .

Luego, suponiendo que recuperemos una instancia (o null si por alguna razón Stripe.js no se carga), lo exportamos desde nuestro archivo. Como veremos a continuación, esto nos permitirá importar una copia "lista para usar" de Stripe.js sin tener que volver a escribir el código anterior cada vez que queremos acceder a la biblioteca (útil si está creando una aplicación y tiene la intención de usar Stripe en varios archivos de proyecto).

Creación de un componente de tarjeta de crédito con Stripe Elements

Ahora viene la parte divertida. Una de las cosas buenas de usar Stripe.js es que nos da acceso a su biblioteca Elements. Esto nos permite configurar rápidamente un formulario de tarjeta en nuestra aplicación sin tener que escribir mucho HTML y CSS repetitivo. Para comenzar, configuraremos un componente basado en clases en React.js (esto nos dará un mejor control sobre la inicialización de Stripe y Elements que el que obtendríamos con un componente basado en funciones).

/páginas/index.js

import React, { useEffect, useState } from "react";

import StyledIndex from "./index.css";

class Index extends React.Component {
  state = {
    token: "",
    cardError: "",
  };

  componentDidMount() {
    // We'll set up Stripe Elements here...
  }

  handleSubmit = () => {
    // We'll handle token generation for our card here...
  };

  render() {
    const { cardError, token } = this.state;

    return (
      <StyledIndex>
        <div className="credit-card" />
        {cardError && <p className="card-error">{cardError}</p>}
        <button
          onClick={() => this.handleSubmit()}
          className="btn btn-primary btn-lg mt-4"
        >
          Get Token
        </button>
        {token && (
          <div className="mt-4">
            <p className="token">{token}</p>
          </div>
        )}
      </StyledIndex>
    );
  }
}

Index.propTypes = {
  // prop: PropTypes.string.isRequired,
};

export default Index;

Configurando, aquí, estamos creando un esqueleto aproximado para la página donde representaremos nuestra tarjeta de crédito a través de Elements. Afortunadamente, la mayor parte del componente es bastante simple.

Aquí, estamos haciendo algunas cosas:

  1. Agregar el marcado HTML que se usará para mostrar nuestro formulario.
  2. Agregar valores predeterminados/de marcador de posición para dos valores de estado que usaremos token y cardError .
  3. Agregar funciones de marcador de posición para componentDidMount() (donde cargaremos Stripe y montaremos nuestro formulario de tarjeta) y handleSubmit() que usaremos para generar nuestro token de tarjeta Stripe.

De nota, aquí, debemos llamar la atención rápidamente al <StyledIndex></StyledIndex> componente que envuelve la totalidad del marcado de nuestro componente. Este es un componente con estilo que es un componente React generado por la biblioteca styled-components . Esta biblioteca nos permite crear componentes React personalizados que representan algún elemento HTML (por ejemplo, un <div></div> o un <p></p> ) y luego adjunte estilos CSS.

Echemos un vistazo al archivo desde donde se está importando muy rápido:

/páginas/index.css.js

import styled from "styled-components";

export default styled.div`
  .credit-card {
    border: 1px solid #eee;
    box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.02);
    padding: 20px;
    border-radius: 3px;
    font-size: 18px;

    &.StripeElement--focus {
      border: 1px solid #ffcc00;
      box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.02);
    }
  }

  .card-error {
    background: #ea4335;
    color: #fff;
    padding: 20px;
    border-radius: 3px;
    margin-top: 10px;
  }

  .token {
    background: #eee;
    padding: 20px;
    border-radius: 3px;
    font-size: 16px;
    color: #444;
  }
`;

Aquí, importamos el objeto styled del styled-components biblioteca (esto está preinstalado en el modelo que clonamos anteriormente). En este objeto podemos encontrar una serie de funciones que llevan el nombre de los elementos HTML estándar, por ejemplo:styled.div() , styled.p() o styled.section() .

Para nuestro formulario de tarjeta de crédito, vamos a utilizar un <div></div> simple etiqueta por lo que estamos usando el styled.div() función aquí. Aunque no lo parezca, el styled.div`` parte aquí es equivalente a styled.div(``) . La idea es que en JavaScript, si vamos a llamar a una función donde el único argumento es una cadena, podemos omitir los paréntesis y reemplazar nuestras comillas simples o dobles con acentos graves, pasando nuestra cadena como de costumbre.

En este archivo, esta es una elección puramente sintáctica para mantener nuestro código alineado con los ejemplos ofrecidos por styled-components y sus autores.

Centrándonos en el contenido de la cadena que estamos pasando a styled.div() , solo estamos agregando un poco de brillo a nuestro formulario de tarjeta (de forma predeterminada, Stripe nos brinda un formulario muy simplificado sin estilos). Cabe destacar que aquí verá el StripeElement--focus clase que tiene estilos aplicados (usamos un selector CSS anidado con ¶ decir "si el .credit-card elemento también tiene la clase StripeElement--focus , aplique estos estilos").

Esta es una clase generada automáticamente que Stripe aplica automáticamente cuando un usuario enfoca o "hace clic en" nuestro formulario de tarjeta. Usamos esto para cambiar el color del borde de nuestro formulario de tarjeta para reconocer la interacción.

/páginas/index.js

import React, { useEffect, useState } from "react";
import stripe from "../lib/stripe";

import StyledIndex from "./index.css";

class Index extends React.Component {
  state = {
    token: "",
    cardError: "",
  };

  componentDidMount() {
    const elements = stripe.elements();

    this.creditCard = elements.create("card", {
      style: {
        base: {
          fontSize: "18px",
        },
      },
    });

    this.creditCard.on("change", (event) => {
      if (event.error) {
        this.setState({ cardError: event.error.message });
      } else {
        this.setState({ cardError: "" });
      }
    });

    this.creditCard.mount(".credit-card");
  }

  handleSubmit = () => {
    // We'll handle token generation for our card here...
  };

  render() {
    const { cardError, token } = this.state;

    return (
      <StyledIndex>
        <div className="credit-card" />
        {cardError && <p className="card-error">{cardError}</p>}
        <button
          onClick={() => this.handleSubmit()}
          className="btn btn-primary btn-lg mt-4"
        >
          Get Token
        </button>
        {token && (
          <div className="mt-4">
            <p className="token">{token}</p>
          </div>
        )}
      </StyledIndex>
    );
  }
}

Index.propTypes = {
  // prop: PropTypes.string.isRequired,
};

export default Index;

De vuelta en nuestro <Index /> componente en el que representamos el marcado de nuestra tarjeta de crédito, ahora estamos listos para montar nuestra tarjeta de crédito. Por "montar", nos referimos a decirle a Stripe que reemplace el <div className="credit-card" /> etiqueta en nuestra página con el formulario de tarjeta de crédito real de Stripe Elements.

Arriba, podemos ver que estamos importando el /lib/stripe.js archivo que configuramos anteriormente. Abajo en nuestro componentDidMount() método, usamos esto para obtener acceso al .elements() función que crea una instancia de la biblioteca de elementos de Stripe para nosotros.

A continuación, para "montar" nuestra tarjeta de crédito, primero debemos crear el elemento que la representa (piense en esto como la representación en memoria del formulario de la tarjeta antes de que se "dibuje" en la pantalla). Para hacerlo llamamos al elements.create() , pasando el tipo de elemento que queremos crear como una cadena "card" como primer argumento y luego un objeto de opciones como segundo argumento.

Para las opciones, estamos configurando un tamaño de fuente un poco más grande que el predeterminado (debido a cómo Stripe monta nuestro formulario de tarjeta, desafortunadamente, no podemos configurar el tamaño de fuente con el resto del CSS en nuestro componente con estilo).

Finalmente, una vez creado nuestro elemento, lo almacenamos en nuestro <Index></Index> clase de componente como this.creditCard . Esto será útil más adelante cuando necesitemos hacer referencia a this.creditCard para acceder a su valor y generar un token.

Debajo de este código, a continuación, para "atrapar" o manejar los errores generados por los elementos de Stripe, debemos agregar un detector de eventos a this.creditCard . Para hacerlo, Stripe nos da un .on() método en esa instancia. Esto toma el nombre del evento que queremos escuchar—aquí, `"cambio"—y una función de devolución de llamada para llamar cada vez que ocurra ese evento.

Para nuestras necesidades, el único cambio que nos importa es si this.creditCard produce un error. Dentro de nuestro change devolución de llamada, estará disponible como event.error . Si existe, aquí tomamos el event.error.message valor (texto que describe el error que está ocurriendo) y configúrelo en el estado.

Si no hay un error (lo que significa que se corrigió un error anterior o nunca hubo un error), nos aseguramos de restablecer cardError en estado para ser una cadena vacía.

Finalmente, debajo de este change controlador de eventos, finalmente llegamos al punto en el que montamos nuestro formulario de elementos Stripe a través de this.creditCard.mount() . Observe que pasamos el className configuramos en el <div></div> abajo en nuestro render() método para esta función. Esto le dice a Stripe que inyecte o "monte" la forma de los elementos en este lugar.

Justo debajo de esto, también podemos ver que renderizamos condicionalmente nuestro cardError si tiene un valor (recuerde, lo diseñamos antes dentro de nuestro /pages/index.css.js archivo).

Si bien esto técnicamente nos brinda un formulario de tarjeta de crédito en la página, para terminar, vamos a aprender cómo acceder al valor ingresado en nuestro formulario de tarjeta de crédito y convertirlo en un token fuente de Stripe.

Generando un token de Stripe

Para que nuestro formulario sea útil, ahora vamos a aprender cómo generar lo que se conoce como un token de origen de Stripe. Debido a varias leyes sobre la transmisión de datos financieros (p. ej., cumplimiento de PCI), ofrecer un formulario de tarjeta de crédito implica un poco más de complejidad legal que recopilar formas de datos más inocuas, como un nombre o una dirección de correo electrónico.

Debido a que cumplir con este tipo de regulación es una carga importante para las pequeñas empresas y los operadores independientes, empresas como Stripe intervienen para resolver el problema. Actúan como intermediarios entre los datos de la tarjeta de crédito de su cliente y sus servidores. En lugar de copiar los datos de la tarjeta de crédito directamente a su propio servidor y, por lo tanto, tener que cumplir con las leyes de PCI, le transfiere los datos a Stripe, cuyos servidores/código ya cumplen con PCI (y prometen cumplirlo en el futuro).

El mecanismo que utiliza Stripe para administrar este proceso se conoce como token de fuente (aquí, la fuente es una "fuente de pago" como una tarjeta de crédito o una cuenta bancaria). Cuando usamos Stripe.js, establecemos una conexión segura a través de HTTPS con los servidores de Stripe, les enviamos los datos de la tarjeta que nuestro usuario ingresa y luego Stripe responde con un token único que representa esa tarjeta de crédito. Para realmente cargar esa tarjeta, pasamos ese token único junto con nuestras otras solicitudes a Stripe en nuestro propio servidor. Cuando lo hacemos, Stripe "busca" los datos reales de la tarjeta de crédito asociados con ese token en sus propios servidores/base de datos seguros.

/páginas/index.js

import React, { useEffect, useState } from "react";
import stripe from "../lib/stripe";

import StyledIndex from "./index.css";

class Index extends React.Component {
  state = {
    token: "",
    cardError: "",
  };

  componentDidMount() { ... }

  handleSubmit = () => {
    stripe.createToken(this.creditCard).then(({ error, token }) => {
      if (error) {
        this.setState({ cardError: error.message });
      } else {
        this.setState({ token: token.id });
      }
    });
  };

  render() {
    const { cardError, token } = this.state;

    return (
      <StyledIndex>
        <div className="credit-card" />
        {cardError && <p className="card-error">{cardError}</p>}
        <button
          onClick={() => this.handleSubmit()}
          className="btn btn-primary btn-lg mt-4"
        >
          Get Token
        </button>
        {token && (
          <div className="mt-4">
            <p className="token">{token}</p>
          </div>
        )}
      </StyledIndex>
    );
  }
}

Index.propTypes = {
  // prop: PropTypes.string.isRequired,
};

export default Index;

De vuelta en nuestro <Index></Index> y centrándonos en nuestro handleSubmit() método, llamamos al stripe.createToken() método, pasando el this.creditCard valor que establecimos anteriormente. A partir de esto, Stripe sabe cómo recuperar el valor de entrada actual. Detrás de escena, toma este valor, lo transmite a sus propios servidores y luego responde. Esa respuesta se captura aquí en el .then() devolución de llamada (esperamos stripe.createToken() para devolver una promesa de JavaScript) aquí en nuestro código.

A esa devolución de llamada, esperamos que se nos pase un objeto con un token propiedad en él que es en sí mismo un objeto que tiene nuestro token de origen real almacenado en su .id propiedad. Aquí, asumiendo que el error el valor también incluido en este objeto de respuesta es no definido, tomamos que token.id y configúrelo de nuevo en el estado de nuestro componente como this.state.token (this.setState() modifica el this.state valor en nuestro componente).

¡Eso es todo! En este punto, tomaríamos el token.id lo recibimos y lo retransmitimos a nuestros propios servidores para luego pasarlo a Stripe. Para probarlo podemos introducir el número de tarjeta 4242 4242 4242 4242 , pasando cualquier fecha de vencimiento futura y CVC.

Terminando

En este tutorial, aprendimos cómo generar un formulario de tarjeta de crédito usando la biblioteca Stripe Elements incluida dentro de Stripe.js. Aprendimos cómo incluir Stripe.js en nuestro HTML e inicializarlo con nuestra clave publicable que obtuvimos del tablero de Stripe y luego importar esa instancia para generar nuestro formulario. También aprendimos cómo recuperar la entrada de nuestro usuario a través de Stripe.js y luego pasarla al .createToken() de Stripe. método para generar un token de tarjeta seguro para usar en otra parte de nuestra aplicación.