En este tutorial, implementaremos una simple aplicación de una tienda donde integraremos pagos online mediante la API de Stripe.
Angular App Tool: Digitalize polygons by Victor Valencia Rico
¿Qué vamos a construir?
Crearemos una simple aplicación de una tienda donde se hará la compra de un libro, posteriormente se realiza el checkout del pago mediante la API de Stripe, al finalizar el pago, si se realiza de manera correcta se tendrá acceso a la descarga del libro en digital.
Usaremos AdonisJS 4.1 en este tutorial y a continuación se muestra la tabla de contenido de cómo se desarrollará la aplicación final:
Tabla de contenido
Requerimientos
Este tutorial asume que tienes lo siguiente instalado en tu computadora:
node >= 8.0 o mayor
$ node --version
npm >= 5.0 o mayor
$ npm --version
Adicional a esto se requerirán las credenciales: (KEY_PUBLISABLE y KEY_SECRET) de una cuenta de Stripe.
Esta cuenta de Stripe se utilizará para realizar las pruebas y además, todo el dinero que se moverá será ficticio. No hay que especificar ni tarjetas de crédito ni nada por el estilo, todo será ficticio, de pruebas. Como si pagaras en tu tienda con billetes del Monopoly.
Angular App: Todo List by Victor Valencia Rico
Instalación de Adonis CLI
Primero necesitamos instalar la Adonis CLI que nos ayudará a crear nuevas aplicaciones de AdonisJS:
$ npm i -g adonisjs-cli
Crear el nuevo proyecto
Comenzaremos creando una nueva aplicación de AdonisJS. Haremos uso de Adonis CLI.
$ adonis new adonisjs-stripe #Creamos la aplicación
El comando anterior creará una nueva aplicación de AdonisJS con el nombre adonisjs-stripe utilizando la plantilla de la aplicación fullstack. Para asegurarnos de que todo funcione como se esperaba, ejecutemos la aplicación recién creada. Primero, ingresamos a la carpeta adonisjs-stripe y ejecutamos el siguiente comando:
$ cd adonisjs-stripe #Ingresar al proyecto
$ adonis serve --dev #Ejecutamos la aplicación
#info: serving app on http://127.0.0.1:3333
Abrimos http://localhost:3333 en el navegador para ver la página de bienvenida.
¡Bien! Ahora comencemos a desarrollar la aplicación.
Angular App Tool: Digitalize polygons by Victor Valencia Rico
Crear el controlador ShopController
Utilizaremos de momento solo un controlador principal llamado ShopController. Usaremos el comando adonis make:controller Shop de Adonis CLI para crealo:
$ adonis make:controller Shop
Cuando se le solicite, elija la opción For HTTP requests y presione Enter. Ahora tenemos un archivo llamado ShopController.js dentro del directorio app/Controllers/Http.
El controlador ShopController tendrá 4 métodos: index(), paySuccess(), payError() y download(), además de un objeto llamado book, el cual describirá los datos del libro principal a comprar en nuestra tienda. Abra el archivo ShopController.js y agregue el siguiente código:
//app/Controllers/Http/ShopController.js
'use strict'
// Definimos el objeto "book" como variable global
const book = {
sku: 'P001',
title: 'Build Apps with Adonis.JS',
image: 'http://www.victorvr.com/img/resources/Book-P001.png',
description: 'Building Node.JS Applications with Javascript.',
author: 'Victor Valencia Rico',
price: 5,
currency: 'USD'
}
class ShopController {
// Desplegará el producto principal
async index ({ view, request }) {
const sessionId = request.input('sessionId')
return view.render('index', {book: book, sessionId: sessionId} )
}
// Desplegará la notificación de un pago exitoso
async paySuccess ({ request, response, session }) {
const sessionId = request.input('sessionId')
session.flash({
sessionId: sessionId,
notification_class: 'alert-success',
notification_icon: 'fa-check',
notification_message: 'Thanks for you purchase! ' + sessionId
})
response.redirect('/?sessionId=' + sessionId);
}
// Desplegará la notificación de un pago fallido o de otros errores
async payError ({ request, response, session }) {
const name = request.input('name')
const message = request.input('message')
session.flash({
notification_class: 'alert-danger',
notification_icon: 'fa-times-circle',
notification_message: 'Payment error! ' + name + ': ' + message
})
response.redirect('/');
}
// Desplegará mensaje de descarga
async download () {
return 'Download File...'
}
}
module.exports = ShopController
El método index() simplemente desplegará el libro principal de la tienda mediante la vista index (la cual crearemos previamente). El método paySuccess() solo recibe el parámetro sessionId y guarda en la variable session los datos de la notificación exitosa a desplegar al redireccionar a la ruta principal. El método payError() recibe 2 parámetros: name y message para asignarlos a la variable session de acuerdo a la notificación fallida a desplegar al redireccionar a la ruta principal. Por último, el método download() simplemente desplegará un mensaje.
Crear las rutas de la aplicación
Abra el archivo start/routes.js y lo actualizamos como a continuación:
//start/routes.js
...
Route.get('/', 'ShopController.index').as('book.index')
Route.get('/pay/success', 'ShopController.paySuccess').as('pay.success')
Route.get('/pay/error', 'ShopController.payError').as('pay.error')
Route.get('/download', 'ShopController.download').as('book.download')
...
Las rutas definidas nos servirán para otorgarle a la aplicación el comportamiento inicial. Es muy importante definirles a cada una de ellas y su alias, ya que este alias nos servirá para hacer referencia a nuestras rutas en la vista principal.
Angular App Tool: Digitalize polygons by Victor Valencia Rico
Crear la vista principal
Vamos a crear una sola vista llamada index para nuestra aplicación. Todos los archivos de las vistas deben estar dentro del directorio resources/views. Entonces, dentro del directorio, crearemos una nueva vista y le asignamos el nombre index.edge. Abra el archivo recién creado y pegue el siguiente código:
Usaremos el framework CSS llamado Bootstrap y la librería de iconos FontAwesome, además de la función global style() de AdonisJS para hacer referencia a nuestros estilos .css en CDN.
Hasta este punto, simplemente hemos realizado la base de la aplicación. Cuando se presione el botón Buy for only $5 USD (Button - Buy), se dará por hecho que se ha realizado el pago y mostrará la nueva vista para descargar el libro comprado, Además, cuando se presione el botón Download PDF (Button - Download), iniciará la descarga del libro comprado en digital. Si visitamos la aplicación en el navegador, deberíamos obtener algo similar a la siguiente imagen:
Instalación de la API de Stripe
Para continuar, instalaremos la API de Stripe el cual nos permitirá procesar los pagos, Entonces, necesitamos instalar el controlador Node.js para la API de Stripe.
$ npm install stripe --save #Instalamos la API de Stripe
Después de instalar la API, debemos ponerlo a disposición de la aplicación y configurar algunas variables de entorno. La configuración de la API de Stripe se refiere a 2 claves principales: KEY_PUBLISHABLE y KEY_SECRET.
var stripe = require('stripe')({YOUR_KEY_SECRET});
Las claves publicables (KEY_PUBLISHABLE) está destinadas únicamente a identificar su cuenta con Stripe, no son secretas. Y las claves secretas (KEY_SECRET) deben mantenerse confidenciales y solo almacenarse en sus propios servidores.
Wheater Dasboard: Angular + OpenWeather by Victor Valencia Rico
Crear el archivo de configuración
Para tener nuestra aplicación estructurada, crearemos un nuevo archivo de configuración llamado stripe.js dentro de la carpeta config, donde concentraremos nuestra variables globales de Stripe. Entonces, abra el archivo stripe.js y agregue el siguiente código:
//config/stripe.js
'use strict'
const Env = use('Env')
module.exports = {
key_publishable: Env.get('STRIPE_KEY_PUBLISHABLE'),
key_secret: Env.get('STRIPE_KEY_SECRET'),
url_success: Env.get('APP_URL') + "/pay/success",
url_error: Env.get('APP_URL') + "/pay/error"
}
Este archivo de configuración leerá e inicializará nuestras variables de entorno: STRIPE_KEY_PUBLISHABLE, STRIPE_KEY_SECRET y APP_URL mediante el archivo .env. Ahi será donde las podremos definir. A continuación, configuramos las variables de entorno ingresando a nuestra configuración en el archivo .env. Entonces, abra el archivo .env y agregue las siguientes líneas:
//.env
...
STRIPE_KEY_PUBLISHABLE={YOUR_KEY_PUBLISHABLE}
STRIPE_KEY_SECRET={YOUR_KEY_SECRET}
...
Recuerde actualizar sus credenciales con las suyas. Realizada esta configuración ahora podremos acceder a ella desde cualquiera de nuestros controladores.
Crear el controlador StripeController
Para el uso esclusivo de la API de Stripe, crearemos un nuevo controlador llamado StripeController. Usaremos el comando adonis make:controller Stripe de Adonis CLI para crealo:
$ adonis make:controller Stripe
Cuando se le solicite, elija la opción For HTTP requests y presione Enter. Ahora tenemos un archivo llamado StripeController.js dentro del directorio app/Controllers/Http.
El controlador StripeController tendrá 5 métodos: getKeyPublishable(), getSuccessURL(), getErrorURL(), createSession() y getSession(), que nos ayudarán a interactuar con la API de Stripe. Abra el archivo StripeController.js y agregue el siguiente código:
//app/Controllers/Http/StripeController.js
'use strict'
const Config = use('Config')
// Configuramos el Stripe con nuestra clave secreta
const Stripe = use('stripe')(Config.get('stripe.key_secret'))
class StripeController {
// Returna la clave publicable
getKeyPublishable () {
return Config.get('stripe.key_publishable')
}
// Returna la URL para procesar un pago exitoso
getSuccessURL () {
return Config.get('stripe.url_success')
}
// Returna la URL para procesar un pago fallido u otros errores
getErrorURL () {
return Config.get('stripe.url_error')
}
// Función "Promise" para crear una sesión de pago en la API de Stripe.
createSession ( payment ) {
return new Promise( ( resolve, reject ) => {
Stripe.checkout.sessions.create( payment, function( err, session ) {
if ( err ) {
reject(err);
}
else {
resolve(session);
}
});
});
}
// Función "Promise" para obtener una sesión de pago en la API de Stripe.
getSession ( sessionId ) {
return new Promise( ( resolve, reject ) => {
Stripe.checkout.sessions.retrieve( sessionId, function(err, session) {
if ( err ) {
reject(err);
}
else {
resolve(session);
}
}
);
});
}
}
module.exports = StripeController
El método getKeyPublishable() retorna la clave publicable de Stripe, los métodos getSuccessURL() y getErrorURL() retornan la URL para procesar un pago exitoso o fallido, según sea el caso. Los otros métodos createSession() y getSession() retornan funciones Promise para crear y obtener la sesión de pago en la API de Stripe.
Las funcines Promise o "Promesas" nos facilitan mucho el control de flujos de datos asíncronos en una aplicación, además las promesas son la base para luego poder implementar características más avanzadas de JavaScript como async/await que nos facilitan aún más nuestro código.
Angular App Tool: Digitalize polygons by Victor Valencia Rico
Las Sesiones de Checkout de la API de Stripe
Los métodos createSession() y getSession() del controlador StripeController.js interactuarán con la API de Stripe a través de Sesiones de Checkout. Una sesión de Checkout representa la sesión de su cliente mientras paga sus compras únicas o suscripciones a través de Checkout. Se recomienda crear una nueva sesión cada vez que su cliente intente pagar. Una vez que el pago se haya realizado correctamente, la sesión de pago contendrá una referencia al cliente y el pago exitoso o una suscripción activa. Puede crear una sesión de pago en su servidor y pasar su sessionId al cliente para comenzar el pago.
Crear el pago
Ahora ligaremos nuestra aplicación para poder realizar el pago del libro dentro de la API de Stripe. Modificamos el controlador ShopController.js en el cual haremos referencia al controlador StripeController.js y crearemos una nueva instancia de este controlador. Tambien se agregará 2 nuevos métodos: tryPay() y payCheckout(). Abra el archivo ShopController.js y agregue el siguiente código:
//app/Controllers/Http/ShopController.js
// Recuerde referenciar el controlador StripeController en la parte de arriba
const StripeController = use('App/Controllers/Http/StripeController')
const Stripe = new StripeController()
...
// Método para realizar el pago en la API de Stripe.
async tryPay({ response }) {
const success_url = Stripe.getSuccessURL()
const error_url = Stripe.getErrorURL()
// Crear el objecto payment
const payment = {
payment_method_types: ['card'],
line_items: [{
name: book.sku + ' - ' + book.title,
description: book.description,
images: [book.image],
amount: book.price + '00',
currency: book.currency,
quantity: 1,
}],
success_url: success_url + '?sessionId={CHECKOUT_SESSION_ID}',
cancel_url: error_url,
}
await Stripe.createSession( payment )
// Indica que la sesión de pago fue creada exitosamente
.then( ( session ) => {
return response.redirect('/pay/checkout' + '?sessionId=' + session.id);
})
// Indica que la sesión de pago fallida
.catch( ( err ) => {
return response.redirect(error_url + '?name=' + err.type + '&message=' + err.raw.message);
});
}
// Método para realizar el checkout del pago en la API de Stripe.
async payCheckout ({ view, request }) {
const sessionId = request.input('sessionId')
return view.render('checkout', {
sessionId: sessionId,
keyPublishable: Stripe.getKeyPublishable()
})
}
...
El método tryPay() nos permitirá realizar el pago en la API de Stripe. Este método definirá el pago dentro de la variable payment que será del tipo Session Object de la API de Stripe. Esta variable será enviada al método createSession() del controlador StripeController.js. Posteriormente este método retornará la sesión creada en el servidor a través de la variable session. Despúes entra en función el método payCheckout(), el cúal usaremos para realizar el chechout en la API de Stripe. Este método recibirá la variable sessionId y nos redireccionará a la vista checkout.edge (Que crearemos a la brevedad)
El objeto payment del tipo Session Object de la API de Stripe, define la forma de pago a Stripe, los detalles de la transacción, la URL a la que redirige al cliente después de que el cliente acepta o cancela el pago de Stripe y además de otra información.
No olvidemos agregar 2 nuevas rutas al archivo start/routes.js donde enlacemos los métodos tryPay() y payCheckout() del controlador ShopController.js y lo actualizamos como a continuación:
//start/routes.js
...
Route.get('/pay/try', 'ShopController.tryPay').as('book.pay')
Route.get('/pay/checkout', 'ShopController.payCheckout').as('pay.checkout')
...
Vamos a crear otra nueva vista llamada checkout para nuestra aplicación. Entonces, dentro del directorio resources/views crearemos una nueva vista y le asignamos el nombre checkout.edge. Abra el archivo recién creado y pegue el siguiente código:
Esta vista solo recibirá la variable sessionId y nos redireccionará a la página de Stripe para realizar el pago correspondiente.
Para terminar, modificamos la vista index donde solo remplazaremos el atributo href del boton para pagar (Button - Buy), reemplazando route('book.index') por la nueva ruta agregada route('book.pay') y eliminamos el parametro ficticio ?sessionId=SESSIONID-XYZ
Hasta este punto, podremos realizar el pago de nuestro libro en la API de Stripe. Cuando se presione el botón Buy for only $5 USD (Button - Buy), nos redireccionará a la página de Stripe, donde tendremos que ingresar un correo electrónico, los datos de la tarjeta de crédito o debito. Una vez ingresados los datos, presionamos el botón Pagar 5,00 US$ para confirmar nuestra compra. Una vez autorizada la compra la página de Stripe nos redireccionará a nuestra tienda, donde desplegará la notificación de la compra exitosa y habilitará la descarga de nuestro libro adquirido, deberíamos obtener algo similar a la siguiente imagen:
Angular App: Todo List by Victor Valencia Rico
Verificar el pago
Para teminar con la aplicación, modificaremos por último el controlador ShopController en el método download(). Abra el archivo ShopController.js y agregue el siguiente código:
//app/Controllers/Http/ShopController.js
// Recuerde referenciar la librería Helpers en la parte de arriba
const Helpers = use('Helpers')
...
// Verificará el pago antes de iniciar la descarga
async download ({ response, request }) {
const sessionId = request.input('sessionId')
await Stripe.getSession( sessionId )
// Indica que la sesión de pago existe
.then( ( session ) => {
const item = session.display_items[0].custom
//Descargar Libro
const name = item.name + '.pdf'
const source = Helpers.resourcesPath('/files/Book-' + item.name.substr(0, 4) + '.pdf')
response.attachment(source, name)
})
// Indica que la sesión de pago no existe
.catch( ( err ) => {
//Mostrar Error
response.send('ERROR: ' + err.type + ' => ' + err.raw.message)
});
}
...
Ya que el método download() recibe el párametro sessionId, verificaremos a través del método getSession() del controlador StripeController.js, si es una sesión de pago existente a través de la API de Stripe. De ser así permitiremos la descarga del libro en digital. De lo contrario solo mostrará un mensaje con el error correspondiente.
Si visitamos la aplicación en el navegador, deberíamos obtener el siguiente resultado con un parámetro sessionId existente y uno ficticio:
Conclusión
Antes de concluir, veamos el resultado final:
Ahora si, hemos terminado con nuestra aplicación, han visto lo fácil que es implementar los pagos online a través del API de Stripe utilizando AdonisJS. Esta aplicación es básica e incluso se pueden agregar mas funcionalidades, tales como guardar los datos de las compras realizadas en una base de datos, imprimir la factura de una compra, etc. Estas actividades se las dejo a su imaginación y criterio para tener una aplicación más completa.