- Actualización a Webpay REST en mi aplicación WordPress con WooCommerce - 13 mayo, 2022
- Actualización de Webpay SOAP a REST en mi aplicación con Ruby On Rails - 28 abril, 2022
- Construyendo arquitecturas altamente escalables - 27 julio, 2020
Usando los web services de Transbank
Muchas veces en nuestros desarrollos, nos encontramos con la necesidad de que las aplicaciones sean capaces de recibir pagos transaccionales para nuestros clientes (si es que tenemos clientes dedicados al comercio electrónico o al intercambio de productos y servicios). Como es sabido, existen muchos proveedores que son capaces de entregar este servicio por un pequeño cobro o porcentaje por transacción, tales como PayPal, Servipag, Mercado Pago, DineroMail, Khipu o, el cuál abordaremos en este tutorial, WebPay.
En las siguientes líneas te explico paso a paso como realizar esta integración.
Importante antes de continuar. En este tutorial se asumirá que el lector o lectora, tiene un algún conocimiento de Ruby On Rails, tales como la generación de una nueva aplicación, creación de algunos de los componentes del MVC, migraciones, formularios en las vistas, procesar una solicitud POST en un controlador, entre otros. Si usted no posee estos conocimientos básicos, tendrá algunas dificultades para enfrentar este desafío (aunque no tiene mucho sentido querer procesar pagos, sin al menos conocer el framework de antemano).
Creación de modelo Payment
Para mantener un registro de los pagos que procesa nuestra aplicación, recomiendo crear un modelo que tenga los atributos que mostraré en el cuadro siguiente. De este modo, podemos asegurar que los pagos que se reciban tengan una información mínima, completa y que nos asegure tener un respaldo en caso de problemas.
create_table "payments", force: :cascade do |t|
t.string "description", limit: 255
t.integer "amount", limit: 4
t.datetime "pay_date"
t.boolean "verified"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "client_id", limit: 4
t.integer "user_id", limit: 4
t.integer "payment_method_id", limit: 4
t.string "tbk_transaction_id", limit: 255
t.string "tbk_token", limit: 255
t.string "state", limit: 255
t.string "webpay_data", limit: 255
end
Los atributos description y amount almacenan una descripción (producto o servicio que se está pagando) y el monto del pago, respectivamente. Por su lado pay_date se encarga de almacenar la fecha del pago, que puede ser distinta a la fecha de creación de registro de pago. Los atributo client_id y user_id almacenan referencias a esos modelos, para mantener un registro del responsable del pago, si en tu caso el cliente es a la vez un usuario, puedes unificar estas referencias. Los últimos 4 elementos (tbk_transaction_id, tbk_token, state, webpay_data) deben estar presentes obligatoriamente en tu modelo, ya que son necesarios para almacenar los datos de Webpay en tu base de datos.
Es responsabilidad tuya crear el modelo, migraciones y todo lo necesario para conseguir la estructura anteriormente descrita.
Transacción normal en WebPay
De acuerdo al sitio oficial de Transbank para desarrolladores, el flujo de una transacción normal es el siguiente:


En donde se ven involucradas las siguientes funciones:
initTransaction
Permite inicializar una transacción en WebPay. Como respuesta a la invocación se genera un token que representa en forma única una transacción. Es importante considerar que una vez invocado este método, el token que es entregado tiene un periodo reducido de vida de 5 minutos, posterior a esto el token es caducado y no podrá ser utilizado en un pago.
getTransactionResult
Permite obtener el resultado de la transacción una vez que WebPay ha resuelto su autorización financiera.
acknowledgeTransaction
Indica a WePay que se ha recibido conforme el resultado de la transacción. El método acknowledgeTransaction debe ser invocado siempre, independientemente del resultado entregado por el método getTransactionResult. Si la invocación no se realiza en un período de 30 segundos, WebPay reversará la transacción, asumiendo que el comercio no pudo informar de su resultado, evitando así el cobro al tarjetahabiente (cliente del ecommerce que posee tarjeta de crédito o débito).
NOTA: se puede notar la importancia de este último método, es por esto que más adelante se modificarán algunos archivos del SDK para asegurar que esta última función se ejecutó correctamente.
Obtención de SDK para Ruby
El sitio developers de Transbank, tiene muchos recursos útiles para desarrolladores, entre los que se encuentran:
- Plugins de integración inmediata con ciertas plataformas. Es una opción rápida para integrar el proveedor de pagos, sin embargo, nos deja con la obligación de utilizar la plataforma que facilita la integración.
- SDKs de integración, con códigos muy útiles en varios lenguajes de programación.
- Códigos de ejemplo para integración con web services de WebPay, a través de su WDSL.
Para este tutorial se utilizará el SDK para el lenguaje Ruby, el cual pueden descargarlo desde el siguiente enlace:*tbk. | Developers – SDK RubyApoyando el desarrollo del e-commerce nacionalwww.transbankdevelopers.cl
Existe una gema desarrollada para poder utilizarla, sin embargo para entender más a completitud el flujo de una transacción WebPay y para hacer algunas modificaciones al código, se extraerán las clases relevantes para realizar la integración. De esta manera también podemos reparar ciertas partes del código que pueden ser un puente de riesgo para la seguridad de nuestro software, agregar el registro de logs (opcional) y pasar los certificados de una forma más segura.
Primer paso es extraer los ficheros que se encuentran dentro del SDK de ruby. Los cuales son los siguientes:
Los archivos que nos interesan se encuentran en la carpeta libwebpay, pero no son todos, puedes copiarlos todos si gustas, pero en este caso vamos integrar solo el servicio de WebPay Normal, por lo que el resto puedes no considerarlos. Si tienes intención en conocer los demás servicios (OneClick, Pago Diferido, Mall, entre otros), puedes hacer el curso gratuito que tiene Transbank en su plataforma. En la imagen puedes apreciar marcados todos esos archivos que no usaremos
El resto de archivos debes copiarlos y pegarlos en la carpeta modelos. Para mantener la estructura del proyecto ordenada, se creó una subcarpeta, llamada WebPay.
Considerar que si se desea integrar otro servicio distinto a Normal, el procedimiento es el mismo, solo que se mantiene el archivo correspondiente.
Configuraciones en el proyecto RoR
Instalación de las gemas necesarias
Para empezar, se deben agregar algunas gemas necesarias para la comunicación con los servicios de WebPay, las cuales son:
gem ‘signer’, ‘~> 1.4.2’gem ‘savon’, ‘~> 2.11.1’
Estas gemas cumplirán la labor de cifrar nuestros certificados y la comunicación con los servicios web de Webpay, respectivamente. Debes pegar las líneas en el Gemfile, y ejecutar el comando bundle install.
Creación de las Routes
La transacción WebPay requiere de algunas URLs, para poder devolver respuestas cuando se esté realización la transacción, las cuales son las siguientes:
- Return Url: es la dirección a la que WebPay debe mandar los datos de la transacción, indicando si esta fue aprobada por los datos del banco o si no. Es responsabilidad de la persona a cargo de la integración asegurar el almacenamiento de estos datos, además de redireccionar correctamente.
- Final Url: es la dirección a la que responde WebPay luego de recibir la confirmación por parte del usuario y haber generado el resumen de la orden en pantalla. Responderá con un Token si la transacción se ejecutó correctamente.
- Success: es la página a la que se envía luego de ejecutar toda la transacción sin errores. Se recomienda mostrar un resumen del pago.
- Error: es la página que se muestra si se encuentra un error en la transacción, ya sea por datos del banco u algún otro factor (conexión inestable, retroceder los pasos en la transacción).
- Nullify: es la página que se muestra cuando se presiona el botón “Anular”, en la primera página de WebPay, cuando se ingresan los datos de la tarjeta del usuario.
Creación de las variables del archivo Secrets.yml
Las variables y nombres de credenciales por razones de seguridad, las pasaremos como variables de entorno en producción. En lo que respecta a develop, las trabajaremos de la siguiente manera:
Creación de los certificados y la llave para el ambiente de Integración
WebPay Plus, como servicio seguro, debe tomar ciertas medidas para asegurar esta característica. Para ello implementa un formato de validación a través de certificados y llaves privadas para poder aceptar una transacción desde una página web.
En producción se deben generar desde la parte integradora (en una siguiente publicación se explicará este paso), sin embargo para la etapa en la que nos encontramos, basta con usar los de ejemplo que WebPay nos provee, los cuales son los siguientes:
Certificado público del comercio
Es un certificado que es responsabilidad del comercio. Sirve para la generación de la llave privada que será utilizada para la comunicación con los servicios web, a través de este mismo archivo, que es cargado en los servidores de WebPay. El certificado para CERTIFICACIÓN e INTEGRACIÓN es el siguiente:
Debe crear un archivo llamado public_cert_develop.crt en la carpeta config del proyecto y pegar el texto del recuadro.
Llave privada del comercio
Se utiliza para validar una transacción, a través del certificado público que se generó y que se encuentra cargado en los servidores de WebPay. La llave para CERTIFICACIÓN e INTEGRACIÓN es la siguiente:
Debe crear un archivo llamado private_key_develop.key en la carpeta config del proyecto y pegar el texto del recuadro.
Certificado público de WebPay
Es un certificado que nos envía Transbank al terminar de certificarnos. Para poder realizar la integración tenemos el siguiente certificado de prueba:
Debe crear un archivo llamado tbk_develop.key en la carpeta config del proyecto y pegar el texto del recuadro.
Pueden revisarlos además en la carpeta samples del SDK, siguiendo la ruta app/views/certificates y seleccionar el del producto que quiera integrar, en este caso WebPay Normal.
Modificación de archivos del SDK y de la APP
Como se mencionó, no se va a utilizar la gema que Transbank preparó para la integración, sino más bien, las clases generadas, y se modificarán a nuestra conveniencia, de esta forma aseguramos la impecabilidad de esta integración.
app.rb
En config/application.rb, agregar la siguiente línea:
config.autoload_paths += Dir[Rails.root.join(‘app’, ‘models’, ‘{**}’)]
Esto para que la aplicación sea capaz de leer las clases que están dentro de la carpeta webpay.
webpaynormal.rb
En el archivo models/webpay/webpaynormal.rb (cambiar normal por el producto que quiera integrar), haremos las siguientes modificaciones:
Comentar la línea: #require_relative “verifier”
Ya que ya no necesitamos requerirla de esta forma, porque la estamos trabajando como un modelo.
En función: initialize(configuration), agregar lo siguiente, justo debajo de la línea “@ambient = configuration.environment”
Esto para asegurar el paso a producción, cuando sea el caso.
En get_transaaccion_result modificar las líneas de acknowledge Transaction y poner:
Se agrega este código por que es necesario verificar que se haya hecho el acknowledge Transaction correctamente en los servidores de Transbank antes de procesar el pago en nuestra aplicación.
webpay_init.rb
Luego, es necesario crear una interfaz de comunicación con las clases de WebPay. Este archivo debe ser creado en la raíz de la carpeta modelsy se debe llamar webpay_init.rb, con el siguiente contenido:
IMPORTANTE: Si fue por la opción de agregar logs a su integración, puede agregar la siguiente línea de código, justo debajo de los response_array, de algunas de las funciones del archivo model/webpay/webpaynormal.rb.
WebpayInit.log('init_transaction', 'token: ' + token.to_s , 'response', response_array.to_s, sessionId)
De esta forma, estará guardando logs de uso de WebPay, en caso de que los necesite.
webpay.rb
En model/webpay/webpay.rb quitar relative, del require_relative, de las siguientes librerías:
‘webpaymallnormal’, ‘webpaynormal’, ‘webpaynullify’, ‘webpaycapture’, ‘webpayoneclick’, ‘webpaycomplete’.
webpay_controller.rb
Debe crear el siguiente controlador:
Este último será el encargado de obtener los datos desde WebPay y comunicarlos a los modelos, y de este modo persistirlos en la tabla de la base de datos que almacena la información del pago.
Finalmente, debes definir las funciones de WebPay: success, error y nullify, para presentar la información en la pantalla para cada uno de los eventos. En este tutorial se definieron en PaymentsController.rb.
# Función que presenta un resumen del pago
def webpay_success
@payment = Payment.where(tbk_token: params[:token_ws]).lock(true).take
end#Función que despliega una vista, indicando un error en la transaccióndef webpay_error
end# Función que despliega información en el caso que el usuario haya cancelado la transaccióndef webpay_nullify
end
A continuación presento un ejemplo de vista para webpay_success.html.erb, para que lo tomes como guía. Las otras funcionalidades son solo presentación de información en caso de error, así que las dejo para tu responsabilidad (recuerda que deben ir en app/views/payments/#{webpay_error || webpay_nullify}.html.erb).
Procesar un pago en la aplicación
Para realizar el pago tienes varias formas, ya sea definiendo una nueva función en el controlador para procesar los datos desde el formulario, o reciclando la función create de un pago (Payment). Si toma la primera opción, debe definir una route en el archivo routes.rb.
De todas formas, el formulario que se encargará de enviar el monto de pago al controlador, es el siguiente:
Donde podemos apreciar, sin tomar especial atención en el las etiquetas html y las clases css, un input que se encargará de recibir el monto que se pagará a través de WebPay, con nombre “payment[webpay_amount]”.
Luego en la función create del controlador payments (o el nombre de la función que tu definiste), debemos agregar el siguiente código:
require 'openssl'
require 'base64'def create
if !params[:payment][:webpay_amount].nil? && (webpay_payment = params[:payment][:webpay_amount].to_i) > 0
payment = Payment.prepare_webpay(payment_params, current_user, webpay_payment)
result = WebpayInit.init_transaction(payment, request.base_url.to_s)
if(result['error_desc'] == 'TRX_OK')
token = result['token']
url = result['url']
response = Net::HTTP.post_form(URI(url.to_s), token_ws: token.to_s)
render html: response.body.html_safe
return
else
redirect_to webpay_error_path
return
end
else
redirect_to :back, notice: 'Ha ocurrido un error procesando el pago.'
end
end
La linea: payment = Payment.prepare_webpay(payment_params, current_user, webpay_payment), simplemente inicializa el pago, con datos mínimos (descripción, método de pago, usuario que hace el pago y todos aquellos datos que tu consideres oportunos). El resto de los datos se incorporarán en las funciones de WebPay. Te doy la libertad de definir la función prepare_webpay como tu estimes conveniente.
Si realizaste todo correctamente, ya puedes acceder a la vista que contiene el formulario de pago con webpay (en mi caso /payments/new) y verás el siguiente formulario (o parecido, claro):


Finalmente, da click al botón pagar y tu controlador incializará la transacción con WebPay y desplegará la pagina de pago que el proveedor dispone.


Conclusiones
Con este tutorial , ya estarás listo para iniciar el proceso de validación con WebPay que te permitirá, posteriormente, operar en producción (con tu código de comercio, certificado y llave privada propios).
En una siguiente publicación explicare este proceso, con recomendaciones para hacerlo más agilmente y no tener que esperar semanas para que WebPay apruebe el paso a producción. Cabe destacar que este proceso es cada vez más rápido, ya que Transbank ha mejorado sus procesos.
Si tienes dudas o sugerencias no dudes en contactarme, o si tienes problemas con tu integración siempre puedes recurrir a nosotros en Garage Labs.
Buenos días Daniel, primero que nada, te felicito por el tutorial, está muy claro. Tengo un pequeño problema. La integración me funciona impecable, excepto que al redireccionar a la página del voucher de transbank programáticamente a través del controlador, no descarga el comprobante de pago ni carga las imágenes propias de transbank, solo redirecciona exitosamente a la url final cuando se clickea, no lo hace en 10 segundos tampoco. Me podrías orientar el porqué del problema? Cuando hago la misma acción desde la vista el problema no ocurre, pero es un paso adicional que molesta a los usuarios.
Hola Bernardo!
Muchas gracias por la felicitación!!
Es posible que desde el controlador no se esté enviando de forma correcta la página como un post. Te recomiendo que envíes la URL de re-dirección a una vista y ejecutar el post desde ahí (Puedes hacer que se ejecute de forma automáticamente con jQuery). Con esto agregas un paso extra, pero te aseguras que el post se ejecute de forma satisfactoria.
Acá un ejemplo:
Quedo atento a los resultados,
Un abrazo!
Hola Daniel, muchas gracias por tu respuesta. Coincidéntemente el mismo día que te escribí encontré la misma solución que mencionas. Muchas gracias y disculpa por la demora. Todo funciona perfectamente y pude hacer todas las integraciones de transbank satisfacoriamente. Muchas gracias por el turorial nuevamente. Un abrazo!