Blog 

Integración de aplicación Ruby On Rails con WebPay Plus

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 PayPalServipagMercado PagoDineroMailKhipu 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 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:

Integración de aplicación Ruby On Rails con WebPay Plus
Descripción de la Transacción de Autorización Normal. Disponible en: http://www.transbankdevelopers.cl/api/webpay/soap/conceptos_generales

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:

Integración de aplicación Ruby On Rails con WebPay Plus
Archivos en la raíz del SDK

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

Integración de aplicación Ruby On Rails con WebPay Plus
Archivos de la carpeta libwebpay

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.
Rails.application.routes.draw do
##
##
## Tus rutas ##
## …
##
#WebPay
post '/webpay/webpay_final_url', :to => 'webpay#webpay_final_url', :as => :webpay_result
post '/webpay/webpay_return_url', :to => 'webpay#webpay_return_url', :as => :webpay_return_url
get 'webpay/success', :to => 'payments#webpay_success', :as => :webpay_success
get 'webpay/error', :to => 'payments#webpay_error', :as => :webpay_error
get 'webpay/nullify', :to => 'payments#webpay_nullify', :as => :webpay_nullify
end
view raw routes.rb hosted with ❤ by GitHub

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:

Rails.application.routes.draw do
##
##
## Tus rutas ##
## …
##
#WebPay
post '/webpay/webpay_final_url', :to => 'webpay#webpay_final_url', :as => :webpay_result
post '/webpay/webpay_return_url', :to => 'webpay#webpay_return_url', :as => :webpay_return_url
get 'webpay/success', :to => 'payments#webpay_success', :as => :webpay_success
get 'webpay/error', :to => 'payments#webpay_error', :as => :webpay_error
get 'webpay/nullify', :to => 'payments#webpay_nullify', :as => :webpay_nullify
end
view raw routes.rb hosted with ❤ by GitHub
development:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
mail_admin: <%= ENV["MAIL_ADMIN"] %>
pass_admin: <%= ENV["PASS_ADMIN"] %>
webpay_wsdl: https://webpay3gint.transbank.cl/WSWebpayTransaction/cxf/WSWebpayService?wsdl
webpay_client_certificate: config/public_cert_develop.crt
webpay_client_private_key: config/private_key_develop.key
webpay_tbk_certificate: config/tbk_develop.key
webpay_commerce_code: '597020000541'
webpay_return_url: /webpay/webpay_return_url
webpay_final_url: /webpay/webpay_final_url
webpay_ambient: 'INTEGRACION'
webpay_logger: 'webpay_development.log'
view raw secrets.yml hosted with ❤ by GitHub

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:

—–BEGIN CERTIFICATE—–
MIIDujCCAqICCQCZ42cY33KRTzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UEBhMC
Q0wxETAPBgNVBAgMCFNhbnRpYWdvMRIwEAYDVQQKDAlUcmFuc2JhbmsxETAPBgNV
BAcMCFNhbnRpYWdvMRUwEwYDVQQDDAw1OTcwMjAwMDA1NDExFzAVBgNVBAsMDkNh
bmFsZXNSZW1vdG9zMSUwIwYJKoZIhvcNAQkBFhZpbnRlZ3JhZG9yZXNAdmFyaW9z
LmNsMB4XDTE2MDYyMjIxMDkyN1oXDTI0MDYyMDIxMDkyN1owgZ4xCzAJBgNVBAYT
AkNMMREwDwYDVQQIDAhTYW50aWFnbzESMBAGA1UECgwJVHJhbnNiYW5rMREwDwYD
VQQHDAhTYW50aWFnbzEVMBMGA1UEAwwMNTk3MDIwMDAwNTQxMRcwFQYDVQQLDA5D
YW5hbGVzUmVtb3RvczElMCMGCSqGSIb3DQEJARYWaW50ZWdyYWRvcmVzQHZhcmlv
cy5jbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANApVXB/EQtbviqQ
j1J82EiHJslyPO0RLAZEM2gNUCXuMPwe4OirylfQ2qn5zYTL3ff8qZhn9EQ0D4ZQ
Wxpc8pis9cYdJUAQ/vTGa4PCbvP3dZNKSG9UC0A54UYEdk9eJ4F6T+DyECrauw7H
RwcmxVOb7wClanR7yJmRc6nsW2Y11scYU/v7BnA+FbOu933Ogfl49lKyhe0MleaT
A6tBwgi0zJmn1gJjax8peopkDPm6gtdoJxBABJKyYkjuj/9tZvYgybOSkGp6SW8t
GhlvJqHGERwzB2Y7U4iD9PiQCYC/SrB/q13ZNm0+2nq3u08ziv2xMIZGBF5zDLf+
ub1egH8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAdgNpIS2NZFx5PoYwJZf8faze
NmKQg73seDGuP8d8w/CZf1Py/gsJFNbh4CEySWZRCzlOKxzmtPTmyPdyhObjMA8E
Adps9DtgiN2ITSF1HUFmhMjI5V7U2L9LyEdpUaieYyPBfxiicdWz2YULVuOYDJHR
n05jlj/EjYa5bLKs/yggYiqMkZdIX8NiLL6ZTERIvBa6azDKs6yDsCsnE1M5tzQI
VVEkZtEfil6E1tz8v3yLZapLt+8jmPq1RCSx3Zh4fUkxBTpUW/9SWUNEXbKK7bB3
zfB3kGE55K5nxHKfQlrqdHLcIo+vdShATwYnmhUkGxUnM9qoCDlB8lYu3rFi9w==
—–END CERTIFICATE—–

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:

—–BEGIN RSA PRIVATE KEY—–
MIIEpQIBAAKCAQEA0ClVcH8RC1u+KpCPUnzYSIcmyXI87REsBkQzaA1QJe4w/B7g
6KvKV9DaqfnNhMvd9/ypmGf0RDQPhlBbGlzymKz1xh0lQBD+9MZrg8Ju8/d1k0pI
b1QLQDnhRgR2T14ngXpP4PIQKtq7DsdHBybFU5vvAKVqdHvImZFzqexbZjXWxxhT
+/sGcD4Vs673fc6B+Xj2UrKF7QyV5pMDq0HCCLTMmafWAmNrHyl6imQM+bqC12gn
EEAEkrJiSO6P/21m9iDJs5KQanpJby0aGW8mocYRHDMHZjtTiIP0+JAJgL9KsH+r
Xdk2bT7aere7TzOK/bEwhkYEXnMMt/65vV6AfwIDAQABAoIBAHnIlOn6DTi99eXl
KVSzIb5dA747jZWMxFruL70ifM+UKSh30FGPoBP8ZtGnCiw1ManSMk6uEuSMKMEF
5iboVi4okqnTh2WSC/ec1m4BpPQqxKjlfrdTTjnHIxrZpXYNucMwkeci93569ZFR
2SY/8pZV1mBkZoG7ocLmq+qwE1EaBEL/sXMvuF/h08nJ71I4zcclpB8kN0yFrBCW
7scqOwTLiob2mmU2bFHOyyjTkGOlEsBQxhtVwVEt/0AFH/ucmMTP0vrKOA0HkhxM
oeR4k2z0qwTzZKXuEZtsau8a/9B3S3YcgoSOhRP/VdY1WL5hWDHeK8q1Nfq2eETX
jnQ4zjECgYEA7z2/biWe9nDyYDZM7SfHy1xF5Q3ocmv14NhTbt8iDlz2LsZ2JcPn
EMV++m88F3PYdFUOp4Zuw+eLJSrBqfuPYrTVNH0v/HdTqTS70R2YZCFb9g0ryaHV
TRwYovu/oQMV4LBSzrwdtCrcfUZDtqMYmmZfEkdjCWCEpEi36nlG0JMCgYEA3r49
o+soFIpDqLMei1tF+Ah/rm8oY5f4Wc82kmSgoPFCWnQEIW36i/GRaoQYsBp4loue
vyPuW+BzoZpVcJDuBmHY3UOLKr4ZldOn2KIj6sCQZ1mNKo5WuZ4YFeL5uyp9Hvio
TCPGeXghG0uIk4emSwolJVSbKSRi6SPsiANff+UCgYEAvNMRmlAbLQtsYb+565xw
NvO3PthBVL4dLL/Q6js21/tLWxPNAHWklDosxGCzHxeSCg9wJ40VM4425rjebdld
DF0Jwgnkq/FKmMxESQKA2tbxjDxNCTGv9tJsJ4dnch/LTrIcSYt0LlV9/WpN24LS
0lpmQzkQ07/YMQosDuZ1m/0CgYEAu9oHlEHTmJcO/qypmu/ML6XDQPKARpY5Hkzy
gj4ZdgJianSjsynUfsepUwK663I3twdjR2JfON8vxd+qJPgltf45bknziYWvgDtz
t/Duh6IFZxQQSQ6oN30MZRD6eo4X3dHp5eTaE0Fr8mAefAWQCoMw1q3m+ai1PlhM
uFzX4r0CgYEArx4TAq+Z4crVCdABBzAZ7GvvAXdxvBo0AhD9IddSWVTCza972wta
5J2rrS/ye9Tfu5j2IbTHaLDz14mwMXr1S4L39UX/NifLc93KHie/yjycCuu4uqNo
MtdweTnQt73lN2cnYedRUhw9UTfPzYu7jdXCUAyAD4IEjFQrswk2x04=
—–END RSA PRIVATE KEY—–

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:

—–BEGIN CERTIFICATE—–
MIIDKTCCAhECBFZl7uIwDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCQ0wxDjAMBgNVBAgMBUNo
aWxlMREwDwYDVQQHDAhTYW50aWFnbzEMMAoGA1UECgwDa2R1MQwwCgYDVQQLDANrZHUxCzAJBgNV
BAMMAjEwMB4XDTE1MTIwNzIwNDEwNloXDTE4MDkwMjIwNDEwNlowWTELMAkGA1UEBhMCQ0wxDjAM
BgNVBAgMBUNoaWxlMREwDwYDVQQHDAhTYW50aWFnbzEMMAoGA1UECgwDa2R1MQwwCgYDVQQLDANr
ZHUxCzAJBgNVBAMMAjEwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAizJUWTDC7nfP
3jmZpWXFdG9oKyBrU0Bdl6fKif9a1GrwevThsU5Dq3wiRfYvomStNjFDYFXOs9pRIxqX2AWDybjA
X/+bdDTVbM+xXllA9stJY8s7hxAvwwO7IEuOmYDpmLKP7J+4KkNH7yxsKZyLL9trG3iSjV6Y6SO5
EEhUsdxoJFAow/h7qizJW0kOaWRcljf7kpqJAL3AadIuqV+hlf+Ts/64aMsfSJJA6xdbdp9ddgVF
oqUl1M8vpmd4glxlSrYmEkbYwdI9uF2d6bAeaneBPJFZr6KQqlbbrVyeJZqmMlEPy0qPco1TIxrd
EHlXgIFJLyyMRAyjX9i4l70xjwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBn3tUPS6e2USgMrPKp
sxU4OTfW64+mfD6QrVeBOh81f6aGHa67sMJn8FE/cG6jrUmX/FP1/Cpbpvkm5UUlFKpgaFfHv+Kg
CpEvgcRIv/OeIi6Jbuu3NrPdGPwzYkzlOQnmgio5RGb6GSs+OQ0mUWZ9J1+YtdZc+xTga0x7nsCT
5xNcUXsZKhyjoKhXtxJm3eyB3ysLNyuL/RHy/EyNEWiUhvt1SIePnW+Y4/cjQWYwNqSqMzTSW9TP
2QR2bX/W2H6ktRcLsgBK9mq7lE36p3q6c9DtZJE+xfA4NGCYWM9hd8pbusnoNO7AFxJZOuuvLZI7
JvD7YLhPvCYKry7N6x3l
—–END CERTIFICATE—–
view raw tbk_develop.key hosted with ❤ by GitHub

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

if Rails.env.production?
@ambient = 'PRODUCCION'
end
view raw webapynormal.rb hosted with ❤ by GitHub

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:

class WebpayInit
#Normal transacition
def self.init_transaction(payment, base_url)
puts 'start init transaction'
self.init_webpay(base_url)
amount = payment.amount.to_i
sessionId = rand(1111111..9999999).to_s
payment.update(tbk_transaction_id: sessionId)
return @webpay.getNormalTransaction.initTransaction(amount, payment.id, sessionId, @urlReturn, @urlFinal)
end
def self.get_transaction_result(token, base_url)
self.init_webpay(base_url)
return @webpay.getNormalTransaction.getTransactionResult(token)
end
def self.acknowledge_transaction(token, base_url)
self.init_webpay(base_url)
return @webpay.getNormalTransaction.acknowledgeTransaction(token)
end
def self.nullify_transaction(token, base_url, authorizationCode, authorizedAmount, buyOrder, nullifyAmount)
self.init_webpay(base_url)
return @webpay.getNullifyTransaction.nullify(authorizationCode, authorizedAmount, buyOrder, nullifyAmount, Rails.application.secrets.webpay_commerce_code.to_s)
end
## OPCIONAL, DEFINIR UN LOGGER, PARA IR AGREGAN LA INFORMACIÓN DE WEBPAY
#Logger
def self.log(action, id, type, payload_text, sessionId)
puts 'Saving webpay logs'
file = Rails.application.secrets.webpay_logger
puts 'archivo: ' + file
directory = Rails.root.join('log', file)
time = Time.now.to_s
File.open(directory, 'a+') do |f|
if type.to_s == "request" && action.to_s == "init_transaction"
source = payload_text
response_document = Nokogiri::HTML(source.to_s)
f << action.to_s + ': ' + time.to_s + "\n"
f << type.upcase.to_s + "\n"
f << '(' + "\n"
f << "\t" + "[wSTransactionType] => \'TR_NORMAL_WS\'" + "\n"
f << "\t [buyOrder] => " + id.to_s.split(':')[1].to_s + "\n"
f << "\t [sessionId] =>" + sessionId.to_s + "\n"
f << "\t [returnURL] => https://frutaconsentido.com" + Rails.application.secrets.webpay_return_url.to_s + "\n"
f << "\t [finalURL] https://frutaconsentido.com" + Rails.application.secrets.webpay_final_url.to_s + "\n"
f << "\t [transactionDetails]" + "\n"
f << "\t (" + "\n"
f << "\t\t [amount] => " + response_document.xpath("//amount").text.to_s + "\n"
f << "\t\t [commerceCode] => " + Rails.application.secrets.webpay_commerce_code.to_s + "\n"
f << "\t\t [buyOrder] =>" + id.to_s.split(':')[1].to_s + "\n"
f << "\t )" + "\n"
elsif type.to_s == "response" && action.to_s == "init_transaction"
source = payload_text
source = JSON.parse(source.gsub("=>",":").to_s)
f << "RESPONSE" + "\n"
f << "(" + "\n"
f << "\t\t [token] =>" + source["token"].to_s + "\n"
f << "\t\t [url] =>" + source["url"].to_s + "\n"
f << ")" + "\n"
elsif type.to_s == "request" && action.to_s == "get_transaction_result"
f << action.to_s + ': ' + time.to_s + "\n"
f << type.upcase.to_s + "\n"
f << '(' + "\n"
f << "\t" + "[tokenInput] =>" + sessionId + "\n"
f << ")" + "\n"
elsif type.to_s == "response" && action.to_s == "get_transaction_result"
response_document= Nokogiri::HTML(sessionId.to_s)
accountingdate = response_document.xpath("//accountingdate").text
buyorder = response_document.at_xpath("//buyorder").text
cardnumber = response_document.xpath("//cardnumber").text
sharesnumber = response_document.xpath("//sharesnumber").text
amount = response_document.xpath("//amount").text
commercecode = response_document.xpath("//commercecode").text
authorizationcode = response_document.xpath("//authorizationcode").text
paymenttypecode = response_document.xpath("//paymenttypecode").text
responsecode = response_document.xpath("//responsecode").text
transactiondate = response_document.xpath("//transactiondate").text
urlredirection = response_document.xpath("//urlredirection").text
sessionid = response_document.xpath("//sessionid").text
vci = response_document.xpath("//vci").text
f << type.upcase.to_s + "\n"
f << '(' + "\n"
f << "\t [accountingDate] =>" + accountingdate + "\n"
f << "\t [buyOrder] => " + buyorder + "\n"
f << "\t [cardDetail] =>" + "\n"
f << "\t (" + "\n"
f << "\t\t [cardNumber] =>" + cardnumber + "\n"
f << "\t )" + "\n"
f << "\t [detailOutput] =>" + "\n"
f << "\t (" + "\n"
f << "\t\t [authorizationCode] => " + authorizationcode + " \n"
f << "\t\t [paymentTypeCode] => " + paymenttypecode + "\n"
f << "\t\t [responseCode] => " + responsecode + " \n"
f << "\t\t [sharesNumber] => " + sharesnumber + "\n"
f << "\t\t [amount] => " + amount + " \n"
f << "\t\t [commerceCode] => " + commercecode + " \n"
f << "\t\t [buyOrder] => " + buyorder + " \n"
f << "\t )" + "\n"
f << "\t [sessionId] =>" + sessionid +"\n"
f << "\t [transactionDate] => " + transactiondate + "\n"
f << "\t [urlRedirection] => " + urlredirection + "\n"
f << "\t [VCI] => " + vci + "\n"
f << ')' + "\n"
elsif type.to_s == "request" && action.to_s == "acknowledge_transaction"
f << action.to_s + ': ' + time.to_s + "\n"
f << type.upcase.to_s + "\n"
f << '(' + "\n"
f << "\t [tokenInput] => " + sessionId + " \n"
f << ')' + "\n"
elsif type.to_s == "response" && action.to_s == "acknowledge_transaction"
f << type.upcase.to_s + "\n"
f << '(' + "\n"
f << ')' + "\n"
else
end
end
end
#Se inicializan las variables con lo que se ha traído desde la librería:
private
def self.init_webpay(base_url)
libwebpay = Libwebpay.new
config = libwebpay.getConfiguration
config.commerce_code = Rails.application.secrets.webpay_commerce_code
config.environment = Rails.application.secrets.environment
config.private_key = OpenSSL::PKey::RSA.new(File.read(Rails.application.secrets.webpay_client_private_key))
config.public_cert = OpenSSL::X509::Certificate.new(File.read(Rails.application.secrets.webpay_client_certificate))
config.webpay_cert = OpenSSL::X509::Certificate.new(File.read(Rails.application.secrets.webpay_tbk_certificate))
puts 'commerce code: ' + Rails.application.secrets.webpay_commerce_code.to_s
puts 'env: ' + Rails.application.secrets.environment.to_s
puts 'private_key: ' + OpenSSL::PKey::RSA.new(File.read(Rails.application.secrets.webpay_client_private_key)).to_s
puts 'public_cert: ' + OpenSSL::X509::Certificate.new(File.read(Rails.application.secrets.webpay_client_certificate)).to_s
puts 'webpay_cert: ' + OpenSSL::X509::Certificate.new(File.read(Rails.application.secrets.webpay_tbk_certificate)).to_s
@webpay = libwebpay.getWebpay(config)
@urlReturn = base_url + Rails.application.secrets.webpay_return_url.to_s
@urlFinal = base_url + Rails.application.secrets.webpay_final_url.to_s
end
end
view raw webpay_init.rb hosted with ❤ by GitHub

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:

class WebpayController < ApplicationController
skip_before_action :verify_authenticity_token
#Normal transaction
def webpay_return_url
result = WebpayInit.get_transaction_result(params[:token_ws], request.base_url.to_s)
puts 'resultado obtenido: ' + result['buyorder'].to_s
puts 'token de transbank: ' + params[:token_ws]
payment = Payment.find(result['buyorder'])
payment.update(tbk_token: params[:token_ws])
payment.update(state: 'pending')
if(result['error_desc'] == 'TRX_OK')
state = result['error_desc'].to_s
accountingdate = result['accountingdate'].to_s
buyorder = result['buyorder'].to_s
sharesnumber = result['sharesnumber'].to_s
cardnumber = result['cardnumber'].to_s
amount = result['amount'].to_s
commercecode = result['commercecode'].to_s
authorizationcode = result['authorizationcode'].to_s
paymenttypecode = result['paymenttypecode'].to_s
responsecode = result['responsecode'].to_s
transactiondate = result['transactiondate'].to_s
urlredirection = result['urlredirection'].to_s
vci = result['vci'].to_s
if responsecode == '0'
webpay_data = [state, accountingdate, cardnumber, amount, authorizationcode, paymenttypecode, responsecode, transactiondate, urlredirection, vci, sharesnumber].to_s
payment.update(state: 'complete')
payment.update(webpay_data: webpay_data)
payment.update(pay_date: Time.now)
payment.update(verified: true)
response = Net::HTTP.post_form(URI(urlredirection.to_s), token_ws: params[:token_ws])
render html: response.body.html_safe
else
payment.update(webpay_data: result['error_desc'])
payment.update(description: "Pago con WebPay de: "+ current_user.client.full_name + " con errores")
redirect_to webpay_error_path
end
return
else
payment.update(description: "Pago con WebPay de: "+ current_user.client.full_name + " con errores")
payment.destroy
redirect_to webpay_nullify_path
end
end
def webpay_final_url
if(params[:TBK_ID_SESION] == nil)
@payment =Payment.where(tbk_token: params[:token_ws]).take
redirect_to webpay_success_path(token_ws: params[:token_ws])
else
redirect_to webpay_nullify_path
end
end
end

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):

Integración de aplicación Ruby On Rails con WebPay Plus
Formulario definido en líneas anteriores

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.

Integración de aplicación Ruby On Rails con WebPay Plus
Página de pago de WebPay en ambiente de INTEGRACIÓN

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.

Publicado el 7 agosto, 2018

Categorías

Etiquetas

Te puede interesar

Certificación con WebPay Plus

Certificación con WebPay Plus

Para el proceso de integración, Certificación y Paso a Producción Transbank dispone de 3 ambientes, que requerirán...

3 Comentarios

  1. Bernardo rodríguez

    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.

    Responder
    • Daniel Vargas

      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:

      Ejemplo integración WebPay

      Quedo atento a los resultados,

      Un abrazo!

      Responder
      • Bernardo Rodriguez

        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!

        Responder

Enviar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

HABLEMOS

¿TE AYUDAMOS A IMPLEMENTAR SOFTWARE EN TU EMPRESA?

Estaríamos encantados de ayudarte

Garage Labs

contacto@garagelabs.cl

+569 8741 1528 | +569 3537 2885

Badajoz 100, Las Condes

Wework. Sánchez Fontecilla 310, Las Condes